swarm-0.4: src/Swarm/Game/World/Interpret.hs
{-# LANGUAGE GADTs #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Interpreter for the Swarm world description DSL.
module Swarm.Game.World.Interpret (
interpBTerm,
interpConst,
interpReflect,
interpRot,
) where
import Control.Applicative (Applicative (..))
import Data.ByteString (ByteString)
import Data.Hash.Murmur (murmur3)
import Data.Tagged (unTagged)
import Numeric.Noise.Perlin (noiseValue, perlin)
import Swarm.Game.World.Abstract (BTerm (..))
import Swarm.Game.World.Coords (Coords (..))
import Swarm.Game.World.Gen (Seed)
import Swarm.Game.World.Syntax (Axis (..), Rot (..))
import Swarm.Game.World.Typecheck (Const (..), Empty (..), Over (..))
import Witch (from)
import Witch.Encoding qualified as Encoding
import Prelude hiding (Applicative (..))
-- | Interpret an abstracted term into the host language.
interpBTerm :: Seed -> BTerm a -> a
interpBTerm seed (BApp f x) = interpBTerm seed f (interpBTerm seed x)
interpBTerm seed (BConst c) = interpConst seed c
-- | Interpret a constant into the host language.
interpConst :: Seed -> Const a -> a
interpConst seed = \case
CLit a -> a
CCell c -> c
CIf -> \b t e -> if b then t else e
CNot -> not
CNeg -> negate
CAbs -> abs
CAnd -> (&&)
COr -> (||)
CAdd -> (+)
CSub -> (-)
CMul -> (*)
CDiv -> (/)
CIDiv -> div
CMod -> mod
CEq -> (==)
CNeq -> (/=)
CLt -> (<)
CLeq -> (<=)
CGt -> (>)
CGeq -> (>=)
CMask -> \b x c -> if b c then x c else empty
CSeed -> fromIntegral seed
CCoord ax -> \(Coords (x, y)) -> fromIntegral (case ax of X -> x; Y -> y)
CHash -> \(Coords ix) -> fromIntegral . murmur3 0 . unTagged . from @String @(Encoding.UTF_8 ByteString) . show $ ix
CPerlin -> \s o k p ->
let noise = perlin (fromIntegral s) (fromIntegral o) k p
sample (i, j) = noiseValue noise (fromIntegral i / 2, fromIntegral j / 2, 0)
in \(Coords ix) -> sample ix
CReflect ax -> \w -> w . interpReflect ax
CRot r -> \w -> w . interpRot r
CFI -> fromInteger
COver -> (<!>)
K -> const
S -> (<*>)
I -> id
B -> (.)
C -> flip
Φ -> liftA2
-- | Interprect a reflection.
interpReflect :: Axis -> Coords -> Coords
interpReflect ax (Coords (r, c)) = Coords (case ax of X -> (r, -c); Y -> (-r, c))
-- | Interpret a rotation.
interpRot :: Rot -> Coords -> Coords
interpRot rot (Coords crd) = Coords (rotTuple rot crd)
where
rotTuple = \case
Rot0 -> id
Rot90 -> \(r, c) -> (-c, r)
Rot180 -> \(r, c) -> (-r, -c)
Rot270 -> \(r, c) -> (c, -r)