Day 14 - Part 1
This commit is contained in:
parent
f3729952e1
commit
2b0283a9b6
1 changed files with 128 additions and 1 deletions
129
day14/main.hs
129
day14/main.hs
|
@ -1,6 +1,6 @@
|
||||||
#! /usr/bin/env -S"ANSWER=42" nix-shell
|
#! /usr/bin/env -S"ANSWER=42" nix-shell
|
||||||
#! nix-shell -p ghcid
|
#! nix-shell -p ghcid
|
||||||
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [pretty-simple])"
|
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [pretty-simple attoparsec])"
|
||||||
#! nix-shell -i "ghcid -c 'ghci' -T main"
|
#! nix-shell -i "ghcid -c 'ghci' -T main"
|
||||||
|
|
||||||
{-# OPTIONS_GHC -Wall -Wincomplete-uni-patterns #-}
|
{-# OPTIONS_GHC -Wall -Wincomplete-uni-patterns #-}
|
||||||
|
@ -11,6 +11,133 @@
|
||||||
import Debug.Trace (trace)
|
import Debug.Trace (trace)
|
||||||
import Text.Pretty.Simple
|
import Text.Pretty.Simple
|
||||||
|
|
||||||
|
import Data.IntMap (IntMap)
|
||||||
|
import qualified Data.IntMap as I
|
||||||
|
|
||||||
|
import Data.Bits ((.&.),(.|.),FiniteBits,bit,complementBit)
|
||||||
|
import Data.Maybe (catMaybes)
|
||||||
|
|
||||||
|
import Numeric (showIntAtBase)
|
||||||
|
import Data.Char (intToDigit)
|
||||||
|
|
||||||
|
import Data.Attoparsec.Text (Parser)
|
||||||
|
import qualified Data.Attoparsec.Text as P
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
import Data.String (IsString)
|
||||||
|
|
||||||
|
import Data.Monoid (Sum(..))
|
||||||
|
|
||||||
|
exampleData :: String
|
||||||
|
exampleData = unlines $
|
||||||
|
[ "mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X"
|
||||||
|
, "mem[8] = 11"
|
||||||
|
, "mem[7] = 101"
|
||||||
|
, "mem[8] = 0"
|
||||||
|
]
|
||||||
|
|
||||||
|
-- The *special* mask is essentially a successive application of:
|
||||||
|
-- - (.&.) with 0 (all others are 1), in order to force 0 on some bits, or
|
||||||
|
-- - (.|.) with 1 (all others are 0), in order to force 1 on some bits.
|
||||||
|
--
|
||||||
|
data BW a = BWAnd a
|
||||||
|
| BWOr a
|
||||||
|
|
||||||
|
instance (Show a, Integral a) => Show (BW a)
|
||||||
|
where
|
||||||
|
show (BWAnd a) = "And " <> showIntAtBase 2 intToDigit a ""
|
||||||
|
show (BWOr a) = "Or " <> showIntAtBase 2 intToDigit a ""
|
||||||
|
|
||||||
|
mkBW :: String -> Mask
|
||||||
|
mkBW xs | length xs == 36 = catMaybes $ map go (zip [35,34..] xs)
|
||||||
|
where
|
||||||
|
go (p,'1') = Just $ BWOr (bit p)
|
||||||
|
go (p,'0') = Just $ BWAnd (complementBit (2 ^ (36 :: Int) - 1) p)
|
||||||
|
go (p,'X') = Nothing
|
||||||
|
go (_,c) = trace ("Illegal char: " <> show c) $ undefined
|
||||||
|
mkBW xs | otherwise = trace "Mask is not 36 bit long" $ undefined
|
||||||
|
|
||||||
|
applyMask :: (FiniteBits a) => [BW a] -> a -> a
|
||||||
|
applyMask xs a = foldl applyBitwise a xs
|
||||||
|
where
|
||||||
|
applyBitwise v (BWAnd n) = n .&. v
|
||||||
|
applyBitwise v (BWOr n) = n .|. v
|
||||||
|
--
|
||||||
|
|
||||||
|
-- We need a datatype to hold our datastructure
|
||||||
|
--
|
||||||
|
type Mask = [BW Int]
|
||||||
|
type Mem = IntMap Int
|
||||||
|
type Addr = Int
|
||||||
|
|
||||||
|
data InitBlock = InitBlock { unMask :: Mask, unInitLines :: [(Addr,Int)] }
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
data Program = Program { unMem :: Mem, unInits :: [InitBlock] }
|
||||||
|
deriving Show
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Now, let's parse our input uwu
|
||||||
|
--
|
||||||
|
-- mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
|
||||||
|
parseMask :: Parser Mask
|
||||||
|
parseMask = do
|
||||||
|
maskStr <- "mask = " *> P.take 36
|
||||||
|
P.endOfLine
|
||||||
|
pure $ mkBW $ T.unpack maskStr
|
||||||
|
|
||||||
|
-- mem[x] = y
|
||||||
|
parseInitLines :: Parser (Addr,Int)
|
||||||
|
parseInitLines = do
|
||||||
|
x <- "mem[" *> P.decimal
|
||||||
|
y <- "] = " *> P.decimal
|
||||||
|
P.endOfLine
|
||||||
|
pure $ (x,y)
|
||||||
|
|
||||||
|
parseBlock :: Parser InitBlock
|
||||||
|
parseBlock = do
|
||||||
|
mask <- parseMask
|
||||||
|
ix <- P.many1 parseInitLines
|
||||||
|
pure $ InitBlock mask ix
|
||||||
|
|
||||||
|
parseInput :: Parser [InitBlock]
|
||||||
|
parseInput = do
|
||||||
|
bx <- P.many1 parseBlock
|
||||||
|
P.endOfInput
|
||||||
|
pure bx
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Now we've got everyting for part1, let's solve the thing
|
||||||
|
--
|
||||||
|
runInitBlock :: Mem -> InitBlock -> Mem
|
||||||
|
runInitBlock mem (InitBlock mask inits) =
|
||||||
|
I.union res mem
|
||||||
|
where
|
||||||
|
res = foldMap (\(addr,v) -> I.insert addr (applyMask mask v) I.empty) $ reverse inits
|
||||||
|
|
||||||
|
solvePart1 :: Text -> Either String Int
|
||||||
|
solvePart1 input = do
|
||||||
|
bx <- P.parseOnly parseInput input
|
||||||
|
pure . getSum . (foldMap Sum) . I.elems $ fullMem bx
|
||||||
|
where
|
||||||
|
fullMem :: [InitBlock] -> Mem
|
||||||
|
fullMem = foldMap (runInitBlock I.empty) . reverse
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
|
input <- T.pack <$> readFile "day14/input"
|
||||||
|
putStrLn ":: Tests - Part 1"
|
||||||
|
pPrint . lines $ exampleData
|
||||||
|
print $ mkBW "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X"
|
||||||
|
print $ applyMask (mkBW "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X") 11
|
||||||
|
print $ applyMask (mkBW "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X") 101
|
||||||
|
print $ applyMask (mkBW "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X") 0
|
||||||
|
pPrint . P.parseOnly parseBlock . T.pack $ exampleData
|
||||||
|
-- pPrint . P.parseOnly parseInput $ input
|
||||||
putStrLn ":: Day 14 - Part 1"
|
putStrLn ":: Day 14 - Part 1"
|
||||||
|
print . solvePart1 . T.pack $ exampleData
|
||||||
|
print . solvePart1 $ input
|
||||||
|
|
Loading…
Reference in a new issue