Allow multiple robots in palette (#711)

- support specifying multiple robots in a cell
- add a test with multiple robots revealing new rooms
  - add T shaped walls
- closes #710
This commit is contained in:
Ondřej Šebek 2022-10-01 17:26:05 +02:00 committed by GitHub
parent f3c92ca03d
commit 559a3450bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 287 additions and 19 deletions

View File

@ -575,6 +575,22 @@
- A lower right corner wall. - A lower right corner wall.
properties: [unwalkable, known] properties: [unwalkable, known]
- name: down and horizontal wall
display:
attr: entity
char: '┬'
description:
- A down and horizontal wall.
properties: [unwalkable, known]
- name: up and horizontal wall
display:
attr: entity
char: '┴'
description:
- A up and horizontal wall.
properties: [unwalkable, known]
- name: horizontal wall - name: horizontal wall
display: display:
attr: entity attr: entity

View File

@ -228,8 +228,9 @@ at various locations.
#### Cells #### Cells
Each cell of the world is specified by a 1-, 2-, or 3-tuple, for Each cell of the world is specified by a list of terrain, optional entity
example, `[grass]`, `[grass, tree]`, or `[grass, null, base]`. and robots present (if any). For example, `[grass]`, `[grass, tree]`,
or `[grass, null, base]`.
- The first (required) item specifies the terrain. Currently, valid - The first (required) item specifies the terrain. Currently, valid
terrain values are `stone`, `dirt`, `grass`, `ice`, or `blank`. terrain values are `stone`, `dirt`, `grass`, `ice`, or `blank`.
@ -237,9 +238,9 @@ example, `[grass]`, `[grass, tree]`, or `[grass, null, base]`.
should be present in the cell. This may be a built-in entity, or a should be present in the cell. This may be a built-in entity, or a
custom entity specified in the `entities` section. `null` may be custom entity specified in the `entities` section. `null` may be
used to explicitly specify no entity in the cell. used to explicitly specify no entity in the cell.
- The third item (if present) specifies the name of a robot which - The third item and later (if present) specifies the names of the robots
should be present in the cell. This must be the name of a robot which should be present in the cell. These must be names of robots
specified in the `robots` section. A copy of the robot will be specified in the `robots` section. A copy of each robot will be
created at each location in the `map` where it is drawn. created at each location in the `map` where it is drawn.
Although multiple robots may be in a single location in general, Although multiple robots may be in a single location in general,

View File

@ -13,3 +13,4 @@
378-objectives.yaml 378-objectives.yaml
684-swap.yaml 684-swap.yaml
699-movement-fail 699-movement-fail
710-multi-robot.yaml

View File

@ -0,0 +1,249 @@
name: Multiple robots in palette
description: |
Using multiple robots in palette to open new rooms.
See the lowright/upright robots in the second room.
https://github.com/swarm-game/swarm/issues/710
objectives:
- goal:
- Move to the end of the room.
condition: |
r <- robotNamed "check1";
as r {has "Win"}
- goal:
- Move to the end of the second room.
condition: |
r <- robotNamed "check2";
as r {has "Win"}
- goal:
- Move to the end of the third room.
condition: |
r <- robotNamed "check3";
as r {has "Win"}
solution: |
move;move;move; move;move;move; move;move;move;
world:
default: [blank]
palette:
'.': [blank]
# FIRST ROOM
'┌': [blank, upper left corner]
'└': [blank, lower left corner]
'─': [blank, horizontal wall]
'│': [blank, vertical wall]
'┐': [blank, upper right corner, upright0]
'┘': [blank, lower right corner, lowright0]
# SECOND ROOM
'1': [blank, vertical wall, gate1]
'-': [blank, "1", horizon]
'u': [blank, "1", upright, upright1]
'l': [blank, "1", lowright, lowright1]
'2': [blank, "1", vertice, gate2]
# THIRD ROOM
'~': [blank, "2", horizon]
'/': [blank, "2", vertice]
'U': [blank, "2", upright]
'L': [blank, "2", lowright]
upperleft: [-1, 1]
map: |
┌────┐--u~~U
│....1..2../
└────┘--l~~L
robots:
- name: base
dir: [1,0]
loc: [0,0]
devices:
- treads
- logger
- name: check1
dir: [0,0]
loc: [3,0]
system: true
display:
invisible: true
program: |
def until = \c. b <- c; if b {} {until c} end;
l <- whereami;
until (
try {
loc <- as base {whereami};
return (loc == l)
} { return false }
);
create "Win"
- name: check2
dir: [0,0]
loc: [6,0]
system: true
display:
invisible: true
program: |
def until = \c. b <- c; if b {} {until c} end;
l <- whereami;
until (
try {
loc <- as base {whereami};
return (loc == l)
} { return false }
);
create "Win"
- name: check3
dir: [0,0]
loc: [9,0]
system: true
display:
invisible: true
program: |
def until = \c. b <- c; if b {} {until c} end;
l <- whereami;
until (
try {
loc <- as base {whereami};
return (loc == l)
} { return false }
);
create "Win"
- name: horizon
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,horizontal wall]]
program: |
def until = \c. b <- c; if b {} {until c} end;
n <- (s <- scan down; case s (\_. fail "Fatal error: missing room number!") return);
c1 <- robotNamed ("check" ++ n);
until (as c1 {has "Win"});
swap "horizontal wall"
- name: vertice
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,vertical wall]]
program: |
def until = \c. b <- c; if b {} {until c} end;
n <- (s <- scan down; case s (\_. fail "Fatal error: missing room number!") return);
c1 <- robotNamed ("check" ++ n);
until (as c1 {has "Win"});
swap "vertical wall"
- name: upright
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,upper right corner]]
program: |
def until = \c. b <- c; if b {} {until c} end;
n <- (s <- scan down; case s (\_. fail "Fatal error: missing room number!") return);
c1 <- robotNamed ("check" ++ n);
until (as c1 {has "Win"});
swap "upper right corner"
- name: lowright
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,lower right corner]]
program: |
def until = \c. b <- c; if b {} {until c} end;
n <- (s <- scan down; case s (\_. fail "Fatal error: missing room number!") return);
c1 <- robotNamed ("check" ++ n);
until (as c1 {has "Win"});
swap "lower right corner"
- name: upleft
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,upper left corner]]
program: |
def until = \c. b <- c; if b {} {until c} end;
n <- (s <- scan down; case s (\_. fail "Fatal error: missing room number!") return);
c1 <- robotNamed ("check" ++ n);
until (as c1 {has "Win"});
swap "upper left corner"
- name: upright0
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,down and horizontal wall]]
program: |
def until = \c. b <- c; if b {} {until c} end;
c1 <- robotNamed "check1";
until (as c1 {has "Win"});
swap "down and horizontal wall"
- name: lowright0
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,up and horizontal wall]]
program: |
def until = \c. b <- c; if b {} {until c} end;
c1 <- robotNamed "check1";
until (as c1 {has "Win"});
swap "up and horizontal wall"
- name: upright1
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,down and horizontal wall]]
program: |
def until = \c. b <- c; if b {} {until c} end;
c1 <- robotNamed "check2";
until (as c1 {has "Win"});
swap "down and horizontal wall"
- name: lowright1
dir: [0,0]
system: true
display:
invisible: true
inventory: [[1,up and horizontal wall]]
program: |
def until = \c. b <- c; if b {} {until c} end;
c1 <- robotNamed "check2";
until (as c1 {has "Win"});
swap "up and horizontal wall"
- name: gate1
dir: [0,0]
system: true
display:
invisible: true
program: |
def until = \c. b <- c; if b {} {until c} end;
c1 <- robotNamed "check1";
until (as c1 {has "Win"});
grab
- name: gate2
dir: [0,0]
system: true
display:
invisible: true
program: |
def until = \c. b <- c; if b {} {until c} end;
c1 <- robotNamed "check2";
until (as c1 {has "Win"});
grab
entities:
- name: Win
display:
char: W
attr: gold
description:
- This entity signals that the objective has been met.
- name: "1"
display:
char: W
invisible: true
description:
- This entity is used to mean that the robot should check the 1st objective.
- name: "2"
display:
char: W
invisible: true
description:
- This entity is used to mean that the robot should check the 2nd objective.

