-
Notifications
You must be signed in to change notification settings - Fork 0
/
4.hs
120 lines (92 loc) · 3.16 KB
/
4.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import Data.Tuple (fst)
import qualified Data.List.Split as S
import Data.Char (isSpace)
import Data.List (transpose)
type Board = [[Cell]]
type Cell = (Int, Bool)
testInput :: String
testInput = "7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1\n\n22 13 17 11 0\n 8 2 23 4 24\n21 9 14 16 7\n 6 10 3 18 5\n 1 12 20 15 19\n\n 3 15 0 2 22\n 9 18 13 17 5\n19 8 7 25 23\n20 11 10 24 4\n14 21 16 12 6\n\n14 21 17 24 4\n10 16 15 9 19\n18 8 23 26 20\n22 11 13 6 5\n 2 0 12 3 7\n"
parseInput :: String -> ([Int], [Board])
parseInput input =
(parseRandomNumbers randomNumbers, map parseBoard boards)
where
randomNumbers : boards = S.split (dropCondenseOnSublist "\n\n") input
parseRandomNumbers :: String -> [Int]
parseRandomNumbers =
map read . S.splitOn ","
parseBoard :: String -> Board
parseBoard =
map parseBoardRow . S.splitOn "\n" . trim
parseBoardRow :: String -> [Cell]
parseBoardRow =
map parseBoardCell . S.split (dropCondenseOneOf " ") . trim
parseBoardCell :: String -> Cell
parseBoardCell =
(\i -> (i, False)) . read
findWinningBoard :: ([Int], [Board]) -> (Int, Board)
findWinningBoard ((number:rest), boards) =
let
newBoards = map (markNumber number) boards
in
case filter winning newBoards of
[] -> findWinningBoard (rest, newBoards)
[winningBoard] -> (number, winningBoard)
-- severalWinningBoards -> ?
lastWinningBoard :: ([Int], [Board]) -> (Int, Board)
lastWinningBoard ((number:rest), boards) =
let
newBoards = map (markNumber number) boards
in
(case newBoards of
[remainingBoard] ->
if winning remainingBoard then
(number, remainingBoard)
else
lastWinningBoard (rest, newBoards)
severalRemainingBoards ->
lastWinningBoard (rest, filter (not . winning) newBoards)
)
markNumber :: Int -> Board -> Board
markNumber number board =
map
(\row ->
map
(\(n, marked) ->
if n == number then (n, True) else (n, marked))
row
)
board
winning :: Board -> Bool
winning board =
any isComplete board || any isComplete (transpose board)
score :: (Int, Board) -> Int
score (finalNumber, board) =
finalNumber * sumUnmarkedNumbers
where
sumUnmarkedNumbers =
foldl (+) 0 unmarkedNumbers
unmarkedNumbers =
map fst . filter isUnmarked . concat $ board
isComplete :: [Cell] -> Bool
isComplete row =
all isMarked row
isMarked :: Cell -> Bool
isMarked (_, marked) =
marked == True
isUnmarked :: Cell -> Bool
isUnmarked (_, marked) =
marked == False
trim :: String -> String
trim = f . f
where f = reverse . dropWhile isSpace
dropCondenseOneOf :: [Char] -> S.Splitter Char
dropCondenseOneOf delim =
S.dropDelims . S.condense $ S.oneOf delim
dropCondenseOnSublist :: [Char] -> S.Splitter Char
dropCondenseOnSublist delim =
S.dropDelims . S.condense $ S.onSublist delim
main :: IO ()
main = do
raw <- getContents
-- print . score . findWinningBoard . parseInput $ raw
print . score . lastWinningBoard . parseInput $ raw