[hs] Solve 2019_05 part 1

This commit is contained in:
Joscha 2020-12-06 14:00:45 +00:00
parent 53aacea987
commit e335e9d874
3 changed files with 213 additions and 117 deletions

View file

@ -7,6 +7,7 @@ import qualified Aoc.Y2019.D01 as D01
import qualified Aoc.Y2019.D02 as D02 import qualified Aoc.Y2019.D02 as D02
import qualified Aoc.Y2019.D03 as D03 import qualified Aoc.Y2019.D03 as D03
import qualified Aoc.Y2019.D04 as D04 import qualified Aoc.Y2019.D04 as D04
import qualified Aoc.Y2019.D05 as D05
year :: Year year :: Year
year = Year 2019 year = Year 2019
@ -14,4 +15,5 @@ year = Year 2019
, ( 2, D02.day) , ( 2, D02.day)
, ( 3, D03.day) , ( 3, D03.day)
, ( 4, D04.day) , ( 4, D04.day)
, ( 5, D05.day)
] ]

View file

@ -1,117 +0,0 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Aoc.Y2019.A05
( solve201905
) where
import Control.Monad
import Data.Bifunctor
import Data.Foldable
import qualified Data.Map.Strict as M
import Data.Maybe
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.IO as T
-- Basic types
-- Typesafe addresses and values so we don't confuse the two unless we want to.
newtype Addr = Addr Integer
deriving (Show, Eq, Ord, Enum, Num, Real, Integral)
newtype Value = Value Integer
deriving (Show, Eq, Ord, Enum, Num, Real, Integral)
-- Memory
newtype Memory = Memory { unmemory :: M.Map Addr Value }
instance Show Memory where
show mem = "Memory " <> show (memToList mem)
newMem :: [Integer] -> Memory
newMem = Memory . M.fromList . map (bimap Addr Value) . zip [0..]
memToList :: Memory -> [Integer]
memToList (Memory m) =
let maxAddr = fromMaybe 0 $ S.lookupMax $ M.keysSet m
in map (toInteger . fromMaybe 0 . (m M.!?)) [0..maxAddr]
readMem :: Addr -> Memory -> Maybe Value
readMem addr (Memory mem) = mem M.!? addr
writeMem :: Addr -> Value -> Memory -> Memory
writeMem addr val = Memory . M.insert addr val . unmemory
-- State
data State = State
{ stateMem :: Memory
, stateIdx :: Addr
, stateInput :: [Integer]
} deriving (Show)
newState :: Memory -> State
newState mem = State mem 0 []
data StepError
= Halted
| CouldNotRead Addr
| UnknownOpcode Integer
deriving (Show)
readAt :: State -> Addr -> Either StepError Value
readAt s i = case readMem i $ stateMem s of
Nothing -> Left $ CouldNotRead i
Just v -> Right v
writeAt :: Addr -> Value -> State -> State
writeAt addr val s = s{stateMem = writeMem addr val $ stateMem s}
-- Opcode
data ParamMode = PositionMode | ImmediateMode
deriving (Show, Eq)
digits :: Integer -> [Integer]
digits i = (i `mod` 10) : digits (i `div` 10)
pmFromDigit :: Integer -> ParamMode
pmFromDigit 0 = PositionMode
pmFromDigit 1 = ImmediateMode
pmFromDigit _ = undefined
paramModes :: Integer -> [ParamMode]
paramModes i = map pmFromDigit $ drop 2 $ (digits i ++ repeat 0)
data Operand = Direct Value | Indirect Addr
deriving (Show)
data Opcode
= OpAdd Operand Operand Addr
| OpMul Operand Operand Addr
| OpInput Addr
| OpOutput Operand Addr
| OpHalt
deriving (Show)
opWidth :: Opcode -> Addr
opWidth (OpAdd _ _ _) = 4
opWidth (OpMul _ _ _) = 4
opWidth (OpInput _) = 2
opWidth (OpOutput _ _) = 3
opWidth OpHalt = 1
parseOpcode :: State -> Either StepError Opcode
parseOpcode s = do
let idx = stateIdx s
undefined
solve201905 :: FilePath -> IO ()
solve201905 f = do
stuff <- readFile f
putStrLn ">> Part 1"
putStrLn ">> Part 2"

211
hs/src/Aoc/Y2019/D05.hs Normal file
View file

