Skip to content

Commit e70389d

Browse files
committed
comments and refactor
1 parent 419bc55 commit e70389d

File tree

1 file changed

+52
-53
lines changed

1 file changed

+52
-53
lines changed

solutions/src/2024/21.hs

+52-53
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Maintainer : [email protected]
2323
module Main (main) where
2424

2525
import Advent (getInputLines)
26-
import Advent.Coord (above, below, left, origin, right, Coord(..))
26+
import Advent.Coord (Coord(..), coordLines)
2727
import Advent.Memo (memo2)
2828
import Data.Map (Map)
2929
import Data.Map qualified as Map
@@ -36,83 +36,82 @@ import Data.Set qualified as Set
3636
main :: IO ()
3737
main =
3838
do codes <- getInputLines 2024 21
39-
let score n x = (read (init x) * doorInputs n x)
39+
let score n x = (read (init x) * shortestDoorCodeLength n x)
4040
print (sum (map (score 2) codes))
4141
print (sum (map (score 25) codes))
4242

4343
data Pad = Pad (Set Coord) (Map Char Coord)
4444

45-
padFromList :: [(Coord, Char)] -> Pad
46-
padFromList xs = Pad (Set.fromList [p | (p, _) <- xs]) (Map.fromList [(c,p) | (p,c) <- xs])
45+
-- | Turn a list of lines into a 'Pad'. Spaces are removed.
46+
padFromList :: [String] -> Pad
47+
padFromList strs = Pad (Set.fromList (Map.elems buttons)) buttons
48+
where
49+
buttons = Map.fromList [(c, p) | (p, c) <- coordLines strs, c /= ' ']
4750

51+
-- | Find the coordinate of a button.
4852
padCoord :: Pad -> Char -> Coord
4953
padCoord (Pad _ m) c = m Map.! c
5054

55+
-- | Test if a coordinate is contained within the pad.
5156
inPad :: Pad -> Coord -> Bool
5257
inPad (Pad s _) x = Set.member x s
5358

54-
-- | The 4-direction pad centered on the @A@ button.
59+
-- | The 4-direction pad used to control a robot
5560
robotPad :: Pad
56-
robotPad = padFromList [(C 0 (-1), '^'), (C 0 0, 'A'), (C 1 (-2), '<'), (C 1 (-1), 'v'), (C 1 0, '>')]
61+
robotPad = padFromList [" ^A", "<v>"]
5762

58-
-- | The 10-digit pad centered on the @A@ button.
63+
-- | The 10-digit pad used to control the door
5964
doorPad :: Pad
60-
doorPad = padFromList
61-
[ (C (-3) (-2), '7')
62-
, (C (-3) (-1), '8')
63-
, (C (-3) 0 , '9')
64-
, (C (-2) (-2), '4')
65-
, (C (-2) (-1), '5')
66-
, (C (-2) 0 , '6')
67-
, (C (-1) (-2), '1')
68-
, (C (-1) (-1), '2')
69-
, (C (-1) (0) , '3')
70-
, (C 0 (-1) , '0')
71-
, (C 0 0 , 'A')
72-
]
73-
74-
doorInputs :: Int -> String -> Int
75-
doorInputs n str =
65+
doorPad = padFromList ["789","456","123"," 0A"]
66+
67+
-- | The length of the shortest input sequence that enters the given
68+
-- door code via a given number of robot layers.
69+
shortestDoorCodeLength ::
70+
Int {- ^ robot layers -} ->
71+
String {- ^ door code -} ->
72+
Int {- ^ shortest button press count -}
73+
shortestDoorCodeLength n str =
7674
minimum
77-
[ sum (map (robotLength n) keys)
78-
| let deltas = padDeltas doorPad str
79-
, keys <- traverse deltaToKeys deltas
80-
, validate doorPad (concat keys)
75+
[ sum (map (shortestRobotCodeLength n) keys)
76+
| keys <- sequence (route doorPad str)
8177
]
8278

83-
robotLength :: Int -> String -> Int
84-
robotLength = memo2 \n str ->
79+
-- | The length of the shortest input sequence that enters the given
80+
-- robot directional code via a given number of robot layers.
81+
shortestRobotCodeLength ::
82+
Int {- ^ robot layers -} ->
83+
String {- ^ door code -} ->
84+
Int {- ^ shortest button press count -}
85+
shortestRobotCodeLength = memo2 \n str ->
8586
if n == 0 then length str else
8687
minimum
87-
[ sum (map (robotLength (n-1)) keys)
88-
| let deltas = padDeltas robotPad str
89-
, keys <- traverse deltaToKeys deltas
90-
, validate robotPad (concat keys)
88+
[ sum (map (shortestRobotCodeLength (n-1)) keys)
89+
| keys <- sequence (route robotPad str)
9190
]
9291

93-
validate :: Pad -> [Char] -> Bool
94-
validate pad str = all (inPad pad) posns
95-
where
96-
posns = scanl move origin str
97-
move here 'A' = here
98-
move here '>' = right here
99-
move here '<' = left here
100-
move here '^' = above here
101-
move here 'v' = below here
102-
move _ _ = undefined
103-
104-
padDeltas :: Pad -> String -> [Coord]
105-
padDeltas pad str = zipWith (-) (absolutes) (origin:absolutes)
92+
-- | Find a list of steps needed to input a code on a pad. The inner
93+
-- lists allow for there to be multiple, valid subsequences that exist
94+
-- for some keys. Only the most direct routes are considered. This
95+
-- takes advantage of our input pads only missing corners. Sequences
96+
-- always start from @A@.
97+
--
98+
-- >>> route doorPad "029A"
99+
-- [["<A"],["^A"],["^^>A",">^^A"],["vvvA"]]
100+
route :: Pad -> String -> [[String]]
101+
route pad str = zipWith (walk pad) (padCoord pad 'A' : absolutes) absolutes
106102
where
107103
absolutes = map (padCoord pad) str
108104

109-
deltaToKeys :: Coord -> [String]
110-
deltaToKeys (C y x) =
105+
-- | Find the unique, shortest paths to move from one location to
106+
-- another on a pad.
107+
walk :: Pad -> Coord -> Coord -> [String]
108+
walk pad (C y1 x1) (C y2 x2) =
111109
[ keys ++ "A"
112110
| let rawKeys =
113-
replicate (-y) '^' ++
114-
replicate x '>' ++
115-
replicate y 'v' ++
116-
replicate (-x) '<'
117-
, keys <- rawKeys : [reverse rawKeys | y /= 0, x /= 0]
111+
replicate (y1 - y2) '^' ++
112+
replicate (y2 - y1) 'v' ++
113+
replicate (x2 - x1) '>' ++
114+
replicate (x1 - x2) '<'
115+
, keys <- [ rawKeys | inPad pad (C y2 x1) ]
116+
++ [reverse rawKeys | y1 /= y2, x1 /= x2, inPad pad (C y1 x2)]
118117
]

0 commit comments

Comments
 (0)