|
| 1 | +{-# LANGUAGE OverloadedStrings #-} |
| 2 | +module Day14 |
| 3 | + ( part1 |
| 4 | + , part2 |
| 5 | + ) where |
| 6 | + |
| 7 | +import Data.Int (Int64) |
| 8 | +import Data.Bits ((.&.), (.|.), testBit, clearBit, setBit, complement, zeroBits) |
| 9 | +import Data.List (foldl') |
| 10 | +import Data.Text (Text) |
| 11 | +import qualified Data.Text as Text |
| 12 | +import Data.Map (Map, (!)) |
| 13 | +import qualified Data.Map as Map |
| 14 | +import Control.Monad.State |
| 15 | +import Common (textToInt, readFileUtf8, sum') |
| 16 | + |
| 17 | +data Inst = SetMask (Int64, Int64, Int64) | SetMem (Int, Int64) deriving Show |
| 18 | +type ProgramState = (Map Int Int64, (Int64, Int64, Int64)) |
| 19 | + |
| 20 | +run :: (ProgramState -> Inst -> ProgramState) -> IO () |
| 21 | +run execInst = do |
| 22 | + input <- fmap parseLine . Text.lines <$> (readFileUtf8 "input/day_14.txt") |
| 23 | + let (finalMem, _) = foldl' execInst (Map.empty, (0, 0, 0)) input |
| 24 | + print (sum' $ Map.elems finalMem) |
| 25 | + |
| 26 | +part1 :: IO () |
| 27 | +part1 = run runInst |
| 28 | + |
| 29 | +part2 :: IO () |
| 30 | +part2 = run runInstWithFloat |
| 31 | + |
| 32 | +runInst :: ProgramState -> Inst -> ProgramState |
| 33 | +runInst (mem, mask) (SetMask newMask) = (mem, newMask) |
| 34 | +runInst (mem, mask) (SetMem (addr, val)) = (Map.insert addr maskedVal mem, mask) |
| 35 | + where maskedVal = applyMask mask val |
| 36 | + |
| 37 | +runInstWithFloat :: ProgramState -> Inst -> ProgramState |
| 38 | +runInstWithFloat (mem, mask) (SetMask newMask) = (mem, newMask) |
| 39 | +runInstWithFloat (mem, mask) (SetMem (addr, val)) = |
| 40 | + (foldl' (\mem' addr -> Map.insert addr val mem') mem (fromIntegral <$> maskedAddrs), mask) |
| 41 | + where maskedAddrs = applyMaskWithFloat mask (fromIntegral addr) |
| 42 | + |
| 43 | +binTextToInt64 :: Text -> Int64 |
| 44 | +binTextToInt64 txt = binTextToInt64' 0 (Text.unpack txt) |
| 45 | + where binTextToInt64' acc [] = acc |
| 46 | + binTextToInt64' acc ('0':cs) = binTextToInt64' (2*acc) cs |
| 47 | + binTextToInt64' acc ('1':cs) = binTextToInt64' (2*acc + 1) cs |
| 48 | + |
| 49 | +parseMask :: Text -> (Int64 ,Int64, Int64) |
| 50 | +parseMask mask = (floatMask, andMask, orMask) |
| 51 | + where andMask = binTextToInt64 (Text.replace "X" "1" mask) |
| 52 | + orMask = binTextToInt64 (Text.replace "X" "0" mask) |
| 53 | + floatMask = |
| 54 | + binTextToInt64 (Text.replace "X" "1" (Text.replace "1" "0" mask)) |
| 55 | + |
| 56 | +parseSet :: Text -> (Int, Int64) |
| 57 | +parseSet text = (addr, arg) |
| 58 | + where (_, rest) = Text.break (=='[') text |
| 59 | + (addrText, rest') = Text.break (==']') rest |
| 60 | + arg = textToInt (last (Text.words rest')) |
| 61 | + addr = textToInt (Text.drop 1 addrText) |
| 62 | + |
| 63 | +applyMask :: (Int64, Int64, Int64) -> Int64 -> Int64 |
| 64 | +applyMask (_, andMask, orMask) n = andMask .&. (orMask .|. n) |
| 65 | + |
| 66 | +applyMask' :: (Int64, Int64, Int64) -> Int64 -> Int64 |
| 67 | +applyMask' (_, andMask, orMask) n = orMask .|. (andMask .&. n) |
| 68 | + |
| 69 | +makeFloatMasks :: Int64 -> [(Int64, Int64, Int64)] |
| 70 | +makeFloatMasks mask = |
| 71 | + let bits = filter (testBit mask) [0..35] |
| 72 | + subMasks = powerset bits |
| 73 | + setBits = foldl' setBit 0 |
| 74 | + unsetAll = foldl' clearBit (complement zeroBits) bits |
| 75 | + in (\mask -> (0, unsetAll, setBits mask)) <$> subMasks |
| 76 | + |
| 77 | +powerset :: [a] -> [[a]] |
| 78 | +powerset [] = [[]] |
| 79 | +powerset (x:xs) = [x:ps | ps <- powerset xs] ++ powerset xs |
| 80 | + |
| 81 | +applyMaskWithFloat :: (Int64, Int64, Int64) -> Int64 -> [Int64] |
| 82 | +applyMaskWithFloat mask n = |
| 83 | + let (floatMask, _, orMask) = mask |
| 84 | + floatMasks = makeFloatMasks floatMask |
| 85 | + in zipWith applyMask' floatMasks (repeat $ orMask .|. n) |
| 86 | + |
| 87 | +parseLine :: Text -> Inst |
| 88 | +parseLine line |
| 89 | + | "mask" `Text.isPrefixOf` line = SetMask $ parseMask (last $ Text.words line) |
| 90 | + | otherwise = SetMem $ parseSet line |
0 commit comments