View File

@ -54,6 +54,7 @@ import Control.Carrier.Lift (Lift, sendIO)
import Control.Carrier.Throw.Either (Throw, throwError) import Control.Carrier.Throw.Either (Throw, throwError)
import Control.Lens hiding (from, (<.>)) import Control.Lens hiding (from, (<.>))
import Control.Monad (filterM, when) import Control.Monad (filterM, when)
import Control.Monad.Extra (mapMaybeM)
import Data.Aeson.Key qualified as Key import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap (KeyMap) import Data.Aeson.KeyMap (KeyMap)
import Data.Aeson.KeyMap qualified as KeyMap import Data.Aeson.KeyMap qualified as KeyMap
@ -153,7 +154,7 @@ getRobot = getThing "robot" M.lookup
data Cell = Cell data Cell = Cell
{ cellTerrain :: TerrainType { cellTerrain :: TerrainType
, cellEntity :: Maybe Entity , cellEntity :: Maybe Entity
, cellRobot :: Maybe TRobot , cellRobots :: [TRobot]
} }
deriving (Eq, Show) deriving (Eq, Show)
@ -164,7 +165,7 @@ data Cell = Cell
instance FromJSONE (EntityMap, RobotMap) Cell where instance FromJSONE (EntityMap, RobotMap) Cell where
parseJSONE = withArrayE "tuple" $ \v -> do parseJSONE = withArrayE "tuple" $ \v -> do
let tup = V.toList v let tup = V.toList v
when (null tup || length tup > 3) $ fail "palette entry must have length 1, 2, or 3" when (null tup) $ fail "palette entry must nonzero length (terrain, optional entity and then robots if any)"
terr <- liftE $ parseJSON (head tup) terr <- liftE $ parseJSON (head tup)
@ -174,13 +175,13 @@ instance FromJSONE (EntityMap, RobotMap) Cell where
meName <- liftE $ parseJSON @(Maybe Text) e meName <- liftE $ parseJSON @(Maybe Text) e
traverse (localE fst . getEntity) meName traverse (localE fst . getEntity) meName
rob <- case tup ^? ix 2 of let name2rob r = do
Nothing -> return Nothing mrName <- liftE $ parseJSON @(Maybe Text) r
Just r -> do traverse (localE snd . getRobot) mrName
mrName <- liftE $ parseJSON @(Maybe Text) r
traverse (localE snd . getRobot) mrName
return $ Cell terr ent rob robs <- mapMaybeM name2rob (drop 2 tup)
return $ Cell terr ent robs
------------------------------------------------------------ ------------------------------------------------------------
-- World description -- World description

