Add scenario json schema (#620)

- added JSON schemas for scenarios, entities and recipes
- closes #346
This commit is contained in:
Ondřej Šebek 2022-08-07 01:07:26 +02:00 committed by GitHub
parent 6fd7f4143b
commit 57be1f7c03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 500 additions and 0 deletions

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
dist-newstyle/
TAGS
.vscode/
cabal.project.local
hie.yaml

View File

@ -48,6 +48,46 @@ uses the Haskell [yaml
library](https://hackage.haskell.org/package/yaml) for parsing `.yaml`
files.
### Scenario schema
To ease writing Scenario YAML files, you can use a JSON schema that includes
the information below in a machine readable format. This will allow
your editor to highlight the errors as you are writing.
#### VS Code
If you are using Visual Studio Code or VSCodium, you need to have
the [YAML extension](https://open-vsx.org/extension/redhat/vscode-yaml)
installed.
To point the editor to the right schema for scenarios in this repository,
you can use this `setting.json`:
```JSON
{
"yaml.schemas": {
"https://raw.githubusercontent.com/swarm-game/swarm/main/scenario.json": [
"data/scenarios/*.yaml",
"data/scenarios/**/*.yaml"
]
}
}
```
#### CLI
You can also check the files from the command line:
```Bash
# install latest jsonschema executable version (tested with 4.9.1)
pip install jsonschema
# try it on provided scenarios
yq eval scenarios/creative.yaml -o json | jsonschema data/schema/scenario.json
# try that it works on empty JSON
echo {} | jsonschema data/schema/scenario.json
# {}: 'name' is a required property
# {}: 'world' is a required property
# {}: 'robots' is a required property
```
### YAML conventions
Objects (key-value mappings) are described below using tables. Note

62
data/schema/display.json Normal file
View File

@ -0,0 +1,62 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/display.json",
"title": "Swarm entity display",
"description": "How to display an entity or robot in the Swarm game",
"type": "object",
"properties": {
"char": {
"default": " ",
"type": "string",
"description": "The default character that should be used to draw the robot or entity."
},
"orientationMap": {
"default": {},
"type": "object",
"description": "Currently unused"
},
"curOrientation": {
"default": null,
"type": "array",
"description": "Currently unused"
},
"attr": {
"default": "entity",
"type": "string",
"examples": [
"entity",
"device",
"plant",
"rock",
"wood",
"flower",
"rubber",
"copper",
"copper'",
"iron",
"iron'",
"quartz",
"silver",
"gold",
"snow",
"sand",
"fire",
"red",
"green",
"blue",
"water"
],
"description": "The name of the attribute that should be used to style the robot or entity. A list of currently valid attributes can be found at https://github.com/swarm-game/swarm/blob/main/src/Swarm/TUI/Attr.hs."
},
"priority": {
"default": 1,
"type": "number",
"description": "When multiple entities and robots occupy the same cell, the one with the highest priority is drawn. By default, entities have priority 1, and robots have priority 10."
},
"invisible": {
"default": false,
"type": "boolean",
"description": "Whether the entity or robot should be invisible. Invisible entities and robots are not drawn, but can still be interacted with in otherwise normal ways."
}
}
}

104
data/schema/entities.json Normal file
View File

@ -0,0 +1,104 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/entities.json",
"title": "Swarm entities",
"description": "Description of entities in the Swarm game",
"type": "array",
"items": {
"description": "Description of an entity in the Swarm game",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the entity. This is what will show up in the inventory and how the entity can be referred to."
},
"display": {
"type": "object",
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/display.json",
"description": "Display information for the entity."
},
"plural": {
"default": "null",
"type": "string",
"description": "An explicit plural form of the name of the entity. If omitted, standard heuristics will be used for forming the English plural of its name."
},
"description": {
"type": "array",
"items": [
{
"type": "string"
}
],
"description": "A description of the entity, as a list of paragraphs."
},
"orientation": {
"default": "null",
"type": "array",
"items": [
{
"name": "X coordinate",
"type": "number"
},
{
"name": "Y coordinate",
"type": "number"
}
],
"description": "A 2-tuple of integers specifying an orientation vector for the entity. Currently unused."
},
"growth": {
"default": "null",
"type": "array",
"items": [
{
"name": "minimum",
"type": "number"
},
{
"name": "maximum",
"type": "number"
}
],
"description": "For growable entities, a 2-tuple of integers specifying the minimum and maximum amount of time taken for one growth stage. The actual time for one growth stage will be chosen uniformly at random from this range; it takes two growth stages for an entity to be fully grown."
},
"yields": {
"default": "null",
"type": "string",
"description": "The name of the entity which will be added to a robot's inventory when it executes grab or harvest on this entity. If omitted, the entity will simply yield itself."
},
"properties": {
"default": "[]",
"type": "array",
"items": [
{
"type": "string",
"examples": [
"unwalkable",
"portable",
"infinite",
"known",
"growable"
]
}
],
"description": "A list of properties of this entity. See Entity properties."
},
"capabilities": {
"default": "[]",
"type": "array",
"items": [
{
"type": "string"
}
],
"description": "A list of capabilities provided by entity, when it is installed as a device. See Capabilities."
}
},
"required": [
"name",
"display",
"description"
]
}
}

