git @ Cat's Eye Technologies Cosmos-Boulders / master README.md
master

Tree @master (Download .tar.gz)

README.md @masterview rendered · raw · history · blame

Cosmos Boulders
===============

_Play it online_ [@ catseye.tc](https://catseye.tc/installation/Cosmos_Boulders)
| _See also:_ [Bubble Escape](https://codeberg.org/catseye/Bubble-Escape#bubble-escape)
∘ [Nested Modal Transducers][]

![Screenshot of Cosmos Boulders](images/cosmos-boulders-screenshot.png?raw=true)

An arcade-style HTML5 minigame built with [immutable data][] and [reducers][] in
ES5 JavaScript.

Goals
-----

The main goals are to demonstrate:

*   that nested state machines are a good way to describe video games structurally.
*   that reducers are an acceptable way to implement nested extended state machines.
*   that a simple but full-featured arcade game can be written in a few hundred
    lines of (JavaScript) code.

I say reducers are "acceptable" because I think [Nested Modal Transducers][]
would be better in several respects.  The long-term plan is to write a version
using them instead, to compare.

Non-goals
---------

*   It's not intended to be a very playable, very original game.

State machine diagram
---------------------

This diagram was generated by [State Machine Cat](https://state-machine-cat.js.org/)
from the description in [cosmos-boulders.smcat](doc/cosmos-boulders.smcat).
While it does not capture every detail, it does show the overall structure.

![State diagram of this video game](images/state-machine-diagram.png?raw=true)

Tour of the code
----------------

The code uses [Immutable.js][] to implement immutable data, and [Redux.js][] to
implement reducers.  Neither library is used so extensively that it couldn't
be easily eliminated, with the relevant parts of the code written "from scratch".

The code starts with some **utility functions**.  These should be fairly
self-explanatory.  Some of them are designed to work with objects and actions
inside reducers.

There is then a sequence of sections, one for each of the following objects:
**Player, Missile, Boulder, Game**.  Each section contains

*   (optionally) a "reset" function that takes an object and returns a version
    of that object which is reset to its starting state
*   a "make" function that returns a new object of that type.  It may take
    some arguments, which influence the created object.  This function
    usually builds on the reset function.
*   a reducer function that takes an object and an action and returns
    a new object.
*   (optionally) other helper functions that the reducer function uses.

One action that all of the reducers for all of these kinds of objects take,
is `STEP`, which progresses the state of the entire game by one timeslice
(usually somewhere around 1/60th of a second).

When a state machine is implemented with a reducer, the "state variable"
which can take on a finite set of values, is not called `state` as might
be expected; instead it is called the `mode` of the object.  This is to
avoid confusion between it and the state of the entire object.

The code tries to be very careful about not modifying game state data in
the reducer functions, to the extent that it does not even overwrite
local variables; instead it introduces a new local variables (called e.g.
`game2`, `game3`, etc.) and places the new value in it.  (i.e., in ES6
JavaScript, these locals could all be declared `const`.)

The section for **InputContext** objects may seem somewhat roundabout.  Strictly
speaking, the InputContext is not necessary, but it intends to illustrate a
couple of things.

In a web browser, the DOM translates key presses into "key down" and "key up"
events.  But in an arcade cabinet, buttons are simply switches that are either
make or break when the circuit reads them.  The virtual hardware tries to simulate
that.

But it's still useful for the game code to see "button pressed" as events.
So it's the job of InputContext to synthesize those button level changes back
into events for the game reducers.

The **virtual hardware** tries to simulate the basic circuitry of a video game
arcade cabinet, including the screen (as a canvas) and the controls (as switches
whose make or break state is reflected in a boolean value).  It generates
events when a new video frame is ready to be displayed, and when a coin is
inserted.

Finally, the **main driver** sets up the virtual hardware, hooking up the events
to a Redux store; subscribes to the store to update the video display.

Further reading
---------------

*   A couple of years ago (2017) I was motivated to finally write down my
    formulation of [A Basic Theory of Video Games][], which I had been
    thinking about on-and-off for many years beforehand.
*   Earlier this year (2019) I tried to refine the ideas there into a more
    formal model that I called [Nested Modal Transducers][].
*   Many years previous to these (2008), James Hague wrote a series of articles
    about [Purely Functional Retrogames][], which touches on a few of the
    same issues.
*   John Earnest's [Deep: A Postmortem][] (2011), also touches on a few of the
    same issues.

[immutable data]: https://facebook.github.io/immutable-js/
[reducers]: https://redux.js.org/basics/reducers
[Immutable.js]: https://facebook.github.io/immutable-js/
[Redux.js]: https://redux.js.org/
[Nested Modal Transducers]: https://codeberg.org/catseye/The-Dossier/src/branch/master/article/Nested-Modal-Transducers/README.md
[A Basic Theory of Video Games]: http://catseye.tc/view/The-Dossier/article/A%20Basic%20Theory%20of%20Video%20Games.md
[Purely Functional Retrogames]: https://prog21.dadgum.com/23.html
[Deep: A Postmortem]: https://github.com/JohnEarnest/Mako/blob/97b9796ff5c9f9ba0221ccbf5207cea4567d8daf/docs/postmortem-Deep.md