View File

@ -808,7 +808,7 @@ scenarioToGameState scenario userSeed toRun g = do
-- | Take a world description, parsed from a scenario file, and turn -- | Take a world description, parsed from a scenario file, and turn
-- it into a list of located robots and a world function. -- it into a list of located robots and a world function.
buildWorld :: EntityMap -> WorldDescription -> ([TRobot], Seed -> WorldFun Int Entity) buildWorld :: EntityMap -> WorldDescription -> ([TRobot], Seed -> WorldFun Int Entity)
buildWorld em (WorldDescription {..}) = (robots, first fromEnum . wf) buildWorld em WorldDescription {..} = (robots, first fromEnum . wf)
where where
rs = fromIntegral $ length area rs = fromIntegral $ length area
cs = fromIntegral $ length (head area) cs = fromIntegral $ length (head area)
@ -831,11 +831,10 @@ buildWorld em (WorldDescription {..}) = (robots, first fromEnum . wf)
area area
& traversed Control.Lens.<.> traversed %@~ (,) -- add (r,c) indices & traversed Control.Lens.<.> traversed %@~ (,) -- add (r,c) indices
& concat & concat
& mapMaybe & concatMap
( \((fromIntegral -> r, fromIntegral -> c), Cell _ _ robot) -> ( \((fromIntegral -> r, fromIntegral -> c), Cell _ _ robotList) ->
robot let robotWithLoc = trobotLocation ?~ W.coordsToLoc (Coords (ulr + r, ulc + c))
& traverse . trobotLocation in map robotWithLoc robotList
?~ W.coordsToLoc (Coords (ulr + r, ulc + c))
) )
-- | Create an initial game state for a specific scenario. -- | Create an initial game state for a specific scenario.

View File

@ -206,6 +206,7 @@ testScenarioSolution _ci _em =
, testSolution Default "Testing/699-movement-fail/699-move-blocked" , testSolution Default "Testing/699-movement-fail/699-move-blocked"
, testSolution Default "Testing/699-movement-fail/699-move-liquid" , testSolution Default "Testing/699-movement-fail/699-move-liquid"
, testSolution Default "Testing/699-movement-fail/699-teleport-blocked" , testSolution Default "Testing/699-movement-fail/699-teleport-blocked"
, testSolution Default "Testing/710-multi-robot"
] ]
] ]
where where