> Canonical HTML: https://kerfscript.mylasertools.com/models/tilt-maze/

# Tilt maze

Tilt maze: roll a ball bearing from the dot to the star. Every seed draws a new maze, and there is always exactly one way through. The maze layer is stacked as many times as the ball needs, under a clear cover.

This design has 5 parts on 2 sheets. Customize it in the [playground](https://kerfscript.mylasertools.com/playground/?model=tilt-maze).

## Downloads

- [KerfScript source](https://kerfscript.mylasertools.com/models/tilt-maze/tilt-maze.kerf)
- [tilt-maze-sheet-1.svg](https://kerfscript.mylasertools.com/models/tilt-maze/tilt-maze-sheet-1.svg)
- [tilt-maze-sheet-1.dxf](https://kerfscript.mylasertools.com/models/tilt-maze/tilt-maze-sheet-1.dxf)
- [tilt-maze-sheet-2.svg](https://kerfscript.mylasertools.com/models/tilt-maze/tilt-maze-sheet-2.svg)
- [tilt-maze-sheet-2.dxf](https://kerfscript.mylasertools.com/models/tilt-maze/tilt-maze-sheet-2.dxf)

## Parts

| Part | Quantity | Size |
| --- | ---: | --- |
| Base | 1 | 144 × 144 mm |
| Maze layer | 3 | 144 × 144 mm |
| Cover | 1 | 144 × 144 mm |

## Settings

| Name | Default | Range or choices |
| --- | --- | --- |
| `cols` | `12` | 4 to 20 |
| `rows` | `12` | 4 to 20 |
| `seed` | `7` | 1 to 999 |
| `ball` | `6` | 4 to 12 |
| `wall` | `3` | 2 to 6 |
| `cover` | `true` | on or off |
| `$thickness` | `3` | 2 to 6 |
| `cover_thickness` | `2` | 1.5 to 3 |
| `$kerf` | `0.15` | 0 to 0.3 |

## Source

```kerf
// Tilt maze: roll a ball bearing from the dot to the star. Every seed draws
// a new maze, and there is always exactly one way through. The maze layer
// is stacked as many times as the ball needs, under a clear cover.

/* [Maze] */
// Cells across
cols = 12; // [4:1:20]
// Cells down
rows = 12; // [4:1:20]
// Different seeds draw different mazes
seed = 7; // [1:1:999]

/* [Ball and walls] */
// Ball bearing diameter
ball = 6; // [4:0.5:12]
// Width of the walls between channels
wall = 3; // [2:0.5:6]
// Add a clear cover to keep the ball in
cover = true;

/* [Material] */
// Sheet thickness of the wood layers
$thickness = 3; // [2:0.5:6]
// Thickness of the clear cover
cover_thickness = 2; // [1.5:0.5:3]
// Beam width
$kerf = 0.15; // [0:0.01:0.3]

/* [Hidden] */
$material = "plywood";
$fn = 32;
channel = ball + 1.5;
cell = channel + wall;
border = 9;
w = cols * cell + 2 * border;
h = rows * cell + 2 * border;
// Enough maze layers to sink the ball below the cover
layers = ceil((ball + 0.5) / $thickness);
rnd = rands(0, 0.999, cols * rows * 2, seed);

// The centre of cell [x, y]
function at(c) = [border + (c.x + 0.5) * cell, border + (c.y + 0.5) * cell];

// Depth-first search: walk to a random unvisited neighbour, back up when
// there is none. Returns the passages as pairs of cells.
function carve(stack, seen, passages, k) =
  len(stack) == 0 ? passages :
  let (
    here = stack[len(stack) - 1],
    next = [for (d = [[1, 0], [-1, 0], [0, 1], [0, -1]]) let (n = here + d)
      if (n.x >= 0 && n.x < cols && n.y >= 0 && n.y < rows && !seen[n.y * cols + n.x]) n]
  )
  len(next) == 0 ? carve(slice(stack, 0, len(stack) - 1), seen, passages, k) :
  let (n = next[floor(rnd[k % len(rnd)] * len(next))], id = n.y * cols + n.x)
  carve(concat(stack, [n]), [for (i = [0:len(seen) - 1]) i == id || seen[i]],
    concat(passages, [[here, n]]), k + 1);

passages = carve([[0, 0]], [for (i = [0:cols * rows - 1]) i == 0], [], 0);
start = [0, 0];
goal = [cols - 1, rows - 1];

module plate() rect([w, h], r = 5);

module screw_holes() for (x = [border / 2, w - border / 2], y = [border / 2, h - border / 2])
  translate([x, y]) circle(d = 3.4);

module channels() for (p = passages) hull() {
  translate(at(p[0])) rect(channel, center = true);
  translate(at(p[1])) rect(channel, center = true);
}

// Assembled: the base, the maze layers and the cover, stacked; the cover's
// own material block changes $thickness, so keep the plywood's
ply = $thickness;
step("Glue the maze layers onto the base, lining up the screw holes") {
  part("Base", at = [0, 0, 0]) {
    difference() {
      plate();
      screw_holes();
    }
    engrave() {
      translate(at(start)) circle(d = channel * 0.55);
      translate(at(goal)) star(5, channel * 0.42, channel * 0.18);
    }
  }

  part("Maze layer", qty = layers, at = [for (k = [1:layers]) [0, 0, k * ply]]) difference() {
    plate();
    screw_holes();
    channels();
  }
}

if (cover) step("Drop the ball in and screw the clear cover on") material(thickness = cover_thickness, name = "clear acrylic") part("Cover", at = [0, 0, (layers + 1) * ply]) difference() {
  plate();
  screw_holes();
}
```

Set power, speed and cut order in your laser software. See the [laser software guide](https://kerfscript.mylasertools.com/docs/compatibility/index.md).
