{-# Language ImportQualifiedPost, QuasiQuotes #-}
{-# Language OverloadedStrings #-}
module Main (main) where
import Advent.Format (format)
import Data.IntMap.Strict qualified as IntMap
import Data.Sequence (Seq)
import Data.Sequence qualified as Seq
main :: IO ()
IO ()
main =
do [(Int
players, Int
marbles)] <- [format|2018 9 (%u players; last marble is worth %u points%n)*|]
Int -> IO ()
forall a. Show a => a -> IO ()
print (Int -> Int -> Int
game Int
players Int
marbles)
Int -> IO ()
forall a. Show a => a -> IO ()
print (Int -> Int -> Int
game Int
players (Int
100Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
marbles))
game :: Int -> Int -> Int
game :: Int -> Int -> Int
game Int
players Int
marbles = IntMap Int -> Seq Int -> Int -> Int
go IntMap Int
forall a. IntMap a
IntMap.empty (Int -> Seq Int
forall a. a -> Seq a
Seq.singleton Int
0) Int
1
where
go :: IntMap Int -> Seq Int -> Int -> Int
go IntMap Int
scores Seq Int
circle Int
i
| Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
marbles = IntMap Int -> Int
forall a. Ord a => IntMap a -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum IntMap Int
scores
| Int -> Bool
isScoreMarble Int
i =
case Int -> Seq Int -> Seq Int
forall a. Int -> Seq a -> Seq a
rotate (-Int
7) Seq Int
circle of
Seq Int
Seq.Empty -> String -> Int
forall a. HasCallStack => String -> a
error String
"game: empty circle"
Int
picked Seq.:<| Seq Int
circle' -> IntMap Int -> Seq Int -> Int -> Int
go IntMap Int
scores' Seq Int
circle' (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1)
where
scores' :: IntMap Int
scores' = (Int -> Int -> Int) -> Int -> Int -> IntMap Int -> IntMap Int
forall a. (a -> a -> a) -> Int -> a -> IntMap a -> IntMap a
IntMap.insertWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) (Int
i Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
players) (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
picked) IntMap Int
scores
| Bool
otherwise = IntMap Int -> Seq Int -> Int -> Int
go IntMap Int
scores (Int
i Int -> Seq Int -> Seq Int
forall a. a -> Seq a -> Seq a
Seq.<| Int -> Seq Int -> Seq Int
forall a. Int -> Seq a -> Seq a
rotate Int
2 Seq Int
circle) (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1)
rotate :: Int -> Seq a -> Seq a
rotate :: forall a. Int -> Seq a -> Seq a
rotate Int
n Seq a
xs
| Seq a -> Bool
forall a. Seq a -> Bool
Seq.null Seq a
xs = Seq a
xs
| Bool
otherwise = case Int -> Seq a -> (Seq a, Seq a)
forall a. Int -> Seq a -> (Seq a, Seq a)
Seq.splitAt (Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Seq a -> Int
forall a. Seq a -> Int
Seq.length Seq a
xs) Seq a
xs of
(Seq a
l, Seq a
r) -> Seq a
r Seq a -> Seq a -> Seq a
forall a. Semigroup a => a -> a -> a
<> Seq a
l
isScoreMarble :: Int -> Bool
isScoreMarble :: Int -> Bool
isScoreMarble Int
i = Int
i Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
23 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0