DEV Community

Devanshu Biswas
Devanshu Biswas

Posted on

Building Pipe Mania from Four Bits: One Bitmask Rule Runs the Whole Game

Pipe Mania looks like it needs a graph library and a flood-fill. It doesn't. The whole game — laying pipe from a source, racing a burning fuse, then watching water crawl cell by cell until it either reaches a goal or springs a leak — comes down to one idea and one line of code. Here is how it fits together, in pure vanilla JavaScript on a single canvas.

A pipe piece is just a set of open sides

There are no sprites and no per-piece special cases. A piece is which of its four sides are open, packed into four bits: North 1, East 2, South 4, West 8. A horizontal straight has East and West open, so its mask is 2 | 8 = 10. An elbow from North to East is 1 | 2 = 3. The rare cross is all four at once, 15.

const N = 1, E = 2, S = 4, W = 8;          // one bit per side
const OPP = { 1: 4, 4: 1, 2: 8, 8: 2 };    // N<->S, E<->W

const PIECES = {
  H:  E | W,          // 10  straight ──
  V:  N | S,          //  5  straight │
  NE: N | E,          //  3  elbow └
  SE: E | S,          //  6  elbow ┌
  SW: S | W,          // 12  elbow ┐
  NW: N | W,          //  9  elbow ┘
  CROSS: N | E | S | W // 15  crossover ┼
};
Enter fullscreen mode Exit fullscreen mode

That is the entire vocabulary. Every rule below is a bit test against these masks. A weighted "bag" hands you the next piece so straights and elbows are common and the cross is rare, and you click empty cells to drop queue[0], overwriting any pipe the water has not yet reached.

Two questions, answered by one line

When the fuse burns down, the water is released as a little walker sitting in the source. Each tick it fills its current piece, then asks the piece where to leave: a straight or elbow has exactly two open sides, so "the other opening" is just the mask with the entry bit cleared. The cross is the one exception — a genuine crossover that sends water straight through.

function pieceExit(mask, from) {
  if (mask === PIECES.CROSS) return OPP[from];  // crossover: straight through
  return mask & ~from;                          // the single OTHER open side
}
Enter fullscreen mode Exit fullscreen mode

Then comes the heart of the whole game — the connection test. The walker steps to the neighbour in its exit direction and asks exactly one thing: does that neighbour have the opposite opening facing back at me?

const [dr, dc] = DELTA[flow.exit];              // step to the neighbour
const nr = flow.r + dr, nc = flow.c + dc, entry = OPP[flow.exit];
const nb = inBounds(nr, nc) ? board[nr][nc] : null;

if (nb && nb.kind === "pipe" && (nb.mask & entry)) {  // openings line up?
  flow = { r: nr, c: nc, from: entry,
           exit: pieceExit(nb.mask, entry),
           progress: flow.progress, isSource: false };
} else {
  return lose();                                // a leak — flow stops dead
}
Enter fullscreen mode Exit fullscreen mode

The test neighbour.mask & entry is the connectivity model. If the bit is set, water advances and the distance climbs; reach the goal and you win. If the neighbour is off the board, empty, or its openings don't line up, the flow has sprung a leak and stops dead.

Same flow on any monitor

One more trick keeps it fair. The flow is advanced by a fixed-timestep loop: bank real elapsed time in an accumulator and spend it in constant 1/120 s slices, so the water ticks identically on a 60 Hz or a 144 Hz screen.

const STEP = 1 / 120;
let last = performance.now(), acc = 0;
function frame(now) {
  let dt = (now - last) / 1000; last = now;
  if (dt > 0.25) dt = 0.25;                      // clamp a lag spike
  acc += dt;
  while (acc >= STEP) { update(STEP); acc -= STEP; }
  render(); requestAnimationFrame(frame);
}
Enter fullscreen mode Exit fullscreen mode

That is the whole game: pieces are open-side bitmasks, a piece routes water to its other opening, and water advances only where the neighbour's opposite bit is set. Everything else — the overwrite rule, the distance goal, win and lose — is a few lines each.

Play it, step through the logic, and copy the eight build blocks here: https://dev48v.infy.uk/game/day58-pipe-mania.html

Top comments (0)