-
Notifications
You must be signed in to change notification settings - Fork 0
/
AOC9.hs
155 lines (126 loc) · 3.98 KB
/
AOC9.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
module AOC9 where
import Control.Monad.ST
import Data.STRef
data STCircleNode s a = STCircleNode
{ val :: a
, prevNode :: STRef s (STCircleNode s a)
, nextNode :: STRef s (STCircleNode s a)
}
instance Show a => Show (STCircleNode s a) where
show c = show (val c)
singleCircle :: a -> ST s (STCircleNode s a)
singleCircle v = do
p <- newSTRef undefined
n <- newSTRef undefined
let self = STCircleNode v p n
writeSTRef n self
writeSTRef p self
return self
appendNode :: a -> STCircleNode s a -> ST s (STCircleNode s a)
appendNode a thisNode = do
newNode <- singleCircle a
oldNextNode <- readSTRef (nextNode thisNode)
writeSTRef (nextNode thisNode) newNode
writeSTRef (prevNode oldNextNode) newNode
writeSTRef (prevNode newNode) thisNode
writeSTRef (nextNode newNode) oldNextNode
pure newNode
removeNode :: STCircleNode s a -> ST s (a, STCircleNode s a)
removeNode node = do
p <- readSTRef (prevNode node)
n <- readSTRef (nextNode node)
writeSTRef (nextNode p) n
writeSTRef (prevNode n) p
pure (val node, n)
traverseLeft :: Int -> STCircleNode s a -> ST s (STCircleNode s a)
traverseLeft 0 s = pure s
traverseLeft n s = do
p <- readSTRef (prevNode s)
traverseLeft (pred n) p
data Game = Game
{ playerCount :: Int
, finalMarble :: Marble
}
data GameState s = GameState
{ currentMarble :: STCircleNode s Marble
, remainingMarbles :: ![Marble]
, currentPlayer :: !Int
, scores :: ![Score]
}
type Marble = Int
type Score = Int
initialState :: Game -> ST s (GameState s)
initialState g = do
let firstMarble = head $ marblesInGame g
rest = drop 1 $ marblesInGame g
scores = replicate (playerCount g) 0
initialCircle <- singleCircle firstMarble
pure $ GameState initialCircle rest 0 scores
replaceIndex :: Int -> (Int -> Int) -> [Int] -> [Int]
replaceIndex n f l =
let before = take n l
it = head $ drop n l
after = drop (succ n) l
in before ++ [f it] ++ after
isMagic :: Marble -> Bool
isMagic m = 0 == fromEnum m `mod` 23
isFinished :: GameState s -> ST s Bool
isFinished (GameState _ [] _ _) = pure True
isFinished _ = pure False
gameRound :: GameState s -> ST s (GameState s)
gameRound g
| isMagic (head $ remainingMarbles g) = do
let magicMarble = head $ remainingMarbles g
priceMarble <- traverseLeft 7 (currentMarble g)
(priceMarble, currentMarble') <- removeNode priceMarble
let price = fromIntegral (priceMarble + magicMarble)
pure $
g
{ remainingMarbles = drop 1 $ remainingMarbles g
, currentPlayer = succ (currentPlayer g) `mod` length (scores g)
, currentMarble = currentMarble'
, scores = replaceIndex (currentPlayer g) (+ price) (scores g)
}
gameRound g = do
let nextMarble = head $ remainingMarbles g
successor <- readSTRef (nextNode (currentMarble g))
currentMarble' <- appendNode nextMarble successor
pure $
g
{ remainingMarbles = drop 1 $ remainingMarbles g
, currentPlayer = succ (currentPlayer g) `mod` length (scores g)
, currentMarble = currentMarble'
}
untilM :: Monad m => (a -> m Bool) -> (a -> m a) -> m a -> m a
untilM mPredicate mf ma = do
a <- ma
p <- mPredicate a
if p
then pure a
else untilM mPredicate mf (mf a)
marblesInGame :: Game -> [Marble]
marblesInGame g = [0 .. (finalMarble g)]
input = Game 429 70901 -- 399645
legendaryInput = Game 429 (70901 * 100) -- 3352507536
test0 = Game 7 25 -- 32
test1 = Game 10 1618 -- 8317
test2 = Game 13 7999 -- 146373
test3 = Game 17 1104 -- 2764
test4 = Game 21 6111 -- 54718
test5 = Game 30 5807 -- 37305
play :: Game -> ST s Score
play g = do
startState <- initialState g
endState <- untilM isFinished gameRound (pure startState)
pure $ maximum (scores endState)
--399645
solution1 :: IO Score
solution1 = do
let res = runST $ play input
pure res
--3352507536
solution2 :: IO Score
solution2 = do
putStrLn "Warning: This will take a bit"
let res = runST $ play legendaryInput
pure res