In [1]:
inputLines = lines <$> readFile "input/day2.txt"
In [2]:
move :: Int -> Char -> Int
move n 'U'
| n > 3 = n - 3
move n 'D'
| n < 7 = n + 3
move n 'L'
| n `mod` 3 /= 1 = n - 1
move n 'R'
| n `mod` 3 /= 0 = n + 1
move n _ = n -- instead of moving out of the keypad, we just keep the same number
In [3]:
-- Part 1: execute the instructions and collect the resulting numbers (after dropping the initial '5' with tail)
concatMap show . tail . scanl (foldl move) 5 <$> inputLines
In [4]:
keypad1 = [ "123"
, "456"
, "789" ]
In [5]:
keypad2 = [ " 1 "
, " 234 "
, "56789"
, " ABC "
, " D " ]
For example, for both keypads we would map ('2', 'R') to '3'.
To create the map, a function that creates tuples of neighbouring elements in a list will be useful. This is easy to do thanks to Control.Applicative (see http://stackoverflow.com/questions/2546524/how-do-you-write-the-function-pairs-in-haskell and http://stackoverflow.com/questions/11810889/functions-as-applicative-functors-haskell-lyah):
In [6]:
pairs = zip <*> tail
pairs "56789" -- check that it works for the middle row of keypad2
In [7]:
pairs " ABC "
In [8]:
removeSpaces = filter (\ (a, b) -> a /= ' ' && b /= ' ')
removeSpaces $ pairs " ABC "
In [9]:
createForwardBackwardMoves :: Char -> Char -> [(Char, Char)] -> [((Char, Char), Char)]
createForwardBackwardMoves fw bw = concatMap (\(a, b) -> [((a, fw), b), ((b, bw), a)]) . removeSpaces
-- Create all right/left moves for a row
createRightLeftMoves :: String -> [((Char, Char), Char)]
createRightLeftMoves = createForwardBackwardMoves 'R' 'L' . pairs
-- Create all down/up moves for two neighbouring rows
createDownUpMoves :: (String, String) -> [((Char, Char), Char)]
createDownUpMoves = createForwardBackwardMoves 'D' 'U' . uncurry zip
createRightLeftMoves " 234 " -- second row of second keypad
createDownUpMoves (" 1 ", " 234 ") -- first and second row of second keypad
In [10]:
import qualified Data.Map as Map
In [11]:
createMovesMap :: [String] -> Map.Map (Char, Char) Char
createMovesMap rows =
Map.fromList $
concatMap createRightLeftMoves rows ++
concatMap createDownUpMoves (pairs rows)
In [12]:
-- Takes a map of all possible moves, the current key, and the direction, and finds the next key
move' :: Map.Map (Char, Char) Char -> Char -> Char -> Char
move' movesMap currentKey direction = Map.findWithDefault currentKey (currentKey, direction) movesMap
In [13]:
-- Takes a keypad and calculates the code (the initial key '5' is dropped with 'tail')
solution keypad = tail . scanl (foldl (move' movesMap)) '5' <$> inputLines
where
movesMap = createMovesMap keypad
In [14]:
solution keypad1
In [15]:
solution keypad2