hackathon/question/q01/q01.go

139 lines
2.6 KiB
Go

package q01
import (
_ "embed"
"fmt"
"strconv"
"strings"
"text/template"
"github.com/hhhapz/codequest/models"
"github.com/hhhapz/codequest/question"
)
func init() {
t := template.New("directions")
var err error
t, err = t.Parse(q01Text)
if err != nil {
panic(err)
}
question.Register(
&question.Question{
ID: "directions",
Name: "No Time for Directions!",
Text: t,
Level: question.Level1,
Generate: func(u *models.User) string {
return strings.Join(generate(u), "\n")
},
Validate: func(u *models.User, part question.Part, solution string) bool {
return Validate(u, part, solution)
},
})
}
func total(a, b int) int {
if a < 0 {
a = -a
}
if b < 0 {
b = -b
}
return a + b
}
const (
// totalSteps in the input
totalSteps = 100
// stepRange is the range of steps within each step
stepRange = 30
// randRage is the range of steps between where the duplicate will be
// present
randRange = 20
// earliestCandidate the earliest step where the duplicate candidate is
earliestCandidate = 10
// earliestDupe is the earliest step where the candidate is visited
earliestDupe = 35
// directions for a step
directions = "NSWE"
)
func generate(u *models.User) []string {
res := make([]string, 0, totalSteps)
r := question.UserRandom(u)
// known is used to disallow duplicate steps before the chosen dupe
known := make(map[int]map[int]bool)
var x, y int
for len(res) != cap(res) {
dir := directions[r.Intn(4)]
steps := r.Intn(stepRange) + 1
newX, newY := move(x, y, dir, steps)
if known[newX] == nil {
known[newX] = make(map[int]bool)
}
if known[newX][newY] {
continue
}
known[newX][newY] = true
x, y = newX, newY
res = append(res, fmt.Sprintf("%c%d", dir, steps))
}
// the location of the candidate for the duplicate
n := r.Intn(randRange) + earliestCandidate
locX, locY := solveP1(res[:n])
// the location where the candidate is revisited
n = r.Intn(randRange) + earliestDupe
lastX, lastY := solveP2(res[:n])
// jump to the candidate
if locX > lastX {
res[n] = fmt.Sprintf("E%d", locX-lastX)
n++
} else if locX < lastX {
res[n] = fmt.Sprintf("W%d", lastX-locX)
n++
}
if locY > lastY {
res[n] = fmt.Sprintf("N%d", locY-lastY)
} else if locY < lastY {
res[n] = fmt.Sprintf("S%d", lastY-locY)
}
return res
}
func Validate(u *models.User, p question.Part, sol string) bool {
inp := generate(u)
var t int
switch p {
case question.Part1:
t = total(solveP1(inp))
case question.Part2:
t = total(solveP2(inp))
default:
return false
}
return strconv.Itoa(t) == sol
}
//go:embed q01.md
var q01Text string