-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday18.hs
63 lines (54 loc) · 1.76 KB
/
day18.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import Control.Monad
import Data.List
import Data.Map (Map, (!?))
import qualified Data.Map as Map
data Acre = Open | Tree | Lumber deriving (Eq)
type Location = (Int, Int)
type State = Map Location Acre
parse' :: [Char] -> [Acre]
parse' (l:xs) =
let as = parse' xs
in case l of
'.' -> Open:as
'|' -> Tree:as
'#' -> Lumber:as
parse' [] = []
parse :: String -> State
parse input =
let as = map p . zip [1..] . map (zip [1..] . parse') . lines $ input
in Map.fromList $ concat as
where p (y, as) = map (\(x, a) -> ((y, x), a)) as
adjacent :: Location -> [Location]
adjacent (y, x) = do
y' <- [y-1, y, y+1]
x' <- [x-1, x, x+1]
guard $ y' /= y || x' /= x
return (y', x')
next :: State -> State
next state = Map.mapWithKey magic state
where magic (y, x) acre =
let trees = filter (== Just Tree) $ map (state !?) $ adjacent (y, x)
lumber = filter (== Just Lumber) $ map (state !?) $ adjacent (y, x)
in case acre of
Open ->
if length trees >= 3 then Tree else Open
Tree ->
if length lumber >= 3 then Lumber else Tree
Lumber ->
if length trees >= 1 && length lumber >= 1 then Lumber else Open
value :: State -> Int
value state =
let wood = Map.filter (== Tree) state
lumber = Map.filter (== Lumber) state
in Map.size wood * Map.size lumber
valueN :: [State] -> Int -> State -> Int
valueN _ 0 state = value state
valueN seen n state =
let state' = next state
in case elemIndex state' seen of
Just i -> value $ seen !! (i + 1 - n `mod` (i + 1))
Nothing -> valueN (take 30 $ state':seen) (n-1) state'
part1 :: String -> Int
part1 input = valueN [] 10 $ parse input
part2 :: String -> Int
part2 input = valueN [] 1000000000 $ parse input