[hs] Solve 2020_08 part 2
This commit is contained in:
parent
12d4bf1471
commit
9b4e139030
1 changed files with 79 additions and 33 deletions
|
|
@ -4,8 +4,16 @@ module Aoc.Y2020.D08
|
||||||
( day
|
( day
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import Control.Applicative
|
||||||
import qualified Data.Set as Set
|
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.Set as Set
|
||||||
|
|
||||||
import Aoc.Day
|
import Aoc.Day
|
||||||
import Aoc.Parse
|
import Aoc.Parse
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue