Resources: Levels, Graphics, and Audio¶
Apart from code, Reduct-Redux has to load quite a few assets, many of which are bundled for efficiency. They also aren’t handled by the build system, so you’ll have to manage them manually.
Level Definition Format¶
Levels are simply JSON objects, defined as follows:
{
"board": [
// List of strings of code
"(x => x)(1)"
],
"goal": [
// List of strings of code
"1"
],
"textgoal": "This is the textual goal (optional)",
"globals": {
// Map of names to strings of code
"twice": "function twice(f) { return (x) => f(f(x)); }"
},
"toolbox": [
// List of strings of code
],
// Currently disabled
// List of syntax JSON files
"syntax": [],
"animationScales": {
// Map of animation scale names to scale values
"expr-apply": 1.5
},
"fade": {
// Map of expression types to fade indices
"reference": 1
}
}
The only required fields are board
, goal
, and
toolbox
. (Any of these can be an empty list, though.) Notes on
special fields:
animationScales
- Animations can be sped up or slowed down in particular levels. These
settings persist in future levels until overridden. The engine
generates a scale for each expression type, e.g. if an expression
conditional
is defined, then there will be a scale calledexpr-conditional
. There is also a scale calledmulti-step
that controls the duration of the pause between steps in a reduction. fade
- Expressions can have different variants at different points in the game. This is an application of concreteness fading, the focus of the first version of Reduct. This property allows you to control which variant of the specified expression is used, and is also persistent in future levels. See Concreteness Fading.
syntax
- There is a (commented-out) feature called the syntax journal, which
gives a reference manual of constructs that the player has learned
so far. This field adds a new page to the manual. Values should be
the filename (without extension) of a syntax JSON file (look at
levels-progression/syntax-add.json
for an example). textgoal
In addition to the goal, you can specify some text to serve as a hint or description. The text will automatically wrap.
A templating system allows you to refer to primitives (like circles/hamburgers) without having to hardcode their names. For instance,
{star}
and{a star}
will change to “burger” and “a burger” if the junk food theme is chosen.For more on customizing this, see Miscellaneous Hooks.
showConcreteGoal
- If
textgoal
is specified, set this tofalse
to prevent the regular goal from showing. Defaults totrue
.
Levels are arranged into chapters, which are JSON files:
{
"chapterName": "Application",
"description": "Introduction to function application",
// Unused, I think?
"language": "JavaScript",
// For the summer 2018 study
"password": "aardvark",
"levels": [
// …list of level objects…
],
"resources": {
"aliens": [
// List of alien sprites to use
"alien-bag-1",
"alien-function-1",
"alien-bag-2",
"alien-function-2",
"alien-bag-3",
"alien-function-3"
]
}
}
Chapters are organized within the code (src/game/progression.js
):
export const PROGRESSIONS = {
"Elementary": {
dir: "levels-progression/",
digraph: {
"functions": ["replication"],
"replication": ["multiargument"],
"multiargument": ["functions-challenge"],
"functions-challenge": ["application"],
"application": ["definition"],
"definition": ["testing"],
"testing": ["higher-order-functions"],
"higher-order-functions": ["define-challenges"],
"define-challenges": ["booleans-intro"],
"booleans-intro": ["booleans-definition"],
"booleans-definition": ["weekdays"],
"weekdays": ["recursion-basics"],
"recursion-basics": ["recursion-higher-order"],
"recursion-higher-order": [],
},
},
};
Technically, this specifies a directed graph of chapter dependencies, where each key in the map specifies a list of chapters that depend on it.
Sprites & Audio¶
To make loading faster, Reduct-Redux doesn’t load individual sprites
or audio files. Instead, it expects them to have been combined into
spritesheets or audio sprites, and loads them all at once. (You can
see this at the start of src/index.js
.) However, these have to be
generated from the original sprites. There are some Bash scripts to
somewhat automate this process, detailed below.
Assets are generated from a separate repository. They should not be committed to this repository. I’ve created a script that will generate all the necessary assets. First, you will need ImageMagick installed. (MacOS users should be able to find this in Homebrew.) Then, install the other dependencies:
# First, cd into the reduct-assets repository.
npm install
Now you can build the assets:
./build.sh
# Now, cd into the reduct-redux repository.
cp ../reduct-assets/output/output.* resources/audio/
cp ../reduct-assets/output/*assets.* resources/graphics/
The result will be in the output
directory and should be copied to
the resources
directory in Reduct.
chapterutil¶
This is a tool that can convert levels between the JSON representation and a CSV representation, allowing people to collaboratively edit the progression. It is not round-trippable, as it is fairly loose in the kinds of CSV files it accepts, but also does not preserve all columns in the CSV when converting to JSON. The best approach is to export levels from JSON to CSV once, then only ever edit the levels in CSV format.
Requirements:
- Python 3.6
- virtualenv
- Bash
Setup:
cd chapterutil/
virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
If you download the file from Google Docs as XSLX, this script will automatically import all sheets in the XSLX:
source venv/bin/activate
./automate.sh path/to/progression.xslx