import: common-extensions, build-directives
main-is: advent20/Main.hs
build-depends: linear, mtl, containers
+
+executable advent21
+ import: common-extensions, build-directives
+ main-is: advent21/Main.hs
+ build-depends: text, attoparsec, mtl, containers, monad-loops
--- Writeup at https://work.njae.me.uk/2021/12/18/advent-of-code-2021-day-16/
-
+-- Writeup at https://work.njae.me.uk/2021/12/23/advent-of-code-2021-day-20/
import Control.Monad.State.Strict
import Control.Monad.Reader
enhanceImage n = do newImage
enhanceImage (n - 1)
+
newImage :: ImageEnhancer ()
newImage =
- do image <- get
- let region = explicitRegion image
+ do region <- gets explicitRegion
let region' = expandRegion region
let heres = range region'
- let distant = distantPixel image
- newPixelStates <- mapM newPixel heres
- let grid' = S.fromList $ catMaybes newPixelStates
+ newPixels <- mapM newPixel heres
+ let grid' = S.fromList $ catMaybes newPixels
+ distant <- gets distantPixel
enhancement <- ask
let distant' = if distant then (last enhancement) else (head enhancement)
put $ Image {grid = grid', distantPixel = distant', explicitRegion = region'}
-
showImage :: Image -> String
showImage image =
unlines $ [ [showPixel (V2 r c) | c <- [minC..maxC] ] | r <- [minR..maxR]]
d <- gets distantPixel
r <- gets explicitRegion
return $ map (findContents g d r) nbrs
+ -- mapM findContents nbrs
findContents :: S.Set Pixel -> Bool -> (Pixel, Pixel) -> Pixel -> Bool
findContents grid distant region here
| inRange region here = here `S.member` grid
| otherwise = distant
+-- more consitent but much slower
+-- findContents :: Pixel -> ImageEnhancer Bool
+-- findContents here =
+-- do g <- gets grid
+-- distant <- gets distantPixel
+-- region <- gets explicitRegion
+-- return $ if inRange region here
+-- then (here `S.member` g)
+-- else distant
neighbours :: [Pixel]
neighbours = [V2 r c | r <- [-1, 0, 1], c <- [-1, 0, 1]]
expandRegion :: (Pixel, Pixel) -> (Pixel, Pixel)
expandRegion ((V2 r0 c0), (V2 r1 c1)) = (V2 (r0 - 1) (c0 - 1), V2 (r1 + 1) (c1 + 1))
+parse :: String -> (Enhancement, Image)
parse text = (enhancement, image)
where ls = lines text
enhancement = [ c == '#' | c <- head ls]
maxCol = (length $ head rows) - 1
grid = S.fromList [V2 r c | r <- [0..maxRow], c <- [0..maxCol], (rows!!r)!!c == '#']
-
+intify :: [Bool] -> Int
intify pixels = foldl' addBit 0 pixels
where addBit w b = (w * 2) + (if b then 1 else 0)
-
--- wordify :: BS.BitString -> Integer
--- wordify bs = foldl' addBit 0 $ BS.to01List bs
--- where addBit w b = (w * 2) + (fromIntegral b)
--- /dev/null
+-- Writeup at https://work.njae.me.uk/2021/12/23/advent-of-code-2021-day-20/
+
+import Data.Text ()
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text hiding (take, takeWhile, dropWhile)
+import Control.Applicative
+
+-- import Control.Monad.State.Strict
+-- import Control.Monad.Reader
+-- import Control.Monad.Writer
+-- import Control.Monad.RWS.Strict
+-- import Control.Monad.Loops
+
+import qualified Data.Map.Strict as M
+import Data.Map.Strict ((!))
+
+data Player = Player1 | Player2 deriving (Eq, Ord, Show)
+
+data PlayerState = PlayerState
+ { position :: Int
+ , score :: Int
+ } deriving (Eq, Show)
+
+data Game = Game
+ { players :: M.Map Player PlayerState
+ , current :: Player
+ , rolls :: Int
+ } deriving (Eq, Show)
+
+
+-- type GameState = State Game
+
+
+main :: IO ()
+main =
+ do text <- TIO.readFile "data/advent21.txt"
+ let game = successfulParse text
+ print game
+ print $ gameStep game
+ print $ take 8 $ iterate gameStep game
+ print $ finished game
+ print $ part1 game
+ -- let (enhancement, image) = parse text
+ -- print $ part1 enhancement image
+ -- print $ part2 enhancement image
+
+part1 game = scoreGame finalGame
+ where finalGame = head $ dropWhile (not . finished) $ iterate gameStep game
+
+finished game = any (>= 1000) $ map score $ M.elems (players game)
+
+scoreGame game = (rolls game) * losingScore
+ where losingScore = minimum $ map score $ M.elems (players game)
+
+
+gameStep :: Game -> Game
+gameStep game = game'
+ where rs = rolls game + 1
+ theseRolls = [n `mod1` 100 | n <- [rs..(rs + 2)]] :: [Int]
+ activePlayer = (players game) ! (current game)
+ pos = position activePlayer
+ sc = score activePlayer
+ pos' = (pos + (sum theseRolls)) `mod1` 10
+ sc' = sc + pos'
+ activePlayer' = PlayerState {position = pos', score = sc'}
+ current' = nextPlayer (current game)
+ players' = M.insert (current game) activePlayer' (players game)
+ game' = Game { players = players'
+ , current = current'
+ , rolls = rolls game + 3
+ }
+
+
+
+
+
+nextPlayer :: Player -> Player
+nextPlayer Player1 = Player2
+nextPlayer Player2 = Player1
+
+mod1 :: Int -> Int -> Int
+mod1 a b = ((a - 1) `mod` b) + 1
+
+-- Parsing
+
+playerP = ("1" *> pure Player1) <|> ("2" *> pure Player2)
+
+playerStateP = playerify <$> ("Player " *> playerP) <*> (" starting position: " *> decimal)
+ where playerify name pos = (name, PlayerState {position = pos, score = 0})
+
+gameP = gamify <$> playerStateP `sepBy` endOfLine
+ where gamify ps = Game {rolls = 0, current = Player1, players = M.fromList ps}
+
+
+-- successfulParse :: Text -> (Integer, [Maybe Integer])
+successfulParse input =
+ case parseOnly gameP input of
+ Left _err -> Game {rolls=0, current=Player1, players=M.empty} -- TIO.putStr $ T.pack $ parseErrorPretty err
+ Right game -> game
+
--- /dev/null
+Player 1 starting position: 4
+Player 2 starting position: 3
--- /dev/null
+Player 1 starting position: 4
+Player 2 starting position: 8