Files
newt/aoc2024/Day14.newt

157 lines
4.3 KiB
Agda
Raw Blame History

module Day14
import Prelude
import Node
import Aoc
import Parser
import SortedMap
data Robot : U where
MkBot : Point Point Robot
pint : Parser Int
pint = do
sign <- pure (0 - 1) <* match '-' <|> pure 1
num <- number
pure $ sign * num
ppoint : Parser Point
ppoint = _,_ <$> pint <* match ',' <*> pint
probot : Parser Robot
probot = do
token "p="
p <- ppoint
ws
token "v="
v <- ppoint
ws
many (match '\n')
pure $ MkBot p v
-- TODO make Mod typeclass or something
infixl 7 _%_
pfunc _%_ : Int Int Int := `(x,y) => x % y`
pmod : Int Int Int
pmod a b =
if a < 0 then (a + b * ((0 - a / b) + 1)) % b else a % b
move : Int Int Int Robot Point
move w h time (MkBot (x,y) (dx,dy)) =
-- no HMul yet
-- let (x',y') = trace "to" $ (x,y) + (time * dx, time * dy) in
let x' = pmod (x + time * dx) w
y' = pmod (y + time * dy) h
in (x', y')
count : a. (a Bool) List a Int
count {a} f xs = go xs 0
where
go : List a Int Int
go Nil acc = acc
go (x :: xs) acc = if f x then go xs (acc + 1) else go xs acc
draw' : Int Int SortedMap Point Int IO Unit
draw' w h pts = go 0
where
mkLine : Int Int SnocList Char String
mkLine y x acc = if x == w then pack (acc <>> Nil)
else case lookupMap (x,y) pts of
Nothing => mkLine y (x + 1) (acc :< '.')
_ => mkLine y (x + 1) (acc :< '*')
go : Int IO Unit
go y = if y == h then pure MkUnit else do
putStrLn $ mkLine y 0 Lin
go (y + 1)
draw : Int Int List Point IO Unit
draw w h pts =
let m = foldMap _+_ EmptyMap $ map (flip _,_ 1) pts in draw' w h m
part2 : Int Int List Robot List Point × Int
part2 w h robots = go 1
where
heuristic : Point Bool
heuristic (x,y) = if x < w / 2 then 100 < 2 * x + y else 2 * (x - 50) < y
-- heuristic (x,y) = x == w / 2
go : Int List Point × Int
go t =
let pts = map (move w h t) robots
cnt = count heuristic pts
in if cnt > 20 then (pts, t)
else go (t + 1)
run : String Int Int IO Unit
run fn w h = do
putStrLn fn
text <- readFile fn
let (Right (robots, Nil)) = some probot $ unpack text
| Left msg => putStrLn $ "Parse Error " ++ msg
| Right (robots, rest) => putStrLn $ "stuck at¬" ++ pack rest
let result = map (move w h 100) robots
-- debugLog result
let q1 = count quad1 result
let q2 = count quad2 result
let q3 = count quad3 result
let q4 = count quad4 result
debugLog (q1,q2,q3,q4)
let p1 = q1 * q2 * q3 * q4
putStrLn $ "part1 " ++ show p1
printLn $ count (\ x => True) robots
let scores = collect robots 0 Nil
let stuff = qsort (\ a b => snd a < snd b) $ collect robots 0 Nil
-- debugLog stuff
dump robots stuff 1
where
dump : List Robot List (Int × Int) Int IO Unit
dump robots Nil _ = pure MkUnit
dump robots (((t,cnt) :: xs)) 0 = pure MkUnit
dump robots (((t,cnt) :: xs)) gas = do
printLn t
draw w h $ map (move w h t) robots
printLn ""
dump robots xs (gas - 1)
quad1 : Point Bool
quad1 (x,y) = x < w / 2 && y < h / 2
quad2 : Point Bool
quad2 (x,y) = x > w / 2 && y < h / 2
quad3 : Point Bool
quad3 (x,y) = x > w / 2 && y > h / 2
quad4 : Point Bool
quad4 (x,y) = x < w / 2 && y > h / 2
heuristic : Point Bool
heuristic (x,y) = if x < w / 2 then 100 - 2 * x < y else 2 * x - 100 < y
dist : Point Int
dist (x,y) = let d = if x < w / 2 then y - (h - 2 * x) else y - (h - 2 * (w - x)) in
if d < 0 then 0 - d else d
collect : List Robot Int List (Int × Int) List (Int × Int)
collect robots iter acc =
if iter > w * h then acc else
let pts = map (move w h iter) robots in
-- This heuristic found it, attempting to find an outline of a large tree
-- but the picture looks nothing like that, so I got lucky
-- let cnt = foldl _+_ 0 $ map dist pts in
-- so I'll go with min danger (although it takes 4 * as long)
let q1 = count quad1 pts
q2 = count quad2 pts
q3 = count quad3 pts
q4 = count quad4 pts
cnt = q1 * q2 * q3 * q4 in
collect robots (iter + 1) ((iter,cnt) :: acc)
main : IO Unit
main = do
-- run "aoc2024/day14/eg.txt" 11 7
run "aoc2024/day14/input.txt" 101 103