147 lines
5.2 KiB
Haskell
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)
|