An engine for describing games built around Xerces, exprtk, and SFML.
Status: built (games/frogger.xml, with the verbs below)
The first three games share one core: a ball or a bullet, a paddle or a shooter, and things that die or bounce. None had a hazard that hurts the player, objects that loop round the screen, or objects that carry another. Frogger (1981) needs all three, plus lives and one-step movement, in a game that is otherwise small: about sixty rectangles, two dozen of them moving. It is the first game chosen to test the vocabulary (13): the rule was to add a verb only where the game could not be described without it, and to keep each verb a behavior, not an implementation.
A grid of 48-pixel cells, 13 across and 14 down: a row for the score and lives, a row of home pads between hedges, five lanes of river, a strip of grass, five lanes of road, and the pavement the frog starts on. Everything is a rectangle drawn 6 pixels in from its cell (so the frog fits a lane with room to spare), which keeps every position a small sum of cell and inset. A truck is two cells long, a log three to five. Cars, trucks and logs are each their own object, spaced by hand: a lane has a different spacing from every other, which a <grid> (one spacing, one velocity) cannot say. Written out in full that was about sixty objects; a <group> per lane (29) says what a lane shares once and each member says where it starts. The scenery (river, grass, road, lane markings, pavement) is more rectangles, drawn first because objects are drawn in file order.
| Need | Chosen | Options considered |
|---|---|---|
| Lives that end the game at zero | dec() beside inc(), and a condition form atmost |
Count deaths up and end at 3 (hides the number of lives in the condition); comparison operators (remaining="<3") instead of names; a separate lives object |
| One step per key press, however long it is held or paused | hop.up/down/left/right(distance), queued by the press and made at the start of the next move |
A held move.* stopped by a timer; a flag on <input> saying “once”; a hop that is a velocity for one frame (swept along the way); move.* in a collision (needs something to collide with) |
| Lanes that loop | wrap() on the screen-edge rule: acts only once the object is completely off the screen and still heading that way, and keeps any overshoot |
reset() at the far edge (every object jumps back to its own start position, and appears whole); teleporting the moment an edge is touched (pops in whole); duplicating each object at both ends |
| Riding a log | carry() in a rule about another object: lends that object’s velocity for the frame |
Copy the velocity into the object’s own (has to be undone when it steps off, and fights the hop and the keys); attach as a child (an ownership model the language does not have); move it by a position offset (bypasses the sweep) |
| The river kills the frog unless it is on a log | unless="logs" on the rule: passed over while the object is also touching something of that class |
Rule order among the frog’s rules (they are separate pairs, so order does not compose); a “safe” class that overrides a “hazard”; water only in the gaps between logs (moving and re-shaped every frame); an object-level condition counting overlaps |
| What touching a hazard or a pad does | reset(), inc(), dec() and move.* in rules about another object (they were ignored there) |
A second verb set for object rules |
| A frog left sitting in a home | A bright green frog under every pad; when the pad die()s, the frog is seen |
show()/hide() verbs; swapping the sprite (there is no way to change one) |
| Scenery colors | More named colors: grey, darkgrey, lightgrey, brown, orange, purple, darkblue, darkgreen, forestgreen | #rrggbb literals as a color (probably where this ends up; the named set is what fits the current way of resolving a color) |
A few reasons behind the choices worth keeping:
wrap() and the screen-edge rules use the carried velocity without special cases.unless the right shape: the rule says what the water does and the exception says what makes it safe, in the file, in one line.edge="horizontal" rule on the frog, so the game needs no rule for it beyond what it already had.Frogger as described here is the game without its extras: no diving turtles, crocodiles, snakes or flies; no timer; one point for each frog home, because inc() added one (an amount on inc/dec came later, with Asteroids); no speed-up between levels; a lost frog goes back to the start at once, with no animation; and a filled home can be entered again without harm (the frog under the pad is drawn there, and nothing tells the game it is taken). The lanes’ hand-set spacing repeats every window width plus the object’s size, so a lane is laid out with that in mind.
inc/dec is built: <inc variable="frog.score">10</inc>, see 41.)<grid> with a velocity per row, or a list of positions, for lanes. (Done another way: a <group> per lane, 29.)unless turns out not to be enough (compare 12).