-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.hs
78 lines (66 loc) · 2.24 KB
/
run.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
{-# LANGUAGE RecordWildCards #-}
import Control.Monad (forM_)
import Data.List (foldl')
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Sequence (Seq(..), (|>), (<|))
import qualified Data.Sequence as Seq
type Counter a = HashMap a Int
data Game = Game { players :: Int
, turn :: Int
, board :: Seq Int
, scores :: Counter Int }
deriving Show
emptyGame :: Int -> Game
emptyGame p = Game { players = p
, turn = 0
, board = Seq.empty
, scores = HashMap.empty }
highscore :: Game -> Int
highscore = maximum . HashMap.elems . scores
rotateLeft :: Int -> Seq Int -> Seq Int
rotateLeft _ Empty = Seq.empty
rotateLeft 0 s = s
rotateLeft n (x :<| s) = rotateLeft (n-1) (s |> x)
rotateRight :: Int -> Seq Int -> Seq Int
rotateRight _ Empty = Seq.empty
rotateRight 0 s = s
rotateRight n (s :|> x) = rotateRight (n-1) (x <| s)
addScore :: Counter Int -> Int -> Int -> Counter Int
addScore s p n = HashMap.unionWith (+) s (HashMap.singleton p n)
stepGame :: Game -> Int -> Game
stepGame g@Game {..} i
| i > 0 && i `mod` 23 == 0 =
let (x :<| board') = rotateRight 7 board
in g { scores = addScore scores turn (i + x)
, turn = (turn + 1) `mod` players
, board = board' }
| otherwise = g { turn = (turn + 1) `mod` players
, board = i <| rotateLeft 2 board }
solve :: Int -> Int -> Int
solve players lastMarble =
highscore $ foldl' stepGame (emptyGame players) [0..lastMarble]
testCases :: [(Int, Int, Int)]
testCases =
[ (10, 1618, 8317)
, (13, 7999, 146373)
, (17, 1104, 2764)
, (21, 6111, 54718)
, (30, 5807, 37305) ]
test :: IO ()
test = forM_ testCases $ \(ps, lm, r) -> do
let result = solve ps lm
if result == r
then putStrLn $ show ps ++ ", " ++ show lm ++ " succeeded!"
else putStrLn $ show ps
++ ", "
++ show lm
++ " fail! Expected "
++ show r
++ " but got "
++ show result
main :: IO ()
main = do
-- 427 players; last marble is worth 70723 points
print (solve 427 70723)
print (solve 427 7072300)