01 — Tiles & Sockets

An alphabet of 17 tiles

Before a dungeon can be generated, it has to be spelled. The whole world is built from just seventeen square tiles and one rule about how their edges may touch. Get the alphabet right and the walls take care of themselves.

One cell, sixteen ways to wear a wall

The dungeon lives on a square grid. Every cell is either solid rock — we call it void — or an open floor. That sounds like two tiles, but a floor cell also needs to know which of its four edges carry a wall. With four edges that can each independently be wall-or-open, a floor has 2⁴ = 16 variants. Add the void tile and we get 17 tiles in total.

We encode the four walls as a 4-bit marching-squares mask — bit 0 = North, 1 = East, 2 = South, 3 = West. A set bit means “this edge borders the void, so draw a wall here.” Click the edges below to toggle walls and watch the mask change.

Sockets: edges that have to agree

Here is the trick that makes everything else work. Each edge of a tile exposes a little symbol — a socket — describing what it offers a neighbour:

Two tiles may sit next to each other only if their touching sockets are compatible:

F ↔ F   two floors share an open, walkable seam
W ↔ E   a wall always backs onto void
E ↔ E   void meets void
(anything else is forbidden)

Drag the two tiles’ walls below and watch the shared seam light up green when it’s legal and red when it isn’t.

Why the walls are never wrong

Look at what the socket rule forces. A floor edge facing void must show W, because the only thing compatible with the void’s E is a wall — F↔E is illegal. And two adjacent floors must leave that seam open, because F is only compatible with F. So:

The payoff: walls appear exactly on floor/void seams — never missing, never doubled, never stranded in the middle of a room. You don’t place walls; they are implied by where the floor is.

Paint some floor below (click and drag). The walls are derived automatically from the marching-squares mask of every floor cell — that’s the same deriveMasks() the real generator runs.

Weights: tuning the texture

All 17 tiles are legal, but they aren’t equally likely. Each tile gets a weight — a relative frequency the solver biases toward. Three knobs shape them: voidWeight (how much rock), openWeight (how much open floor), and wallFactor — a per-wall multiplier below 1 that makes heavily-walled tiles progressively rarer, so rooms come out open rather than cramped and boxy.

Drag the knobs and watch the relative weight of each of the 16 floor tiles (grouped by how many walls they carry). This is exactly the table fed to the Wave Function Collapse solver in the next chapter.

That’s the entire alphabet: 17 tiles, one compatibility rule, a handful of weights. Next we hand this to a solver that fills a whole grid with tiles such that every seam is legal — Wave Function Collapse.