@ -0,0 +1,211 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module Aoc.Y2019.D05
( day
) where
import Data.Bifunctor
import Data.Maybe
import Text.Read (readMaybe)
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Aoc.Day
import Aoc.Parse
-------------------
-- General types --
-------------------
newtype Addr = Addr Integer
deriving (Show, Eq, Ord, Enum, Num, Real, Integral)
newtype Value = Value Integer
deriving (Show, Eq, Ord, Enum, Num, Real, Integral)
data StepError
= Halted State
| CouldNotRead Addr
| UnknownOpcode Addr Integer
| InvalidInput T.Text
deriving (Show)
displayError :: StepError -> T.Text
displayError (Halted s) = "Halted at " <> T.pack (show $ stateIdx s)
displayError (CouldNotRead a) = "Could not read value at " <> T.pack (show a)
displayError (UnknownOpcode a i) = "Unknown opcode " <> T.pack (show i) <> " at " <> T.pack (show a)
displayError (InvalidInput t) = "Invalid input: " <> t
type StepM = Either StepError
------------
-- Memory --
------------
newtype Memory = Memory { unmemory :: M.Map Addr Value }
instance Show Memory where
show mem = "Memory " <> show (memToList mem)
newMem :: [Integer] -> Memory
newMem = Memory . M.fromList . map (bimap Addr Value) . zip [0..]
memToList :: Memory -> [Integer]
memToList (Memory m) =
let maxAddr = fromMaybe 0 $ S.lookupMax $ M.keysSet m
in map (toInteger . fromMaybe 0 . (m M.!?)) [0..maxAddr]
readMem :: Addr -> Memory -> Maybe Value
readMem addr (Memory mem) = mem M.!? addr
writeMem :: Addr -> Value -> Memory -> Memory
writeMem addr val = Memory . M.insert addr val . unmemory
-----------
-- State --
-----------
data State = State
{ stateMem :: Memory
, stateIdx :: Addr
} deriving (Show)
newState :: Memory -> State
newState mem = State mem 0
readAt :: State -> Addr -> StepM Value
readAt s i = case readMem i $ stateMem s of
Nothing -> Left $ CouldNotRead i
Just v -> Right v
writeAt :: State -> Addr -> Value -> State
writeAt s addr val = s{stateMem = writeMem addr val $ stateMem s}
-------------
-- Opcodes --
-------------
data ParamMode = PositionMode | ImmediateMode
deriving (Show, Eq)
-- | Infinite list of param modes based on the digits of a number. The default
-- mode is 'PositionMode'.
paramModes :: Integer -> [ParamMode]
paramModes = map pmFromDigit . digits
where
digits i = (i `mod` 10) : digits (i `div` 10)
pmFromDigit 0 = PositionMode
pmFromDigit _ = ImmediateMode
data Operand = Direct Value | Indirect Addr
deriving (Show)
pmToOp :: ParamMode -> Value -> Operand
pmToOp PositionMode = Indirect . fromIntegral
pmToOp ImmediateMode = Direct
data Opcode
= OpAdd Operand Operand Addr
| OpMul Operand Operand Addr
| OpInput Addr
| OpOutput Operand
| OpHalt
deriving (Show)
--------------
-- Stepping --
--------------
-- Parsing opcodes
getOp :: State -> Addr -> [ParamMode] -> Int -> StepM Operand
getOp s a pms i = do
let pm = pms !! i
value <- readAt s $ a + 1 + fromIntegral i
pure $ pmToOp pm value
getAddr :: State -> Addr -> Int -> StepM Addr
getAddr s a i = fromIntegral <$> readAt s (a + 1 + fromIntegral i)
parseOpcode :: State -> StepM Opcode
parseOpcode s = do
let a = stateIdx s
value <- toInteger <$> readAt s a
let opcode = value `mod` 100
pms = paramModes $ value `div` 100
case opcode of
1 -> OpAdd <$> getOp s a pms 0 <*> getOp s a pms 1 <*> getAddr s a 2
2 -> OpMul <$> getOp s a pms 0 <*> getOp s a pms 1 <*> getAddr s a 2
3 -> OpInput <$> getAddr s a 0
4 -> OpOutput <$> getOp s a pms 0
99 -> pure OpHalt
_ -> Left $ UnknownOpcode a opcode
-- Executing opcodes
data StepResult
= NormalStep State
| InputStep (Integer -> State)
| OutputStep State Integer
readOp :: State -> Operand -> StepM Value
readOp _ (Direct v) = pure v
readOp s (Indirect a) = readAt s a
incIdx :: Int -> State -> State
incIdx i s = s{stateIdx = stateIdx s + fromIntegral i}
execOpcode :: State -> Opcode -> StepM StepResult
execOpcode s (OpAdd x y r) = do
vx <- readOp s x
vy <- readOp s y
pure $ NormalStep $ incIdx 4 $ writeAt s r $ vx + vy
execOpcode s (OpMul x y r) = do
vx <- readOp s x
vy <- readOp s y
pure $ NormalStep $ incIdx 4 $ writeAt s r $ vx * vy
execOpcode s (OpInput r) = pure $ InputStep $ incIdx 2 . writeAt s r . fromInteger
execOpcode s (OpOutput x) = do
vx <- readOp s x
pure $ OutputStep (incIdx 2 s) $ toInteger vx
execOpcode s OpHalt = Left $ Halted s
step :: State -> Either StepError StepResult
step s = execOpcode s =<< parseOpcode s
run :: State -> IO StepError
run s = case step s of
Left e -> pure e
Right (NormalStep s') -> run s'
Right (InputStep f) -> do
putStr "?> "
t <- T.getLine
case readMaybe $ T.unpack t of
Nothing -> pure $ InvalidInput t
Just i -> run $ f i
Right (OutputStep s' o) -> do
putStrLn $ "-> " ++ show o
run s'
runAndPrintResult :: State -> IO ()
runAndPrintResult s = do
e <- run s
T.putStrLn $ displayError e
parser :: Parser Memory
parser = newMem <$> (signed (pure ()) decimal `sepBy` char ',') <* newline
solver :: Memory -> IO ()
solver mem = do
putStrLn ">> Part 1"
runAndPrintResult $ newState mem
putStrLn ""
putStrLn ">> Part 2"
day :: Day
day = dayParse parser solver