151 lines
2.6 KiB
Go
151 lines
2.6 KiB
Go
package question
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"math"
|
|
"math/rand"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/hhhapz/codequest/models"
|
|
)
|
|
|
|
func move(x, y int, dir byte, step int) (int, int) {
|
|
switch dir {
|
|
case 'N':
|
|
return x, y + step
|
|
case 'S':
|
|
return x, y - step
|
|
case 'E':
|
|
return x + step, y
|
|
case 'W':
|
|
return x - step, y
|
|
}
|
|
return x, y
|
|
}
|
|
|
|
func q1P1(steps []string) (x int, y int) {
|
|
for _, step := range steps {
|
|
dir := step[0]
|
|
amt, _ := strconv.Atoi(step[1:])
|
|
x, y = move(x, y, dir, amt)
|
|
}
|
|
return
|
|
}
|
|
|
|
func q1P2(steps []string) (x int, y int) {
|
|
known := make(map[int]map[int]bool)
|
|
|
|
for _, step := range steps {
|
|
dir := step[0]
|
|
amt, _ := strconv.Atoi(step[1:])
|
|
x, y = move(x, y, dir, amt)
|
|
|
|
if known[x] == nil {
|
|
known[x] = make(map[int]bool)
|
|
}
|
|
if known[x][y] {
|
|
return
|
|
}
|
|
known[x][y] = true
|
|
}
|
|
return
|
|
}
|
|
|
|
func q1Total(a, b int) int {
|
|
if a < 0 {
|
|
a = -a
|
|
}
|
|
if b < 0 {
|
|
b = -b
|
|
}
|
|
return a + b
|
|
}
|
|
|
|
const (
|
|
q1Entries = 100
|
|
q1Range = 20
|
|
directions = "NSWE"
|
|
)
|
|
|
|
func init() {
|
|
var q *Question
|
|
q = &Question{
|
|
ID: "directions",
|
|
Text: q01Text,
|
|
Level: Level1,
|
|
Generate: func(u *models.User) string {
|
|
res := make([]string, 0, 100)
|
|
|
|
r := userRandom(u)
|
|
|
|
known := make(map[int]map[int]bool)
|
|
var x, y int
|
|
for i := 0; i < q1Entries; i++ {
|
|
dir := directions[r.Intn(4)]
|
|
steps := r.Intn(30) + 1
|
|
newX, newY := move(x, y, dir, steps)
|
|
|
|
if known[newX] == nil {
|
|
known[newX] = make(map[int]bool)
|
|
}
|
|
if known[newX][newY] {
|
|
i--
|
|
continue
|
|
}
|
|
|
|
known[newX][newY] = true
|
|
x, y = newX, newY
|
|
|
|
res = append(res, fmt.Sprintf("%c%d", dir, steps))
|
|
}
|
|
|
|
n := rand.Intn(q1Range) + 10
|
|
locX, locY := q1P1(res[:n])
|
|
|
|
n = rand.Intn(q1Range) + 35
|
|
lastX, lastY := q1P1(res[:n])
|
|
|
|
dX := math.Max(float64(locX), float64(lastX)) - math.Min(float64(locX), float64(lastX))
|
|
dY := math.Max(float64(locY), float64(lastY)) - math.Min(float64(locY), float64(lastY))
|
|
|
|
if locX > lastX {
|
|
res[n] = fmt.Sprintf("E%d", int(dX))
|
|
n++
|
|
} else if locX < lastX {
|
|
res[n] = fmt.Sprintf("W%d", int(dX))
|
|
n++
|
|
}
|
|
|
|
if locY > lastY {
|
|
res[n] = fmt.Sprintf("N%d", int(dY))
|
|
} else if locY < lastY {
|
|
res[n] = fmt.Sprintf("S%d", int(dY))
|
|
}
|
|
|
|
return strings.Join(res, "\n")
|
|
},
|
|
|
|
Validate: func(u *models.User, level Part, input string) bool {
|
|
raw := q.Generate(u)
|
|
inp := strings.Split(raw, "\n")
|
|
|
|
switch level {
|
|
case Part1:
|
|
x, y := q1P1(inp)
|
|
return q1Total(x, y) == 0
|
|
case Part2:
|
|
x, y := q1P2(inp)
|
|
return q1Total(x, y) == 0
|
|
}
|
|
return false
|
|
},
|
|
}
|
|
|
|
Register(q)
|
|
}
|
|
|
|
//go:embed prompts/q01.md
|
|
var q01Text string
|