2024-12-21 20:03:24 +01:00

147 lines
5.2 KiB
Haskell

{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
{-# OPTIONS_GHC -Wno-type-defaults #-}
module Day17 (day17_1, day17_2) where
import Control.Monad.State
import Data.Bits
import Data.Char (isDigit)
import Data.List (uncons)
import Data.List.Split (splitOn)
import Data.Maybe (fromJust)
type Program = [Int]
data Computer = Computer
{ registerA :: Integer,
registerB :: Integer,
registerC :: Integer,
program :: Program,
pointer :: Int,
output :: String
}
deriving (Show)
adv :: Int -> State Computer ()
adv n = state $ \c ->
if n `elem` [0 .. 3]
then ((), c {registerA = registerA c `div` (2 ^ n), pointer = pointer c + 2})
else
if n == 4
then ((), c {registerA = registerA c `div` (2 ^ registerA c), pointer = pointer c + 2})
else
if n == 5
then ((), c {registerA = registerA c `div` (2 ^ registerB c), pointer = pointer c + 2})
else ((), c {registerA = registerA c `div` (2 ^ registerC c), pointer = pointer c + 2})
bxl :: Int -> State Computer ()
bxl n = state $ \c -> ((), c {registerB = registerB c `xor` fromIntegral n, pointer = pointer c + 2})
bst :: Int -> State Computer ()
bst n = state $ \c ->
if n `elem` [0 .. 3]
then ((), c {registerB = fromIntegral n `mod` 8, pointer = pointer c + 2})
else
if n == 4
then ((), c {registerB = registerA c `mod` 8, pointer = pointer c + 2})
else
if n == 5
then ((), c {registerB = registerB c `mod` 8, pointer = pointer c + 2})
else ((), c {registerB = registerC c `mod` 8, pointer = pointer c + 2})
jnz :: Int -> State Computer ()
jnz n = state $ \c ->
if registerA c == 0
then ((), c {pointer = pointer c + 2})
else ((), c {pointer = n})
bxc :: State Computer ()
bxc = state $ \c -> ((), c {registerB = registerB c `xor` registerC c, pointer = pointer c + 2})
out :: Int -> State Computer ()
out n = state $ \c ->
if n `elem` [0 .. 3]
then ((), c {output = output c ++ "," ++ show (n `mod` 8), pointer = pointer c + 2})
else
if n == 4
then ((), c {output = output c ++ "," ++ show (registerA c `mod` 8), pointer = pointer c + 2})
else
if n == 5
then ((), c {output = output c ++ "," ++ show (registerB c `mod` 8), pointer = pointer c + 2})
else ((), c {output = output c ++ "," ++ show (registerC c `mod` 8), pointer = pointer c + 2})
bdv :: Int -> State Computer ()
bdv n = state $ \c ->
if n `elem` [0 .. 3]
then ((), c {registerB = registerA c `div` (2 ^ n), pointer = pointer c + 2})
else
if n == 4
then ((), c {registerB = registerA c `div` (2 ^ registerA c), pointer = pointer c + 2})
else
if n == 5
then ((), c {registerB = registerA c `div` (2 ^ registerB c), pointer = pointer c + 2})
else ((), c {registerB = registerA c `div` (2 ^ registerC c), pointer = pointer c + 2})
cdv :: Int -> State Computer ()
cdv n = state $ \c ->
if n `elem` [0 .. 3]
then ((), c {registerC = registerA c `div` (2 ^ n), pointer = pointer c + 2})
else
if n == 4
then ((), c {registerC = registerA c `div` (2 ^ registerA c), pointer = pointer c + 2})
else
if n == 5
then ((), c {registerC = registerA c `div` (2 ^ registerB c), pointer = pointer c + 2})
else ((), c {registerC = registerA c `div` (2 ^ registerC c), pointer = pointer c + 2})
getInstruction :: State Computer (Int, Int)
getInstruction = state $ \c -> ((program c !! pointer c, program c !! (pointer c + 1)), c)
runProgram :: State Computer ()
runProgram = do
c <- get
if pointer c >= length (program c)
then return ()
else do
(ins, op) <- getInstruction
case ins of
0 -> adv op
1 -> bxl op
2 -> bst op
3 -> jnz op
4 -> bxc
5 -> out op
6 -> bdv op
7 -> cdv op
_ -> return ()
runProgram
checkIfCreatesCopy :: Computer -> Bool
checkIfCreatesCopy c =
let o = map read . filter (not . null) . splitOn "," . output $ execState runProgram c
in o == program c
parseInput :: IO ([Integer], [Int])
parseInput = do
contents <- lines <$> readFile "input/day17.txt"
let [r, [p]] = splitOn [""] contents
registers = map (read . filter isDigit) r
prog = map (read . filter isDigit) $ splitOn "," p
return (registers, prog)
day17_1 :: IO ()
day17_1 = do
(registers, prog) <- parseInput
let computer = Computer {registerA = fst . fromJust $ uncons registers, registerB = registers !! 1, registerC = registers !! 2, pointer = 0, program = prog, output = ""}
putStr "Day 17, Puzzle 1 solution: "
print . drop 1 . output $ execState runProgram computer
day17_2 :: IO ()
day17_2 = do
(registers, prog) <- parseInput
let computer = Computer {registerA = 0, registerB = registers !! 1, registerC = registers !! 2, pointer = 0, program = prog, output = ""}
regA = [805464 * 2 ^ 27 ..] -- Threshold derived empirically, a better threshold must be possible because this is very slow, but got the correct answer.
putStrLn $
"Day 17, Puzzle 2 solution: "
++ show (fst . fromJust . uncons $ dropWhile (\x -> not (checkIfCreatesCopy computer {registerA = x})) regA)