[hs] Solve 2020_08 part 2

This commit is contained in:
Joscha 2020-12-08 09:59:45 +00:00
parent 12d4bf1471
commit 9b4e139030

View file

@ -4,6 +4,14 @@ module Aoc.Y2020.D08
( day ( day
) where ) where
import Control.Applicative
import Control.Monad
import Data.Functor
import Data.Maybe
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.State
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.Set as Set import qualified Data.Set as Set
@ -21,52 +29,90 @@ parser = manyLines $ Instr <$> (pOpcode <* char ' ') <*> signed (pure ()) decima
where where
pOpcode = (Acc <$ string "acc") <|> (Jmp <$ string "jmp") <|> (Nop <$ string "nop") pOpcode = (Acc <$ string "acc") <|> (Jmp <$ string "jmp") <|> (Nop <$ string "nop")
data State = State data Machine = Machine
{ sInstrs :: Map.Map Int Instr { mInstrs :: Map.Map Int Instr
, sIdx :: Int , mHaltIdx :: Int
, sAcc :: Int , mVisited :: Set.Set Int
, mIdx :: Int
, mAcc :: Int
} deriving (Show) } deriving (Show)
newState :: [Instr] -> State newMachine :: [Instr] -> Machine
newState instrs = State (Map.fromList $ zip [0..] instrs) 0 0 newMachine instrs = Machine
{ mInstrs = Map.fromList $ zip [0..] instrs
, mHaltIdx = length instrs
, mVisited = Set.empty
, mIdx = 0
, mAcc = 0
}
incIdx :: Int -> State -> State data StepError
incIdx delta s = s { sIdx = delta + sIdx s } = InvalidIdx Int
| DuplicatedIdx Int
| Halted
deriving (Show, Eq)
incAcc :: Int -> State -> State type RunM = ExceptT StepError (State Machine)
incAcc delta s = s { sAcc = delta + sAcc s }
step :: State -> Maybe State visit :: Int -> RunM ()
step s = do visit idx = lift $ modify $ \m -> m { mVisited = Set.insert idx $ mVisited m }
(Instr op val) <- sInstrs s Map.!? sIdx s
pure $ case op of
Acc -> incIdx 1 $ incAcc val s
Jmp -> incIdx val s
Nop -> incIdx 1 s
run :: State -> [State] incIdx :: Int -> RunM ()
run s = s : case step s of incIdx delta = lift $ modify $ \m -> m { mIdx = delta + mIdx m }
Nothing -> []
Just s' -> run s'
untilRepeatingIdx :: [State] -> [State] incAcc :: Int -> RunM ()
untilRepeatingIdx = helper Set.empty incAcc delta = lift $ modify $ \m -> m { mAcc = delta + mAcc m}
where
helper _ [] = [] getInstr :: RunM Instr
helper idxs (s:ss) getInstr = do
| i `Set.member` idxs = [] m <- lift get
| otherwise = s : helper (Set.insert i idxs) ss let idx = mIdx m
where i = sIdx s when (idx == mHaltIdx m) $ throwE Halted
when (idx `Set.member` mVisited m) $ throwE $ DuplicatedIdx idx
case mInstrs m Map.!? idx of
Nothing -> throwE $ InvalidIdx idx
Just instr -> visit idx $> instr
step :: RunM ()
step = do
(Instr op val) <- getInstr
case op of
Acc -> incAcc val *> incIdx 1
Jmp -> incIdx val
Nop -> incIdx 1
run :: Machine -> (Machine, StepError)
run m = case runState (runExceptT (forever step)) m of
(Left e, m') -> (m', e)
(Right _, _) -> error "infinite loop was not infinite"
variations :: Machine -> [Machine]
variations m = do
let instrs = mInstrs m
maxIdx = maximum $ Map.keys instrs
idx <- [0 .. maxIdx - 1]
(Instr op val) <- maybeToList $ instrs Map.!? idx
newOp <- case op of
Acc -> []
Jmp -> pure Nop
Nop -> pure Jmp
let newInstrs = Map.insert idx (Instr newOp val) instrs
pure m { mInstrs = newInstrs}
solver :: [Instr] -> IO () solver :: [Instr] -> IO ()
solver instrs = do solver instrs = do
let s = newState instrs let m = newMachine instrs
putStrLn ">> Part 1" putStrLn ">> Part 1"
print $ sAcc $ last $ untilRepeatingIdx $ run s let (m1, e1) = run m
print e1
print $ mAcc m1
putStrLn "" putStrLn ""
putStrLn ">> Part 2" putStrLn ">> Part 2"
let m2 = head [m' | (m', Halted) <- run <$> variations m]
print Halted
print $ mAcc m2
day :: Day day :: Day
day = dayParse parser solver day = dayParse parser solver