|
4 | 4 | {-# LANGUAGE KindSignatures #-}
|
5 | 5 | {-# LANGUAGE MultiParamTypeClasses #-}
|
6 | 6 | {-# LANGUAGE TemplateHaskell #-}
|
| 7 | +{-# LANGUAGE TypeOperators #-} |
7 | 8 |
|
8 | 9 | module AOC2017.Day18 (day18a, day18b) where
|
9 | 10 |
|
10 | 11 | import AOC2017.Types (Challenge)
|
11 | 12 | import AOC2017.Util.Accum (AccumT(..), execAccumT, look, add)
|
12 | | -import AOC2017.Util.Tape (Tape(..), HasTape(..), move, unsafeTape) |
13 | | -import Control.Applicative (many, empty) |
14 | | -import Control.Lens (makeClassy, use, at, non, (%=), use, (.=), (<>=), zoom) |
15 | | -import Control.Monad (guard, when) |
| 13 | +import Control.Applicative |
| 14 | +import Control.Lens |
| 15 | +import Control.Monad |
| 16 | +import Control.Monad.Fail |
16 | 17 | import Control.Monad.Prompt (Prompt, prompt, runPromptM)
|
17 | | -import Control.Monad.State (MonadState(get,put), StateT(..), State, execStateT, evalState) |
| 18 | +import Control.Monad.State (MonadState, StateT(..), State, execStateT, evalState) |
18 | 19 | import Control.Monad.Trans.Class (MonadTrans(lift))
|
19 | 20 | import Control.Monad.Trans.Maybe (MaybeT(..))
|
20 | | -import Control.Monad.Writer (MonadWriter(..), WriterT(..), Writer, execWriter) |
| 21 | +import Control.Monad.Writer |
21 | 22 | import Data.Char (isAlpha)
|
22 | 23 | import Data.Kind (Type)
|
23 | 24 | import Data.Maybe (fromJust)
|
24 | 25 | import Data.Monoid (First(..), Last(..))
|
| 26 | +import Data.Type.Disjunction |
| 27 | +import qualified Data.List.PointedList as P |
25 | 28 | import qualified Data.Map as M
|
26 | 29 | import qualified Data.Vector.Sized as V
|
27 | 30 |
|
@@ -53,135 +56,148 @@ parseOp inp = case words inp of
|
53 | 56 | "jgz":(addr->x):(addr->y):_ -> OJgz x y
|
54 | 57 | _ -> error "Bad parse"
|
55 | 58 |
|
56 | | -parse :: String -> Tape Op |
57 | | -parse = unsafeTape . map parseOp . lines |
| 59 | +parse :: String -> P.PointedList Op |
| 60 | +parse = fromJust .P.fromList . map parseOp . lines |
58 | 61 |
|
59 | 62 | {-
|
60 | 63 | **************************
|
61 | 64 | * The Abstract Machine *
|
62 | 65 | **************************
|
63 | 66 | -}
|
64 | 67 |
|
| 68 | +data Memory :: Type -> Type where |
| 69 | + MGet :: Char -> Memory Int |
| 70 | + MSet :: Char -> Int -> Memory () |
| 71 | + MMov :: Int -> Memory () |
| 72 | + MPk :: Memory Op |
| 73 | + |
65 | 74 | -- | Abstract data type describing "IO" available to the abstract machine
|
66 | 75 | data Command :: Type -> Type where
|
67 | 76 | CRcv :: Int -> Command Int -- ^ input is current value of buffer
|
68 | 77 | CSnd :: Int -> Command () -- ^ input is thing being sent
|
69 | 78 |
|
70 | | -type Machine = Prompt Command |
71 | | - |
72 | | -rcvMachine :: Int -> Machine Int |
73 | | -rcvMachine = prompt . CRcv |
74 | | - |
75 | | -sndMachine :: Int -> Machine () |
76 | | -sndMachine = prompt . CSnd |
77 | | - |
78 | | -data ProgState = PS { _psTape :: Tape Op |
| 79 | +type Machine = Prompt (Memory :|: Command) |
| 80 | + |
| 81 | +cRcv :: Int -> Machine Int |
| 82 | +cRcv = prompt . R . CRcv |
| 83 | +cSnd :: Int -> Machine () |
| 84 | +cSnd = prompt . R . CSnd |
| 85 | +cGet :: Char -> Machine Int |
| 86 | +cGet = prompt . L . MGet |
| 87 | +cSet :: Char -> Int -> Machine () |
| 88 | +cSet r = prompt . L . MSet r |
| 89 | +cMov :: Int -> Machine () |
| 90 | +cMov = prompt . L . MMov |
| 91 | +cPk :: Machine Op |
| 92 | +cPk = prompt $ L MPk |
| 93 | + |
| 94 | +data ProgState = PS { _psTape :: P.PointedList Op |
79 | 95 | , _psRegs :: M.Map Char Int
|
80 | 96 | }
|
81 | 97 | makeClassy ''ProgState
|
82 | 98 |
|
83 | | --- | Context in which a 'Duet' program runs |
84 | | -type Duet = MaybeT (StateT ProgState Machine) |
85 | | -execDuet :: Duet a -> ProgState -> Machine ProgState |
86 | | -execDuet = execStateT . runMaybeT |
87 | | - |
88 | 99 | -- | Single step through program tape.
|
89 | | -stepTape :: Duet () |
90 | | -stepTape = use (psTape . tFocus) >>= \case |
| 100 | +stepTape :: Machine () |
| 101 | +stepTape = cPk >>= \case |
91 | 102 | OSnd x -> do
|
92 | | - lift . lift . sndMachine =<< addrVal x |
93 | | - advance 1 |
| 103 | + cSnd =<< addrVal x |
| 104 | + cMov 1 |
94 | 105 | OBin f x y -> do
|
95 | 106 | yVal <- addrVal y
|
96 | | - psRegs . at x . non 0%=(`f` yVal) |
97 | | - advance 1 |
| 107 | + cSet x . (`f` yVal)=<< cGet x |
| 108 | + cMov 1 |
98 | 109 | ORcv x -> do
|
99 | | - y <- lift . lift . rcvMachine =<< use (psRegs . at x . non 0) |
100 | | - psRegs . at x . non 0.= y |
101 | | - advance 1 |
| 110 | + y <- cRcv =<< cGet x |
| 111 | + cSet x y |
| 112 | + cMov 1 |
102 | 113 | OJgz x y -> do
|
103 | 114 | xVal <- addrVal x
|
104 | | - moveAmt <- if xVal > 0 |
105 | | - then addrVal y |
106 | | - else return 1 |
107 | | - advance moveAmt |
| 115 | + cMov =<< if xVal > 0 |
| 116 | + then addrVal y |
| 117 | + else return 1 |
108 | 118 | where
|
109 | | - addrVal (Left r) = use (psRegs . at r . non 0) |
| 119 | + addrVal (Left r ) = cGet r |
110 | 120 | addrVal (Right x) = return x
|
111 | | - advance n = do |
112 | | - Just t' <- move n <$> use psTape |
| 121 | + |
| 122 | +interpMem |
| 123 | + :: (MonadState s m, MonadPlus m, HasProgState s) |
| 124 | + => Memory a |
| 125 | + -> m a |
| 126 | +interpMem = \case |
| 127 | + MGet c -> use (psRegs . at c . non 0) |
| 128 | + MSet c x -> psRegs . at c . non 0 .= x |
| 129 | + MMov n -> do |
| 130 | + Just t' <- P.moveN n <$> use psTape |
113 | 131 | psTape .= t'
|
| 132 | + MPk -> use (psTape . P.focus) |
114 | 133 |
|
115 | 134 | {-
|
116 | 135 | ************************
|
117 | 136 | * Context for Part A *
|
118 | 137 | ************************
|
119 | 138 | -}
|
120 | 139 |
|
121 | | --- | Context in which to interpret Command for Part A |
122 | | --- |
123 | | --- Accum parameter is the most recent sent item. Writer parameter is the |
124 | | --- first Rcv'd item. |
125 | | -type PartA = AccumT (Last Int) (Writer (First Int)) |
126 | | -execPartA :: PartA a -> Int |
127 | | -execPartA = fromJust . getFirst . execWriter . flip execAccumT mempty |
| 140 | +type PartA = MaybeT (StateT ProgState (AccumT (Last Int) (Writer (First Int)))) |
| 141 | + |
| 142 | +execPartA :: PartA a -> ProgState -> Int |
| 143 | +execPartA p s = fromJust . getFirst . execWriter |
| 144 | + . flip execAccumT mempty |
| 145 | + . flip execStateT s |
| 146 | + . runMaybeT |
| 147 | + $ p |
128 | 148 |
|
129 | 149 | -- | Interpet Command for Part A
|
130 | | -interpretA :: Command a -> PartA a |
131 | | -interpretA = \case |
132 | | - CRcv x -> do |
| 150 | +interpA :: Command a -> PartA a |
| 151 | +interpA = \case |
| 152 | + CRcv x -> lift . lift $do |
133 | 153 | when (x /= 0) $
|
134 | 154 | tell . First . getLast =<< look
|
135 | 155 | return x
|
136 | | - CSnd x -> add (pure x) |
| 156 | + CSnd x -> lift . lift $ |
| 157 | + add (Last (Just x)) |
137 | 158 |
|
138 | 159 | day18a :: Challenge
|
139 | | -day18a = show. execPartA |
140 | | - . runPromptM interpretA |
141 | | - . execDuet (many stepTape) -- stepTape until program terminates |
142 | | - . (`PS` M.empty) .parse |
| 160 | +day18a = show |
| 161 | + . execPartA (many .runPromptM (interpMem >|< interpA) $ stepTape) |
| 162 | + . (`PS` M.empty) |
| 163 | + . parse |
143 | 164 |
|
144 | 165 | {-
|
145 | 166 | ************************
|
146 | 167 | * Context for Part B *
|
147 | 168 | ************************
|
148 | 169 | -}
|
149 | 170 |
|
150 | | --- | Context in which to interpret Command for Part B |
151 | | -type PartB s = MaybeT (State s) |
152 | | - |
153 | | --- | Interpet Command for Part B, with an [Int] writer side-channel |
154 | | -interpretB |
155 | | - :: Command a |
156 | | - -> WriterT [Int] (PartB [Int]) a |
157 | | -interpretB = \case |
158 | | - CSnd x -> tell [x] |
159 | | - CRcv _ -> get >>= \case |
160 | | - [] -> empty |
161 | | - x:xs -> put xs >> return x |
162 | | - |
163 | 171 | data Thread = T { _tState :: ProgState
|
164 | 172 | , _tBuffer :: [Int]
|
165 | 173 | }
|
166 | 174 | makeClassy ''Thread
|
167 | 175 |
|
168 | | --- | Single step through a thread. Nothing = either the thread terminates, |
169 | | --- or requires extra input. |
170 | | -stepThread :: PartB Thread [Int] |
171 | | -stepThread = do |
172 | | - machine <- execDuet stepTape <$> use tState |
173 | | - (ps, out) <- runWriterT . zoom tBuffer |
174 | | - $ runPromptM interpretB machine |
175 | | - tState .= ps |
176 | | - return out |
| 176 | +instance HasProgState Thread where |
| 177 | + progState = tState |
| 178 | + |
| 179 | +-- | Interpet Command for Part B, with an [Int] writer side-channel |
| 180 | +interpB |
| 181 | + :: (MonadFail m, MonadState s m, HasThread s) |
| 182 | + => Command a |
| 183 | + -> WriterT [Int] m a |
| 184 | +interpB = \case |
| 185 | + CSnd x -> tell [x] |
| 186 | + CRcv _ -> do |
| 187 | + x:xs <- use tBuffer |
| 188 | + tBuffer .= xs |
| 189 | + return x |
177 | 190 |
|
178 | 191 | type MultiState = V.Vector 2 Thread
|
179 | 192 |
|
180 | 193 | -- | Single step through both threads. Nothing = both threads terminate
|
181 | | -stepThreads :: PartB MultiState Int |
| 194 | +stepThreads |
| 195 | + :: MaybeT (State MultiState) Int |
182 | 196 | stepThreads = do
|
183 | | - outA <- zoom (V.ix 0) $ concat <$> many stepThread |
184 | | - outB <- zoom (V.ix 1) $ concat <$> many stepThread |
| 197 | + outA <- execWriterT $ |
| 198 | + zoom (V.ix 0) . many $ runPromptM (interpMem >|< interpB) stepTape |
| 199 | + outB <- execWriterT $ |
| 200 | + zoom (V.ix 1) . many $ runPromptM (interpMem >|< interpB) stepTape |
185 | 201 | V.ix 0 . tBuffer <>= outB
|
186 | 202 | V.ix 1 . tBuffer <>= outA
|
187 | 203 | guard . not $ null outA && null outB
|
|
0 commit comments