intcode-0.3.0.0: src/Intcode/Opcode.hs
{-# Language DeriveTraversable, Safe #-}
{-|
Module : Intcode.Opcode
Description : Intcode opcodes
Copyright : (c) Eric Mertens, 2019
License : ISC
Maintainer : emertens@gmail.com
This module provides a representation of the intcode machine's opcodes.
Opcodes are parameterized over their parameters. This allows the
implementation to store both parameter modes and resolved parameter
pointers in the same constructors.
-}
module Intcode.Opcode
(
-- * Types
Opcode(..), Mode(..),
-- * Decoder
decode,
) where
------------------------------------------------------------------------
-- Opcode decoder
------------------------------------------------------------------------
-- | Parameter modes
data Mode
= Abs -- ^ absolute position
| Imm -- ^ immediate
| Rel -- ^ relative position
deriving (Eq, Ord, Read, Show)
-- | Opcodes parameterized over argument representations.
data Opcode a
= Add !a !a !a -- ^ __addition:__ @c = a + b@
| Mul !a !a !a -- ^ __multiplication:__ @c = a * b@
| Inp !a -- ^ __input:__ @a = input()@
| Out !a -- ^ __output:__ @output(a)@
| Jnz !a !a -- ^ __jump-if-true:__ @if a then goto b@
| Jz !a !a -- ^ __jump-if-false:__ @if !a then goto b@
| Lt !a !a !a -- ^ __less-than:__ @c = a < b@
| Eq !a !a !a -- ^ __equals:__ @c = a == b@
| Arb !a -- ^ __adjust-rel-base:__ @rel += a@
| Hlt -- ^ __halt__
deriving (Eq, Ord, Read, Show, Functor, Foldable)
-- | Decode an instruction to determine the opcode and parameter modes.
--
-- >>> decode 1002
-- Just (Mul Abs Imm Abs)
decode :: Int {- ^ opcode -} -> Maybe (Opcode Mode)
decode n =
case n `rem` 100 of
1 -> fill (Add 1 2 3)
2 -> fill (Mul 1 2 3)
3 -> fill (Inp 1 )
4 -> fill (Out 1 )
5 -> fill (Jnz 1 2 )
6 -> fill (Jz 1 2 )
7 -> fill (Lt 1 2 3)
8 -> fill (Eq 1 2 3)
9 -> fill (Arb 1 )
99 -> fill Hlt
_ -> Nothing
where
fill = traverse (parameter n)
{-# INLINABLE decode #-}
-- | Compute the parameter mode for an argument at a given position.
parameter ::
Int {- ^ opcode -} ->
Int {- ^ position -} ->
Maybe Mode
parameter n i =
case digit (i+1) n of
0 -> Just Abs
1 -> Just Imm
2 -> Just Rel
_ -> Nothing
-- | Arguments visited from left to right.
instance Traversable Opcode where
{-# INLINE traverse #-}
traverse f o =
case o of
Add x y z -> Add <$> f x <*> f y <*> f z
Mul x y z -> Mul <$> f x <*> f y <*> f z
Inp x -> Inp <$> f x
Out x -> Out <$> f x
Jnz x y -> Jnz <$> f x <*> f y
Jz x y -> Jz <$> f x <*> f y
Lt x y z -> Lt <$> f x <*> f y <*> f z
Eq x y z -> Eq <$> f x <*> f y <*> f z
Arb x -> Arb <$> f x
Hlt -> pure Hlt
-- | Extract the ith digit from a number.
--
-- >>> digit 0 2468
-- 8
-- >>> digit 3 2468
-- 2
-- >>> digit 4 2468
-- 0
digit :: Int {- ^ position -} -> Int {- ^ number -} -> Int {- ^ digit -}
digit i x = x `quot` (10^i) `rem` 10