Modeling Pipe Dream in Machinations and Ceu

26 May 2016

Pipe Mania / Pipe Dream is a much-cloned vein of games where the player places plumbing ahead of a slowly advancing flow of brightly colored fluid.

Screenshot of Pipe Dream

I'm going to assume in this post that you're familiar with it, or could become familiar with it by watching a video.

Let's try to create a really VERY low-fidelity model of it, using Machinations, in order to see what we can see. Machinations is a model and tool developed by Joris Dormans, who wrote about it (with Ernest Adams) in a book. It's based on Petri nets. I'm fond of Petri nets, and at least some of the extensions Machinations makes to Petri nets are clearly valuable.

There are things visible on the board - a box of five pieces not yet placed on the left, lots of open spaces available for placing pieces, some full and some empty pipes. In a previous version of the game, Pipe Mania, there was also a visual representation of the allowance of time before the stuff starts flowing.

We model each of those visible things as a pool of tokens.

With those pools decided, it's fairly easy to decide on transitions or transactions in our model - a player can place a pipe, which consumes a pipe from the box of pieces not yet placed, and an open space, but creates an empty pipe. That's under the player's control, represented in Machinations as "double-struck". If and when the player places a pipe, the game refills the box of pieces not yet placed, that's automatic, represented in Machinations as "starred". There is a period of time which gradually runs out, and once that period of time is gone, empty pieces start becoming filled pieces. If there are no empty pieces available when this process goes to transform them into filled pieces, then the player loses. If the player keeps ahead of the process for long enough, though, they will accumulate a certain number of filled pieces, and win.

Obviously, this model destroys a crucial part of the pipes game, the geometry. I'm not going to defend the practice of low-fidelity modeling, or even lean on Box's "of course its wrong, but is it useful?" slogan, and try to argue that this model is at all useful.

Instead, I will just go on to the next part of this blog, which is trying to transliterate this Machinations model into the Ceu programming language.

There are several transitions in the Machinations model, which operate concurrently. We can model that concurrency using Ceu's "par/or" construct.

input void PLACE;

var int available = 5;
var int open = 70;
var int empty = 0;
var int full = 0;
var int initial_delay = 10;

par/or do
  loop do
    await 1s;
    if available < 5 then
      _printf("A new piece becomes available.\n");
      available = available + 1;
  loop do
    await PLACE;
    if available > 0 and open > 0 then
      _printf("You place a piece on the board.\n");
      available = available - 1;
      open = open - 1;
      empty = empty + 1;
  loop do
    await 1s;
    if initial_delay > 0 then
      initial_delay = initial_delay - 1;
    else/if empty > 0 then
      _printf("The goo flows.\n");
      empty = empty - 1;
      full = full + 1;
      if full >= 20 then
        _printf("You win!\n");
      _printf("You lose!\n");

escape 0;

This is only a start. But it is a start, from which subsequent evolutions can make the game more and more real.

For example, we might replace the "available" counter with a data structure, such as a list or map of pieces with actual distinct identities. Similarly, the three counters regarding the board might be replaced by a board data structure, such as a two-dimensional array. Also, another concurrent process in real games would probably be "every frame, redraw the screen" (woah, radical idea!).

Perhaps it is one of those "obvious once you see it" things, but I think trying to do radically simple (e.g. Petri nets) modeling got us from zero to these initial architectural elements of:

  1. model: the five counters
  2. messages: the void PLACE input, and
  3. processes: the three concurrent trails of: 3.1. refreshing the available pieces, 3.2. handling player input, and 3.3. the dynamics of the flowing

Even though the process of evolving this (boring, colorless) skeleton towards actually being a game is likely to distort those initial architectural elements into unrecognizability, they're still nontrivial to find from scratch.

Disclaimer: Of course, DON'T CLONE! Two ways to avoid cloning:

  1. Analyze multiple sources and remix the analyses. For example, you might mix Pipe Mania / Pipe Dream with Tron, adding some sort of competitive element.
  2. After breaking it down to simplistic, colorless structure, elaborate it out differently than your original did. Instead of giving the player pipes, you might choose to give them words, and instead of requiring the pieces to connect geometrically, you might require them to fit a rigid metrical structure or rhyme.