Files
newt/aoc2024/Day20.newt
2024-12-20 17:06:55 -08:00

114 lines
3.6 KiB
Agda
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module Day20
import Prelude
import Node
import Aoc
import SortedMap
gridPoints : String List (Char × Int × Int)
gridPoints text = go 0 0 (unpack text) Nil
where
-- might as well be tail recursive
go : Int Int List Char List (Char × Int × Int) List (Char × Int × Int)
go row col Nil points = points
go row col ('\n' :: cs) points = go (row + 1) 0 cs points
go row col (c :: cs) points = go row (col + 1) cs ((c,row,col) :: points)
State Grid Next Cheat : U
Grid = SortedMap Point Char
State = SortedMap Point Int
Next = (Point × Int)
Cheat = (Point × Point)
data Dir : U where North East South West : Dir
dirs : List Dir
dirs = (North :: South :: East :: West :: Nil)
move : Point Dir Point
move (r, c) North = (r - 1, c)
move (r, c) East = (r, c + 1)
move (r, c) South = (r + 1, c)
move (r, c) West = (r, c - 1)
distances : SortedMap Point Char Point State
distances grid start = go EmptyMap ((start,0) :: Nil) Lin
where
valid : State Point Bool
valid st pt = case lookupMap pt grid of
Just (_, '.') => case lookupMap pt st of
Just _ => False
Nothing => True
_ => False
go : State List (Point × Int) SnocList (Point × Int) State
go st Nil Lin = st
go st xs Lin = go st Nil (Lin <>< xs)
go st xs (ys :< (pt, l)) =
let next = map (flip _,_ $ l + 1) $ filter (valid st) $ map (move pt) dirs
st = foldMap const st next
in go st (next ++ xs) ys
manh : Point Point Int
manh (r,c) (r',c') =
let dr = if r < r' then r' - r else r - r'
dc = if c < c' then c' - c else c - c'
in dr + dc
candidates : Point Int List Point
candidates pt@(sr,sc) dist = go (sr - dist) (sc - dist) Nil
where
go : Int Int List Point List Point
go r' c' acc =
if sr + dist < r' then acc
else if sc + dist < c' then go (r' + 1) (sc - dist) acc
else if manh pt (r',c') <= dist
then go r' (c' + 1) ((r', c') :: acc)
else go r' (c' + 1) acc
calcCheats : State State Int Int Int
calcCheats start dists fuel threshold = go (toList start) 0
where
-- only '.' have dist
tryCand : Int Point Point Int
tryCand l pt cand = case lookupMap cand dists of
Just (_, dist) =>
let d = manh pt cand in
if fuel < d then (let x = (trace "X" (pt, cand, d)) in ?) else
if l + manh pt cand + dist <= threshold then 1 else 0
Nothing => 0
go : List (Point × Int) Int Int
go Nil acc = acc
go (x@(pt,l) :: xs) acc =
let acc = foldl _+_ acc $ map (tryCand l pt) $ candidates pt fuel
in go xs acc
run : String Int IO Unit
run fn time = do
putStrLn fn
text <- readFile fn
let pts = map swap $ gridPoints text
let grid = foldMap const EmptyMap pts
let (Just (start,_)) = find (_==_ 'S' snd) pts | _ => putStrLn "no start"
let (Just (end,_)) = find (_==_ 'E' snd) pts | _ => putStrLn "no end"
let grid' = updateMap start '.' $ updateMap end '.' grid
let dists = distances grid' end
-- this bit threw me off for a while
let dists = updateMap end 0 dists
let dists' = distances grid' start
let dists' = updateMap start 0 dists'
let (Just (_,base)) = lookupMap start dists | _ => putStrLn "no start in dists"
let p1 = calcCheats dists dists' 2 (base - time)
putStrLn $ "base " ++ show base
putStrLn $ "part1 " ++ show p1
let p2 = calcCheats dists dists' 20 (base - time)
putStrLn $ "part2 " ++ show p2
pure MkUnit
main : IO Unit
main = do
run "aoc2024/day20/eg.txt" 50
run "aoc2024/day20/input.txt" 100