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.
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
display:
attr: entity

View File

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

View File

@ -13,3 +13,4 @@
378-objectives.yaml
684-swap.yaml
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.Lens hiding (from, (<.>))
import Control.Monad (filterM, when)
import Control.Monad.Extra (mapMaybeM)
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap (KeyMap)
import Data.Aeson.KeyMap qualified as KeyMap
@ -153,7 +154,7 @@ getRobot = getThing "robot" M.lookup
data Cell = Cell
{ cellTerrain :: TerrainType
, cellEntity :: Maybe Entity
, cellRobot :: Maybe TRobot
, cellRobots :: [TRobot]
}
deriving (Eq, Show)
@ -164,7 +165,7 @@ data Cell = Cell
instance FromJSONE (EntityMap, RobotMap) Cell where
parseJSONE = withArrayE "tuple" $ \v -> do
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)
@ -174,13 +175,13 @@ instance FromJSONE (EntityMap, RobotMap) Cell where
meName <- liftE $ parseJSON @(Maybe Text) e
traverse (localE fst . getEntity) meName
rob <- case tup ^? ix 2 of
Nothing -> return Nothing
Just r -> do
mrName <- liftE $ parseJSON @(Maybe Text) r
traverse (localE snd . getRobot) mrName
let name2rob r = do
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

View File

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