View File

@ -0,0 +1,20 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/inventory.json",
"title": "Swarm entity inventory",
"description": "A list of [count, entity name] pairs, specifying the number of each entity.",
"type": "array",
"items": {
"type": "array",
"items": [
{
"title": "Entity count",
"type": "number"
},
{
"title": "Entity name",
"type": "string"
}
]
}
}

53
data/schema/recipes.json Normal file
View File

@ -0,0 +1,53 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/recipes.json",
"title": "Swarm recipes",
"description": "How to make (or drill) entities in the Swarm game",
"type": "array",
"items": {
"title": "Swarm recipe",
"description": "Recipe describes a process that takes some inputs and produces some outputs, which robots can access using make and drill.",
"type": "object",
"examples": [
{
"in": [
[
2,
"copper wire"
]
],
"out": [
[
1,
"strange loop"
]
]
}
],
"properties": {
"in": {
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/inventory.json",
"description": "A list of ingredients consumed by the recipe. Each ingredient is a tuple consisting of an integer and an entity name, indicating the number of copies of the given entity that are needed."
},
"out": {
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/inventory.json",
"description": "A list of outputs produced by the recipe. It is a list of [count, entity name] tuples just like in."
},
"required": {
"default": [],
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/inventory.json",
"description": "A list of catalysts required by the recipe. They are neither consumed nor produced, but must be present in order for the recipe to be carried out. It is a list of [count, entity name] tuples just like in and out."
},
"time": {
"default": 1,
"type": "number",
"description": "The number of ticks the recipe takes to perform. For recipes which take more than 1 tick, the robot will wait for a number of ticks until the recipe is complete. For example, this is used for many drilling recipes."
},
"weight": {
"default": 1,
"type": "number",
"description": "Whenever there are multiple recipes that match the relevant criteria, one of them will be chosen at random, with probability proportional to their weights. For example, suppose there are two recipes that both output a widget, one with weight 1 and the other with weight 9. When a robot executes make \"widget\", the first recipe will be chosen 10% of the time, and the second recipe 90% of the time."
}
}
}
}

84
data/schema/robot.json Normal file
View File

@ -0,0 +1,84 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/robot.json",
"title": "Swarm robot",
"description": "Description of a robot in the Swarm game",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the robot. This shows up in the list of robots in the game (F2), and is also how the robot will be referred to in the world palette."
},
"description": {
"default": [],
"type": "string",
"description": "A description of the robot, given as a list of paragraphs. This is currently not used for much (perhaps not at all?)."
},
"loc": {
"default": null,
"type": "array",
"items": [
{
"name": "X coordinate",
"type": "number"
},
{
"name": "Y coordinate",
"type": "number"
}
],
"description": "An optional (x,y) starting location for the robot. If the loc field is specified, then a concrete robot will be created at the given location. If this field is omitted, then this robot record exists only as a template which can be referenced from a cell in the world palette. Concrete robots will then be created wherever the corresponding palette character is used in the world map."
},
"dir": {
"type": "array",
"items": [
{
"name": "X coordinate",
"type": "number"
},
{
"name": "Y coordinate",
"type": "number"
}
],
"description": "The starting orientation of the robot, expressed as a vector. Every time the robot executes a move command, this vector will be added to its position. Typically, this is a unit vector in one of the four cardinal directions, although there is no particular reason that it has to be."
},
"display": {
"default": "default",
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/display.json",
"description": "Display information for the robot. If this field is omitted, the default robot display will be used."
},
"program": {
"default": null,
"type": "string",
"description": "This is the text of a Swarm program which the robot should initially run, and must be syntax- and type-error-free. If omitted, the robot will simply be idle."
},
"devices": {
"default": [],
"type": "array",
"items": {
"type": "string"
},
"description": "A list of entity names which should be installed as the robot's devices, i.e. entities providing capabilities to run commands and interpret language constructs."
},
"inventory": {
"default": [],
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/inventory.json",
"description": "A list of [count, entity name] pairs, specifying the entities in the robot's starting inventory, and the number of each."
},
"system": {
"default": false,
"type": "boolean",
"description": "Whether the robot is a \"system\" robot. System robots can do anything, without regard for devices and capabilities."
},
"heavy": {
"default": false,
"type": "boolean",
"description": "Whether the robot is heavy. Heavy robots require tank treads to move (rather than just treads for other robots)."
}
},
"required": [
"name",
"dir"
]
}

