-
-
Notifications
You must be signed in to change notification settings - Fork 12
[SpaceTode] Documentation
SpaceTode is a language that helps you make elements for the SandPond engine.
It is heavily inspired by SPLAT and shares many principles.
The most important part of SpaceTode is the element
expression. It defines a new element:
element Sand
In the SandPond engine, there are two in-built elements:
-
Empty
(all spaces are empty by default) -
Void
(the element of any space outside of the universe)
You can set your element's properties.
element Sand {
colour "yellow"
}
These are all the in-built properties:
colour
-
emissive
(emissive colour of the element) opacity
-
visible
(if false, the element will not render in the world) -
hidden
(if true, the element will not appear in the menu) -
category
(determines which menu category to appear in) -
pour
(if true, holding down the mouse button will continue to pour more atoms, default is true) -
default
(if true, this element will be picked by the dropper when you load the page)
You can set your own properties with the prop
keyword:
element Water {
prop state "liquid"
}
Atoms of the element can carry data.
Use the data
keyword to shows what data the atom holds, and its default value.
element Sand {
colour "yellow"
data isWet false
data temperature 15
}
You can set the arguments you expect when constructing an atom (and their default values).
element Explosion {
arg size 10
arg colour "red"
}
When you make an Explosion atom somewhere in your code, you could use those arguments:
new Explosion(20, "yellow")
Note: You would still need to implement what those arguments do in your element's code.
You can give your element rules by drawing rule diagrams:
element Sand {
colour "yellow"
@ => _
_ @
}
An element's rules show it how to act in the SandPond world.
It checks its surroundings to see if it matches the left-hand-side of the rule (the inputs).
If it matches, it changes it to look like the right-hand-side of the rule (the outputs).
Rules are checked in order. The first rule to match is the one that gets used.
You can optionally use the rule
keyword, which may improve readability in longer elements:
rule {
@ => _
_ @
}
A rule's diagram shows how things are arranged.
Let's look at a rule more closely:
@ => _
_ @
Inputs:
The @
symbol shows where the atom is.
The _
symbol checks for an empty space.
In other words, the rule checks if there is an empty space below the atom.
Outputs:
The @
symbol shows where to put the atom.
The _
symbol shows where to put an empty space.
In other words, the rule moves the atom down, leaving an empty space behind.
Inputs are the symbols you write on the left-hand-side of a rule diagram to check an atom's surroundings.
In the SandPond engine, there are some in-built inputs that you can use straight away:
-
@
The centre of the diagram. -
_
An empty space. -
#
A non-empty space. -
.
Any space. -
*
Not a space (ie: the edge of the universe). -
$
This element.
You can also make your own input symbols by using the following keywords:
symbol
given
select
check
Outputs are the symbols you write on the right-hand-side of a rule diagram to say what to do when a rule has been matched.
In the SandPond engine, some are built-in already:
-
@
Place this atom in the space. -
_
Empty the space. -
.
Do nothing.
You can make your own output symbols with the following keywords:
symbol
change
keep
You can quickly make a symbol with the symbol
keyword:
symbol W Water
@ => W
W @
The any
keyword randomly translates rules in specified symmetry:
any(x) {
@_ => _@
}
The above rule has a x
symmetry. It will randomly reflect itself in the x-axis.
In other words, it might move right, or it might move left.
For a full list of symmetries, see below.
NOTE: I'm currently working on this so it's very broken and subject to change.
Symmetries are sets of translations that transform your diagrams into other shapes.
They're useful because it means you don't have to write every possible shape of a diagram.
Symmetries can be any combination of x
, y
and z
in order.
eg: xz
eg: xyz
eg: y
We specify the axes that you don't care about.
For example, xz
means that we don't care about how the x
and z
axes change.
They could swap over, flip around, etc.
All given
inputs must be true for a rule to be chosen.
given W (element) => element == Water
W => _
@ @
If there is a water atom above me, delete it.
change
outputs say what atom to put in a space.
change W () => new Water()
@ => @
_ W
This makes a water atom and places it below.
keep
outputs don't do anything. They just leave that space how it is.
keep n
@ => n
This does nothing...
Keeps can still have a function like usual.
keep n () => print("I'm doing nothing!")
@ => n
select
inputs save data that can be used in an output.
given W (element) => element == Water
select W (atom) => atom
change W (selected) => selected
@ => W
W @
This selects a water atom below, and swaps places with it.
All check
inputs must be true for a rule to be chosen. Only one check
function is done per symbol. Unlike given
, it is never cached.
given W (element) => element === Water
check W () => Math.random() < 0.2
WWW ...
@ => _
origin
symbols show where the centre of the diagram is.
origin O
O => _
_ @
The for
keyword loops through all translations of a symmetry (in a random order):
for(xyz) {
@_ => @@
}
The above rule will loop through all possible translations of xyz
in a random order, until it finds a match.
The all
keyword works similarly to the for
keyword, but it doesn't randomise the order of translations.
all(xyz) {
@_ => @@
}
You can also use certain subsets of symmetries.
Symmetries have these properties, which let you access subsets:
-
directions
(only points your diagram in different directions - does not rotate) -
rotations
(only rotates your diagram - does not reflect) -
shifts
(points your diagram in different directions and then rotates around those directions - does not do any other rotations or reflections) -
swaps
(only swaps the axes with each other)
For example, this element would move in a random xyz
direction (6 possible directions):
any(xyz.directions) @_ => _@
This would have 12 transformations:
any(xyz.shifts) {
@ => _
_ @
}
When you write functions for given
, change
, keep
or select
, you can name what parameters you want to use. You can choose from these parameters:
space
atom
element
-
origin
(the space that is calling the event) -
self
(the atom that is calling the event) -
Self
(the element that is calling the event) -
selected
(a value that was returned by aselect
function of the same symbol) -
x
(the relative x-value of the space) -
y
(the relative y-value of the space) -
z
(the relative z-value of the space) -
sites
(an array containing all spaces in the origin's event window) -
siteNumber
(the site index of this space) -
time
(the number of event cycles that have happened since page load)
The parameters below are used for internal purposes, but are still available:
-
transformationNumber
(when using a symmetry block, the current transformation number) -
possibleSiteNumbers
(when using anany
block, an array containing possible site numbers for this space) -
possibleXs
(when using anany
block, an array containing possible relative x-values for this space) -
possibleYs
(when using anany
block, an array containing possible relative y-values for this space) -
possibleZs
(when using anany
block, an array containing possible relative z-values for this space) -
transformationNumbers
(when using afor
block, an array containing transformation numbers)
The maybe
keyword specifies the chance of rules happening:
maybe(1/2) {
@ => _
_ @
}
The above rule only happens 50% of the time.
By default, the rule diagram is positioned as if you are looking from the front
.
You can use the pov
keyword to make the diagram represent another point of view.
pov(top) {
@ => _
_ @
}
The above rule's diagram is shown from a bird's eye view instead of from the front.
Valid points of view:
-
front
(default) back
top
bottom
right
left
Use the action
keyword to give actions to an element.
Actions don't end an atom's behaviour when they match (like rules do).
element Dropper {
change S () => new Sand()
// Drop some sand below me
action {
@ => @
_ S
}
// Afterwards... move into space
any(xyz) @_ => _@
}
Elements can copy another element's rules (and actions) with the mimic
keyword.
element Snow {
colour "white"
change W () => new Water()
maybe(1/100) @ => W
mimic(Sand)
}
This snow element melts 1% of the time. The rest of the time, it behaves like Sand.
You can create elements inside elements. This can help you to organise your code. Sub-elements do not appear in the SandPond menu.
element Snake {
element Head {
// Snake.Head code goes here...
}
element Tail {
// Snake.Tail code goes here...
}
}
When you write SpaceTode, you write blocks of code. There are three ways to write a block.
Multi-line block:
element Forkbomb {
@_ => .$
}
Single-line block:
element Forkbomb { @_ => .$ }
Naked block:
element Forkbomb @_ => .$
Symbols that you declare are block-scoped. They only exist inside the block that they are declared in.
This code causes an Unidentified Symbol
error because the E
symbol doesn't exist in the scope of the diagram:
element Sand {
{
symbol E Empty
}
@ => E
E @
}
It can be useful to scope different parts of your code so that you can keep track of what different symbols mean:
element Sand {
{
symbol W Water
@ => W
W @
}
{
symbol W Wind
W@_ => ._@
}
}
All values and functions are written in embedded JavaScript in SpaceTode. For example, all the symbol functions are written in JavaScript:
given W (element) => element === Water
The JavaScript above is (element) => element === Water
.
You can extend your JavaScript over multiple lines by ending a line with a bracket:
given W (element) => {
return element === Water
}
You can also place your JavaScript within brace brackets if you want to make a closure:
given W { return (element) => element === Water }
You need to know how the SandPond engine works on the backend to use this effectively.
The behave keyword lets you write plain JavaScript to manually define what to do.
element Vanisher {
behave (origin) => SPACE.setAtom(origin, new Empty(), Empty)
}