-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathDay04.hs
102 lines (85 loc) · 2.42 KB
/
Day04.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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
import Data.Bifunctor (second)
import Data.Char as C
import Data.Function
import qualified Data.List as L
import qualified Data.Map.Strict as M
import Data.Maybe
import Data.Ord
import System.Environment
type Row = Int
type Col = Int
data Board = Board
{ marked :: M.Map (Row, Col) (Int, Bool)
, revIdx :: M.Map Int (Row, Col)
}
deriving (Show)
parseBoard board =
let (m, idx) =
unzip
[ (((i, j), (value, False)), (value, (i, j)))
| (row, i) <- zip board [0 ..]
, (value, j) <- zip row [0 ..]
]
in Board (M.fromList m) (M.fromList idx)
nextNumber :: Int -> Board -> Board
nextNumber n (Board m idx) = Board (fromMaybe m rep) idx
where
rep = (\idx -> M.adjust (second (const True)) idx m) <$> M.lookup n idx
scoreBoard (Board m _) =
sum . fmap fst . filter (not . snd) $ M.elems m
getAxis (Board m _) axis =
fmap (fmap snd)
. L.groupBy ((==) `on` axis . fst)
. L.sortOn (axis . fst)
$ M.toAscList m
isWinning :: Board -> Bool
isWinning board = or (fmap (and . fmap snd) . getAxis board =<< [fst, snd])
firstWinner :: [Board] -> [Int] -> (Board, Int)
firstWinner boards (n : ns) =
let next = fmap (nextNumber n) boards
winner = L.find isWinning next
in maybe (firstWinner next ns) (,n) winner
solve1 :: [Board] -> [Int] -> Int
solve1 boards movements = scoreBoard w * n
where
(w, n) = firstWinner boards movements
{- Part 2 -}
lastWinner :: [Board] -> [Int] -> (Board, Int)
lastWinner boards (m : ms) =
let next = fmap (nextNumber m) boards
allCompleted = all isWinning next
in if all isWinning next
then
(,m) . snd . head
. filter
( \(bold, bnew) ->
not (isWinning bold) && isWinning bnew
)
$ zip boards next
else lastWinner next ms
solve2 :: [Board] -> [Int] -> Int
solve2 boards movements = scoreBoard w * n
where
(w, n) = lastWinner boards movements
solutions =
[ solve1
, solve2
]
numbers =
map (read @Int)
. filter (C.isNumber . head)
. L.groupBy ((==) `on` C.isNumber)
main :: IO ()
main = do
idx <- (read @Int) . head <$> getArgs
ms <- numbers <$> getLine
boards <-
fmap parseBoard
. filter (not . null . head)
. L.groupBy ((==) `on` L.null)
. fmap numbers
. lines
<$> getContents
print $ (solutions !! (idx - 1)) boards ms