87
data/schema/scenario.json Normal file
View File

@ -0,0 +1,87 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/scenario.json",
"title": "Swarm scenario",
"description": "Scenario for the swarm game",
"type": "object",
"properties": {
"name": {
"description": "The name of the scenario. For official scenarios, this is what shows up in the new game menu.",
"type": "string"
},
"description": {
"description": "A short description of the scenario. This shows up next to the new game menu when the scenario is selected.",
"type": "string"
},
"creative": {
"description": "Whether the scenario should start out in creative mode.",
"default": false,
"type": "boolean"
},
"seed": {
"description": "An optional seed that will be used to seed the random number generator. If a procedurally generated world is used, the seed hence determines the world. Hence, if the seed is specified, the procedurally generated world will be exactly the same every time, for every player. If omitted, a random seed will be used every time the scenario is loaded.",
"default": null,
"type": "number"
},
"entities": {
"description": "An optional list of custom entities, to be used in addition to the built-in entities. See description of Entities.",
"default": [],
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/entities.json"
},
"recipes": {
"description": "An optional list of custom recipes, to be used in addition to the built-in recipes. They can refer to built-in entities as well as custom entities. See description of Recipes.",
"default": [],
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/recipes.json"
},
"world": {
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/world.json"
},
"robots": {
"description": "A list of robots that will inhabit the world. See the description of Robots.",
"type": "array",
"items": {
"$ref": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/robot.json"
}
},
"objectives": {
"description": "An optional list of objectives, aka winning conditions. The player has to complete the objectives in sequence to win. See the description of Objectives.",
"default": [],
"type": "array",
"items": {
"name": "objective",
"description": "The top-level objectives field contains a list of objectives that must be completed in sequence. Each objective has a goal description and a condition.",
"type": "object",
"properties": {
"goal": {
"type": "array",
"items": [
{
"type": "string"
}
],
"description": "The goal description as a list of paragraphs that the player can read."
},
"condition": {
"description": "A swarm program that will be hypothetically run each tick to check if the condition is fulfilled.",
"type": "string"
}
}
}
},
"solution": {
"description": "The (optional) text of a Swarm program that, when run on the base robot, completes all the objectives. For scenarios which are officially part of the Swarm repository, such a solution will be tested as part of CI testing. For scenarios loaded directly from a file, any provided solution is simply ignored.",
"default": null,
"type": "string"
},
"stepsPerTick": {
"description": "When present, this specifies the maximum number of CESK machine steps each robot is allowed to take per game tick. It is rather obscure and technical and only used in a few automated tests; most scenario authors should not need this.",
"default": null,
"type": "number"
}
},
"required": [
"name",
"world",
"robots"
]
}

48
data/schema/world.json Normal file
View File

@ -0,0 +1,48 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/swarm-game/swarm/main/data/schema/world.json",
"title": "Swarm world",
"description": "Description of the world in the Swarm game",
"type": "object",
"properties": {
"default": {
"default": null,
"type": "array",
"description": "A tuple representing the contents of a default cell (see Cells, except that the default cell may not contain a robot). If this key is present, it means that the whole world besides the part specified with the map will be filled with this default cell. If omitted, the world besides the part specified with the map will be procedurally generated."
},
"offset": {
"default": false,
"type": "boolean",
"description": "Whether the base robot's position should be moved to the nearest \"good\" location, currently defined as a location near a tree, in a 16x16 patch which contains at least one each of tree, copper ore, bit (0), bit (1), rock, lambda, water, and sand. The classic scenario uses offset: True to make sure that the it is not unreasonably difficult to obtain necessary resources in the early game. See https://github.com/swarm-game/swarm/blob/main/src/Swarm/Game/WorldGen.hs#L204 ."
},
"palette": {
"default": {},
"type": "object",
"examples": [{"T": ["grass", "tree"]}],
"description": "The palette maps single character keys to tuples representing contents of cells in the world, so that a world containing entities and robots can be drawn graphically. See Cells for the contents of the tuples representing a cell."
},
"map": {
"default": "",
"type": "string",
"description": "A rectangular string, using characters from the palette, exactly specifying the contents of a rectangular portion of the world. Leading spaces are ignored. The rest of the world is either filled by the default cell, or by procedural generation otherwise. Note that this is optional; if omitted, the world will simply be filled with the default cell or procedurally generated."
},
"upperleft": {
"default": [
0,
0
],
"type": "array",
"items": [
{
"name": "X coordinate",
"type": "number"
},
{
"name": "Y coordinate",
"type": "number"
}
],
"description": "A 2-tuple of int values specifying the (x,y) coordinates of the upper left corner of the map."
}
}
}