Skip to content

Commit

Permalink
Implement boundary rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
kostmo committed Sep 18, 2024
1 parent 671fd0f commit 4646cb0
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 5 deletions.
1 change: 1 addition & 0 deletions data/scenarios/Testing/00-ORDER.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Achievements
1218-stride-command.yaml
1234-push-command.yaml
1256-halt-command.yaml
1271-wall-boundaries.yaml
1262-display-device-commands.yaml
1295-density-command.yaml
1138-structures
Expand Down
48 changes: 48 additions & 0 deletions data/scenarios/Testing/1271-wall-boundaries.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
version: 1
name: Wall boundaries
creative: false
description: Stop a robot using halt
objectives:
- goal:
- Just be.
condition: |
return false;
solution: |
noop;
robots:
- name: base
dir: [0,-1]
display:
char: Ω
attr: robot
devices:
- compass
- dictionary
- grabber
- toolkit
- logger
- tank treads
- antenna
- ADT calculator
entities:
- name: wall
display:
char: 'x'
description:
- A wall
properties: [known, boundary]
world:
default: [blank]
palette:
'Ω': [grass, null, base]
'.': [grass]
'#': [grass, wall]
upperleft: [0, 0]
map: |
Ω.......
....#...
..####..
..#.##..
..#..#..
..####..
........
67 changes: 64 additions & 3 deletions src/swarm-scenario/Swarm/Game/Display.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module Swarm.Game.Display (
defaultChar,
orientationMap,
curOrientation,
boundaryOverride,
displayAttr,
displayPriority,
invisible,
Expand All @@ -30,13 +31,19 @@ module Swarm.Game.Display (
-- ** Rendering
displayChar,
hidden,
Neighbors,
Presence (..),
emptyNeighbors,
getBoundaryDisplay,
setDirection,

-- ** Construction
defaultTerrainDisplay,
defaultEntityDisplay,
defaultRobotDisplay,
) where

import Control.Applicative ((<|>))
import Control.Lens hiding (Const, from, (.=))
import Control.Monad (when)
import Data.Hashable (Hashable)
Expand Down Expand Up @@ -84,11 +91,56 @@ data ChildInheritance
| DefaultDisplay
deriving (Eq, Ord, Show, Generic, Hashable)

data Presence
= -- | present
X
| -- | absent
O

emptyNeighbors :: Neighbors Presence
emptyNeighbors = Neighbors O O O O

data Neighbors a = Neighbors
{ e :: a
, w :: a
, n :: a
, s :: a
}

setDirection :: AbsoluteDir -> a -> Neighbors a -> Neighbors a
setDirection DNorth x y = y {n = x}
setDirection DSouth x y = y {s = x}
setDirection DEast x y = y {e = x}
setDirection DWest x y = y {w = x}

-- | For a center cell that itself is a boundary,
-- determine a glyph override for rendering, given certain
-- neighbor combinations.
getBoundaryDisplay :: Neighbors Presence -> Maybe Char
getBoundaryDisplay = \case
Neighbors {e = O, w = O, n = O, s = O} -> Nothing
Neighbors {e = X, w = X, n = O, s = O} -> Just ''
Neighbors {e = X, w = O, n = O, s = O} -> Just ''
Neighbors {e = O, w = X, n = O, s = O} -> Just ''
Neighbors {e = O, w = O, n = X, s = X} -> Just ''
Neighbors {e = O, w = O, n = O, s = X} -> Just ''
Neighbors {e = O, w = O, n = X, s = O} -> Just ''
Neighbors {e = X, w = X, n = X, s = X} -> Just ''
Neighbors {e = O, w = X, n = O, s = X} -> Just ''
Neighbors {e = X, w = O, n = O, s = X} -> Just ''
Neighbors {e = O, w = X, n = X, s = O} -> Just ''
Neighbors {e = X, w = O, n = X, s = O} -> Just ''
Neighbors {e = O, w = X, n = X, s = X} -> Just ''
Neighbors {e = X, w = O, n = X, s = X} -> Just ''
Neighbors {e = X, w = X, n = X, s = O} -> Just ''
Neighbors {e = X, w = X, n = O, s = X} -> Just ''

-- | A record explaining how to display an entity in the TUI.
data Display = Display
{ _defaultChar :: Char
, _orientationMap :: Map AbsoluteDir Char
, _curOrientation :: Maybe Direction
, _boundaryOverride :: Maybe Char
, _displayAttr :: Attribute
, _displayPriority :: Priority
, _invisible :: Bool
Expand Down Expand Up @@ -117,6 +169,9 @@ orientationMap :: Lens' Display (Map AbsoluteDir Char)
-- know which character to use from the orientation map.
curOrientation :: Lens' Display (Maybe Direction)

-- | The display character to substitute when neighbor boundaries are present
boundaryOverride :: Lens' Display (Maybe Char)

-- | The attribute to use for display.
displayAttr :: Lens' Display Attribute

Expand Down Expand Up @@ -146,11 +201,13 @@ instance FromJSONE Display Display where

liftE $ do
let _defaultChar = c
_boundaryOverride = Nothing
_orientationMap <- v .:? "orientationMap" .!= dOM
_curOrientation <- v .:? "curOrientation" .!= (defD ^. curOrientation)
_displayAttr <- (v .:? "attr") .!= (defD ^. displayAttr)
_displayPriority <- v .:? "priority" .!= (defD ^. displayPriority)
_invisible <- v .:? "invisible" .!= (defD ^. invisible)

let _childInheritance = Inherit
pure Display {..}
where
Expand Down Expand Up @@ -179,9 +236,11 @@ instance ToJSON Display where

-- | Look up the character that should be used for a display.
displayChar :: Display -> Char
displayChar disp = fromMaybe (disp ^. defaultChar) $ do
DAbsolute d <- disp ^. curOrientation
M.lookup d (disp ^. orientationMap)
displayChar disp =
fromMaybe (disp ^. defaultChar) $
disp ^. boundaryOverride <|> do
DAbsolute d <- disp ^. curOrientation
M.lookup d (disp ^. orientationMap)

-- | Modify a display to use a @?@ character for entities that are
-- hidden/unknown.
Expand All @@ -204,6 +263,7 @@ defaultEntityDisplay c =
{ _defaultChar = c
, _orientationMap = M.empty
, _curOrientation = Nothing
, _boundaryOverride = Nothing
, _displayAttr = AEntity
, _displayPriority = 1
, _invisible = False
Expand All @@ -227,6 +287,7 @@ defaultRobotDisplay =
, (DSouth, 'v')
, (DNorth, '^')
]
, _boundaryOverride = Nothing
, _curOrientation = Nothing
, _displayAttr = ARobot
, _displayPriority = 10
Expand Down
2 changes: 2 additions & 0 deletions src/swarm-scenario/Swarm/Game/Entity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ data EntityProperty
Pushable
| -- | Obstructs the view of robots that attempt to "scout"
Opaque
| -- | Is automatically rendered as a contiguous border
Boundary
| -- | Regrows from a seed after it is harvested.
Growable
| -- | Can burn when ignited (either via 'Swarm.Language.Syntax.Ignite' or by
Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/View.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,7 @@ displayProperties = displayList . mapMaybe showProperty
showProperty Liquid = Just "liquid"
showProperty Unwalkable = Just "blocking"
showProperty Opaque = Just "opaque"
showProperty Boundary = Just "boundary"
-- Most things are pickable so we don't show that.
showProperty Pickable = Nothing
-- 'Known' is just a technical detail of how we handle some entities
Expand Down
28 changes: 26 additions & 2 deletions src/swarm-tui/Swarm/TUI/View/CellDisplay.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Brick
import Control.Lens (to, view, (&), (.~), (^.))
import Data.ByteString (ByteString)
import Data.Hash.Murmur
import Data.List.Extra (enumerate)
import Data.List.NonEmpty qualified as NE
import Data.Map qualified as M
import Data.Maybe (maybeToList)
Expand All @@ -23,14 +24,21 @@ import Linear.Affine ((.-.))
import Swarm.Game.Display (
Attribute (AEntity),
Display,
Neighbors,
Presence (..),
boundaryOverride,
defaultEntityDisplay,
displayAttr,
displayChar,
displayPriority,
emptyNeighbors,
getBoundaryDisplay,
hidden,
setDirection,
)
import Swarm.Game.Entity
import Swarm.Game.Land
import Swarm.Game.Location (Point (..), toHeading)
import Swarm.Game.Robot
import Swarm.Game.Scenario.Topography.EntityFacade
import Swarm.Game.Scenario.Topography.Structure.Recognition (foundStructures, recognitionState)
Expand All @@ -44,13 +52,15 @@ import Swarm.Game.Tick (TickNumber (..))
import Swarm.Game.Universe
import Swarm.Game.World qualified as W
import Swarm.Game.World.Coords
import Swarm.Language.Syntax.Direction (AbsoluteDir (..))
import Swarm.TUI.Editor.Masking
import Swarm.TUI.Editor.Model
import Swarm.TUI.Editor.Util qualified as EU
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.UI.Gameplay
import Swarm.TUI.View.Attribute.Attr
import Swarm.Util (applyWhen)
import Swarm.Util.Content (getContentAt)
import Witch (from)
import Witch.Encoding qualified as Encoding

Expand Down Expand Up @@ -140,9 +150,23 @@ displayEntityCell ::
Cosmic Coords ->
[Display]
displayEntityCell worldEditor ri coords =
maybeToList $ displayForEntity <$> maybeEntity
maybeToList $ (& (boundaryOverride .~ getBoundaryDisplay presences)) . displayForEntity <$> maybeEntityPaint

Check warning on line 153 in src/swarm-tui/Swarm/TUI/View/CellDisplay.hs

View workflow job for this annotation

GitHub Actions / HLint

Warning in displayEntityCell in module Swarm.TUI.View.CellDisplay: Redundant & ▫︎ Found: "(& (boundaryOverride .~ getBoundaryDisplay presences))" ▫︎ Perhaps: "(boundaryOverride .~ getBoundaryDisplay presences)"
where
(_, maybeEntity) = EU.getEditorContentAt (terrMap ri) worldEditor (multiworldInfo ri) coords
maybeEntityPaint = getEntPaintAtCoord coords

getEntPaintAtCoord = snd . EU.getEditorContentAt (terrMap ri) worldEditor (multiworldInfo ri)
getEntAtCoord = snd . getContentAt (terrMap ri) (multiworldInfo ri)

presences = foldr assignPresence emptyNeighbors enumerate

checkPresence :: AbsoluteDir -> Bool
checkPresence d = maybe False (`hasProperty` Boundary) $ getEntAtCoord offsettedCoord
where
offsettedCoord = fmap (`addTuple` xy) coords
Coords xy = locToCoords $ P $ toHeading d

assignPresence :: AbsoluteDir -> Neighbors Presence -> Neighbors Presence
assignPresence d = applyWhen (checkPresence d) $ setDirection d X

displayForEntity :: EntityPaint -> Display
displayForEntity e = (if isKnownFunc ri e then id else hidden) $ getDisplay e
Expand Down

0 comments on commit 4646cb0

Please sign in to comment.