2019-12-13 11:34:11 +01:00
module Intcode
( TuringMachine ( TM , tape , pointer , pointerOffset , output , input ,
state )
, step
2019-12-13 19:19:55 +01:00
, defaultTM
2019-12-13 11:34:11 +01:00
, TMOutState ( Continue , AwaitInput , Halt )
, execSteps
2019-12-13 19:56:08 +01:00
, readIntcode
2019-12-13 11:34:11 +01:00
) where
import Control.DeepSeq as DeepSeq
import Data.Char
import Data.List as L
2019-12-13 19:56:08 +01:00
import Data.List.Split
2019-12-13 11:34:11 +01:00
import Data.Maybe
import Data.Vector as V hiding
( ( ++ )
, concatMap
, elem
, head
, last
, length
, map
, reverse
, splitAt
)
import qualified Data.Vector as V ( ( ++ ) , last )
data TMOutState
= Continue
| AwaitInput
| Halt
deriving ( Enum , Eq , Show )
data Mode
= Position
| Immediate
| Relative
deriving ( Enum , Eq , Show )
data TuringMachine =
TM
{ tape :: Vector Integer
, pointer :: Integer
, pointerOffset :: Integer
, output :: [ Integer ]
, input :: Maybe Integer
, state :: TMOutState
}
deriving ( Show )
2019-12-13 19:56:08 +01:00
readIntcode :: String -> V . Vector Integer
readIntcode = V . fromList . concatMap ( map read . splitOn " , " ) . lines
2019-12-13 19:19:55 +01:00
defaultTM :: Maybe ( Int , Integer ) -> Vector Integer -> TuringMachine
defaultTM replacement t =
TM
{ tape = maybe t ( tapePreprocess t ) replacement
, pointer = 0
, pointerOffset = 0
, output = []
, input = Nothing
, state = Continue
}
tapePreprocess :: Vector Integer -> ( Int , Integer ) -> Vector Integer
tapePreprocess t ( target , repl ) =
flip ( // ) [ ( target , repl ) ] $ ( V .++ ) t $ V . replicate 500 0
2019-12-13 11:34:11 +01:00
opLength :: Integer -> Integer
opLength x
| n ` elem ` " 1278 " = 4
| n ` elem ` " 56 " = 3
| n ` elem ` " 349 " = 2
| otherwise = 1
where
n = last $ show x
parseModes :: String -> [ Mode ]
parseModes m = L . replicate ( 3 - length l ) Position ++ l
where
l = map ( toEnum . digitToInt ) m
paramChange ::
[ Mode ] -> Integer -> Vector Integer -> Vector Integer -> Vector Integer
paramChange m rbase opvec t = imap f ( V . tail opvec )
where
f i a =
case m !! i of
Immediate -> a
Position -> t ! fromInteger a
Relative -> t ! fromInteger ( a + rbase )
getOpModes :: Vector Integer -> ( String , [ Mode ] )
getOpModes opvec = ( op_dedup , parsed_modes )
where
( op , modes ) = splitAt 2 $ reverse $ show $ opvec ! 0
parsed_modes = reverse $ parseModes $ reverse modes
op_dedup =
if last op == '0'
then [ head op ]
else op
step :: TuringMachine -> TuringMachine
step tm =
case op of
" 1 " -> tmBinop ( + )
" 2 " -> tmBinop ( * )
2019-12-13 19:19:55 +01:00
" 3 " ->
if isJust ( input tm )
then ( getNewTM ( fromJust $ input tm ) ) { input = Nothing }
else tm { state = AwaitInput }
2019-12-13 11:34:11 +01:00
" 4 " -> tmn { output = V . last params : output tm }
" 5 " ->
tm
{ pointer =
if params ! 0 /= 0
then params ! 1
else pNew
}
" 6 " ->
tm
{ pointer =
if params ! 0 == 0
then params ! 1
else pNew
}
" 7 " ->
tmBinop
( \ x y ->
if x < y
then 1
else 0 )
" 8 " ->
tmBinop
( \ x y ->
if x == y
then 1
else 0 )
" 9 " -> tmn { pointerOffset = pointerOffset tm + ( params ! 0 ) }
" 99 " -> tm { state = Halt }
_ -> error " Illegal Opcode. "
where
pNew = pointer tm + opLength ( tape tm ! fromInteger ( pointer tm ) )
tmn = tm { pointer = pNew }
opvec =
slice
( fromInteger ( pointer tm ) )
( fromInteger ( pNew - pointer tm ) )
( tape tm )
( op , m ) = getOpModes opvec
params = paramChange m ( pointerOffset tm ) opvec ( tape tm )
tmBinop x = getNewTM ( ( params ! 0 ) ` x ` ( params ! 1 ) )
{- without the following DeepSeq call, thunks build up eternally and
the vectors won ’ t be garbage collected : > 4 GB RAM usage , god knows
how much with larger tapes ( my laptop crashed ) , now it ’ s a cozy ~ 20 mB - }
getNewTM x =
tmn { tape = DeepSeq . force ( tape tm // [ ( target , x ) ] ) , state = Continue }
target =
fromInteger $
case m !! ( length params - 1 ) of
Relative -> V . last opvec + pointerOffset tm
_ -> V . last opvec
execSteps :: TuringMachine -> TuringMachine
execSteps tm =
case state tmNew of
Continue -> execSteps tmNew
_ -> tmNew
where
tmNew = step tm