Assemblies
| Assembly and namespace | What's in it |
SimpleMaze | The core: generation, the maze data, the play rules, the solver, pictures
and the ScriptableObjects. It references no packages, and everything except the two asset types is plain C#
that also runs outside Unity. |
SimpleMaze.Demo | The 3D demo: the view, the game, the HUD, the hub, sounds and
effects. It uses the Input System and Unity UI packages, and HDRP when that package is installed. |
SimpleMaze.Editor | The Maze Generator window, the inspectors and the documentation
menu. Editor only. |
Everything below is in the SimpleMaze namespace unless it says
SimpleMaze.Demo.
MazeGenerator
The entry point. Every method copies and clamps the settings first, so your object is never changed, and the
same settings with the same seed always give the same maze. A null settings object means the defaults, and an
empty seed means a fresh random one.
| Method | What it does |
MazeData Generate(MazeSettings settings) | One maze from this exact seed, solved and
rated. |
MazeData GenerateVerified(MazeSettings settings, CancellationToken cancel = default) |
Tries the seed, then the seed with #1, #2 and so on, up to
MaxAttempts, until a maze is sound: solvable, a fair race, no soft-lock and enough coverage when
asked for, and the target stars. On a miss it returns the closest maze with a note. If it is cancelled before
the first maze, it throws OperationCanceledException. |
List<MazeData> GenerateBatch(MazeSettings settings, int count, CancellationToken cancel = default) |
A pack of verified mazes with seeds root-0, root-1 and so on. |
Task<MazeData> GenerateAsync(...), GenerateVerifiedAsync(...) | The same
on a background thread. |
Task<List<MazeData>> GenerateBatchAsync(MazeSettings settings, int count, IProgress<int> progress = null, CancellationToken cancel = default) |
A batch on a background thread, reporting how many are done (1 to count). |
MazeSettings and enums
MazeSettings holds every input to the generator. Each field, its range and what it
does is on the Generating page. It also has:
| Member | What it does |
MazeSettings Clamp() | Puts every field into its valid range, in place, and returns
the same object. |
MazeSettings Clone() | A copy. |
bool HasGimmicks | True when any gimmick is asked for. |
| Enum | Values |
GridShape | Square, Hex, Triangle, Circular |
CarveAlgorithm | RecursiveBacktracker, Prim, Kruskal, Wilson, HuntAndKill,
GrowingTree |
Endpoints | Farthest, CenterOut, OutsideIn, Fixed |
Movement | Step, Roll |
ShapeKind | None, Circle, Ring, Heart, Star, Diamond, Cross, Text, Custom |
ShapeSource | Dark, Light, Opaque (which pixels of a picture are inside) |
TopologySpec describes the board: Shape, Width,
Height, Rings, FirstRing and Floors. Build one with
TopologySpec.Grid(shape, width, height) or TopologySpec.Circle(rings, firstRing = 0),
and Clamp() returns a copy with every field in range.
MazeData
A finished maze: the board, the passages, the players and gimmicks, and the solver's verdict stamped on it.
It is serializable, so it stores inside a ScriptableObject, and plain C#, so it works outside Unity too. Cells
are numbered as described in Cells and slots.
| Board | |
TopologySpec Grid | The board this maze is carved on. |
string Seed | The seed that made it. With Settings it rebuilds this exact
maze. |
CarveAlgorithm Algorithm | How it was carved. |
int[] Masks | Each cell's open sides: bit s set means slot s is
open. |
List<int> Holes | Cells removed from the board, including the ones a shape cut
away. |
ShapeKind Shape, List<int> Outside | The shape it was cut to and
the cells the shape cut away. |
List<MazeRoom> Rooms | The open rooms, each a list of
Cells. |
List<Tunnel> Tunnels | Weave crossings: each runs under the corridor cell
Over, joining A and C on either side of it. |
| Players | |
List<int> Starts, List<int> Goals | One start per player.
One goal per player, or a single goal everyone heads to. |
int Start, int Goal | Player 1's start and goal. |
int PlayerCount | How many players. |
int GoalOf(int player) | The goal that player heads to. |
bool SharedGoal | Every player heads to Goals[0]. |
Movement Movement | How the players move. When Roll can't be used (a board that can't
roll, or no start that rolls into the goal), the maze is stored as Step, with a note. |
| Gimmicks | |
List<int> Keys | Keys[k] is the cell holding key k. Taking
it opens every door of colour k for good. |
List<Door> Doors | Locked sides: Cell, Slot and the
Key colour that opens it. A door blocks both ways. |
List<Switch> Switches, List<Gate> Gates | A switch has a
Cell and a Channel (0 to 3). A gate has a Cell, a Slot,
the Channel that flips it, and StartsOpen for a decoy the switch shuts. |
List<int> Gems | Every gem must be collected before a goal lets a player
in. |
List<int> Traps | Trap cells. Stepping or rolling onto one fails. |
List<OneWay> OneWays | A side you can leave Cell through
Slot, but never come back. |
List<Portal> Portals | Pairs of cells A and B.
Entering either puts you on the other. |
| The verdict | |
bool Solvable | The solver proved every player can reach its goal. |
int Par | The proven minimum: steps, or rolls. With several stepping players it is the
total of their routes. -1 means unsolvable or not verified. |
List<int> PlayerPars | Step: each player's own par. |
bool Fair | False only for a race (Step, several players, one goal) whose players'
pars differ. |
long OptimalCount, bool CountComplete | How many different best solutions
there are. When CountComplete is false the count was cut short and is a lower bound. |
bool Unique | Exactly one best solution, proven. |
bool CanGetStuck | Some reachable position can no longer reach the goal (a
soft-lock). |
int Difficulty, int Score | The 1 to 5 star rating (0 when unsolvable)
and the raw score it comes from. |
List<int> Solution | Player 1's positions along a best solution, start first.
Step lists every cell, Roll lists where the player comes to rest after each roll. |
List<PlayerRoute> Routes | Step with several players: each player's best route
as Cells. |
List<int> Moves | Roll: the directions of the best solution. |
| Facts and notes | |
int DeadEnds, int Junctions, int Loops | Cells with one way
in or out, cells where three or more ways meet, and independent loops (0 is a perfect maze). |
int SolutionLength, int Decisions, int RealDecisions | Steps
on player 1's shortest route through the bare maze, the forks on it, and the forks whose wrong way runs 3 or
more cells deep. |
float Coverage | The fraction of the maze the best solution passes through (0 to
1). |
int Shortcuts | Doors and first gates a player can walk around. |
MazeSettings Settings | The settings it was made with. |
List<string> Notes | What the generator adjusted or couldn't do. |
| Helpers | |
Topology Topology, int CellCount | The grid, built from
Grid on first use. |
bool IsOpen(int cell, int slot) | Is this side open? (Doors and one-way sides are open
sides with a rule on them.) |
bool IsHole(int cell) | Is this cell removed from the board? |
Passages ToPassages() | An editable copy of the passages. |
Cells and slots
A cell is an index from 0 to CellCount - 1, and each side of a cell is a slot. Use
Topology.Neighbor(cell, slot) to find the cell across a side and SlotTo(cell, other)
for the side between two neighbours.
| Grid | Cell index | Slots |
| Square | y * Width + x, row 0 at the bottom | 0 N, 1 E, 2 S, 3 W |
| Hex | y * Width + x, row 0 at the bottom, odd rows shifted half a cell right |
0 E, 1 NE, 2 NW, 3 W, 4 SW, 5 SE |
| Triangle | y * Width + x, row 0 at the bottom; cell (x, y) points up when x + y is
even | 0 left, 1 right, 2 the flat side |
| Circular | 0 is the centre, then each ring counter-clockwise from angle 0 | 0
counter-clockwise, 1 clockwise, 2 inward, then one outward slot per cell in the next ring (the centre has one
slot per first-ring cell) |
| Floors | floor * cells per floor + base cell | The base grid's slots, then Up
and Down (the ground floor has no Down slot and the top floor no Up slot) |
Roll directions use the same numbers as the slots on Square and Hex.
Playing: MazeRules
Your game keeps a MazePlay and asks MazeRules what each move does.
These are the same rules the solver counted with, so the game and the par always agree.
| MazePlay | |
int[] Positions | Each player's cell (-1 once home). |
bool[] Home | Which players are home. |
int[] Keys | Keys held, one bit per colour, per player (on Roll every entry is the
same, because keys are shared). Bits 16 to 19 are the switch channels flipped an odd number of times; read a
gate with MazeRules.IsGateOpen. |
int[] Gems | Gems collected, one bit per gem, per player. |
MazePlay Clone() | A copy, for undo. |
| MazeRules | What it does |
MazePlay NewGame(MazeData m) | Every player on its start, nothing held. |
bool TryStep(MazeData m, MazePlay play, int player, int toCell, out string reason) |
Moves one player one cell: a neighbour, the far end of a tunnel, or up or down the stairs. On success the
position is updated in place, including portals, pickups, switches and getting home. Otherwise it returns
false with the reason, such as a wall, a locked door, a shut gate, a one-way side or a trap. There is also an
overload without reason. |
RollResult Roll(MazeData m, MazePlay play, int dir, List<int>[] trails = null) |
Rolls every player in direction dir. play is not changed. The result has
After (the new position), Moved (false for a wasted roll) and
Trapped (someone rolled onto a trap, so the roll fails). The optional trails, one
list per player, receive every cell each player passes, for animation. |
bool Hint(MazeData m, MazePlay play, int player, out int move, int stateBudget = 500_000) |
The first move of a best way on from here. On Step move is the cell to step to, and on Roll
the direction. False with -1 when it's solved, that player is home, there is no way on (a soft-lock) or the
search ran out of budget. |
bool IsSolved(MazeData m, MazePlay play) | Every player is home. |
bool IsGateOpen(MazePlay play, int player, Gate gate) | Is this gate open for that
player now? |
bool CanRoll(GridShape shape), bool CanRoll(TopologySpec grid) | Roll is
defined on Square and Hex, on one floor. |
int DirectionCount(GridShape shape) | Roll directions: 4 on Square, 6 on Hex, 0
elsewhere. |
Vec2 Direction(Topology t, int dir) | The way a roll direction points, in board
units. |
bool IsLegalStep(MazeData m, int from, int to, out string reason) | Can a player step
between two cells of the bare maze, ignoring gimmicks? |
bool Validate(MazeData m, out string error) | Is the data well-formed? Both sides of
every opening agree, holes are sealed, every start, goal and gimmick sits on a playable cell, nothing
overlaps, every gate has its switch, every tunnel runs straight under a corridor, and the stored route
walks. |
const int SwitchShift = 16 | Where the switch flips sit in
MazePlay.Keys. |
Solving and rating
| MazeSolver | What it does |
MazeVerdict Solve(MazeData m, int stateBudget = 500_000) | Solves the maze as it stands
and returns a MazeVerdict: Solvable, Par, PlayerPars,
Count, CountComplete, Unique, CanGetStuck,
Explored, Path, Routes and Moves. |
MazeVerdict SolveFrom(MazeData m, MazePlay play, int player = 0, int stateBudget = 500_000) |
Solves on from a position in play. Par is the moves still needed. On Step it solves for that
player alone, and Moves holds the slot of each step. |
int[] StepParsFromEveryCell(MazeData m, int player = 0, int stateBudget = 500_000) |
On Step, that player's par to its goal from every cell, holding nothing, with all the gimmicks (-1 for a
hole or no way home). Null when the budget ran out. |
const long CountCap | The best-solution count stops at 1,000,000,000. |
| Rating and checks | What it does |
MazeDifficulty.Score(MazeData m), MazeDifficulty.Stars(MazeData m) | The
raw score and the 1 to 5 stars. See How the stars work. |
MazeAudit.Verify(MazeData m, out string before, out string after) | Solves a stored
maze again and restamps its verdict. True when anything changed. |
MazeAudit.Reroll(MazeData m) | A fresh maze from the same settings and a new seed, or
null for a maze with no settings. |
MazeAudit.MeetsRequest(MazeData m) | Did the maze get everything its settings asked
for: solvable, the target stars, the coverage, no soft-lock when asked, and a fair race? |
MazeAudit.Short(MazeData m) | One line, such as "par 63 steps, 3 of 5 stars,
unique". |
Presets and Auto
| Member | What it does |
MazePresets.All | Every preset, in menu order. Each MazePreset has a
Name (its menu path), a Description, and Make(), which returns a fresh
MazeSettings. |
MazePresets.Tune(MazeSettings s, List<string> changes) | What the Auto button
does: a copy with settings that fight each other fixed, and a line per change. |
Pictures
MazeExport draws a maze in plain C#, so it works in a build, in the editor or on a
server.
| Member | What it does |
byte[] Png(MazeData m, ExportOptions options = null) | A PNG file's bytes. |
string Svg(MazeData m, ExportOptions options = null) | An SVG document. |
byte[] Pixels(MazeData m, ExportOptions options, out int width, out int height) | Raw
RGBA bytes, top row first. |
Png and Pixels with a MazeSketch | A picture of a given
moment, for example part way through the solution. |
int FitSize(MazeData m, MazeSketch sketch, ExportOptions options, int maxWidth, int maxHeight) |
The Size that fits a box. |
| ExportOptions | Default | What it does |
Style | Printable | Printable (black on white with the start and goal),
AnswerKey (with the solution drawn in), or AsShown (colour, every gimmick, the solution and a
legend). |
Size | 2048 | The maze's longer side in pixels (64 to 8192). |
WallWidth | 0.12 | Wall thickness as a fraction of a cell (0.02 to
0.4). |
Margin | 0.8 | The white border, in cells (0 to 4). |
Legend | true | AsShown: draw the legend under the maze. |
Floor | -1 | Stacked boards: -1 draws every floor side by side, and 0 or more
just that floor. |
MazeSketch is the drawing model behind the pictures. MazeSketch.Build(maze, play,
focus, trails) sketches a position, MazeSketch.Frames(maze) steps through the stored solution
move by move, and MazeSketch.Describe(maze, cell, play) says what is in a cell in plain words.
Shapes
A ShapeMask is a black and white bitmap (Width, Height,
Pixels, row 0 at the top). Set it on MazeSettings.ShapeMask with
Shape = ShapeKind.Custom.
| Method | What it does |
ShapeMask.FromRows(params string[] rows) | From rows of text: # (or any
character other than a space or .) is inside. |
ShapeMask.FromText(string text) | Text in the built-in 5 by 7 font. |
ShapeMask.FromPixels(byte[] rgba, int width, int height, bool bottomRowFirst, ShapeSource source, float threshold = 0.5f, int maxSide = 128) |
From a picture's RGBA bytes. Pass bottomRowFirst true for Unity's
GetPixels32. The result is cropped to the shape and shrunk to at most maxSide
across, and is null when nothing counts as inside. |
Topology
A Topology is a grid as a graph of cells and slots. Get one from
MazeData.Topology or Topology.Create(spec).
| Member | What it does |
int CellCount, int SlotCount(int cell) | How many cells, and how many
sides a cell has. |
int Neighbor(int cell, int slot) | The cell across a side, or -1 at the
border. |
int OppositeSlot(int cell, int slot), int SlotTo(int cell, int other) | The
neighbour's side that faces back, and the side between two cells (-1 when they don't touch). |
Vec2 Center(int cell) | A cell's centre in board units (x right, y up). |
Edge EdgeOf(int cell, int slot) | The wall on that side: a segment from
A to B, or an arc when Arc is true. |
int CellAt(Vec2 p) | The cell under a point, or -1. |
void Bounds(out Vec2 min, out Vec2 max) | The board's bounds, for framing. |
(int x, int y) Coord(int cell) | Column and row, or index in ring and ring on a
circular board. |
A board with floors is a LayeredTopology, which adds Base,
Floors, FloorOf(cell), BaseCell(cell), At(floor, baseCell),
UpSlot(cell), DownSlot(cell) and IsStairSlot(cell, slot).
ScriptableObjects
| Type | What it holds |
MazePuzzleAsset | One maze: Maze and Label, plus
shortcuts to its verdict (Shape, Floors, Movement,
PlayerCount, Solvable, IsUnique, OptimalCount,
Par, Difficulty, Seed) and Set(data, label). |
MazePuzzlePack | An ordered list of mazes: PackName,
Mazes, Count, Get(index) (null when out of range),
Set, Add, Replace(index, data) and Clear(). |
PuzzleAssets | CreatePuzzle(data, label) and
CreatePack(mazes, packName) make new instances in memory, which you can save with
AssetDatabase.CreateAsset in the editor or use at runtime. |
IPuzzleProvider | Both asset types implement it: AppendTo(List<MazeData>
levels) adds their mazes in order, so one list in the inspector can take either. |
Both are created by the Maze Generator's Save menu, not by an empty right-click Create.
Demo components
These are in SimpleMaze.Demo. The Demos page explains how
they fit together.
| Type | What it does |
MazeDemo | The one component the demo scene needs. It builds the view, game, HUD,
hub, sounds and effects in code and runs the flow. Fields: Camera, Pitch,
HdrpLook, UiThroughCamera and a level pack per mode. Methods include
Setup(), PlayEndless(mode, grid, stars), PlayLevel(mode, index),
Generate(mode, settings), Begin(maze), NextGame() and
BackToHub(). |
MazeGame | Plays a maze on a MazeView3D: input, movement, every gimmick's
animation, moves against par, undo, restart, hints, auto-solve, players and races, stairs and the camera.
Start a game with Begin(maze), drive it with Push(Vector2 screen) or the keyboard,
and listen to Won, Cue, Moment and Message. |
MazeView3D | Builds a maze in 3D from primitives and meshes, on any grid, with no
prefabs: Build(maze), CellPosition, StandPosition,
FocusFloor, FrameCamera, and lists of the objects it made (keys, doors, gates,
players and so on). |
MazeHud | The in-game HUD and win panel, in uGUI with the built-in font. |
MazeHub | The front screen: the welcome panel, the mode cards, and each mode's Endless
and Level Pack setup. |
MazeMode, MazeModes | The five demo modes, their names, colours, sample
mazes and the Endless recipe for each mode, grid and star count. |
MazeProgress | Each pack level's best moves, kept in PlayerPrefs:
Best, Record, Clear and NameOf. |
MazeBeeper | Sounds made in code for every MazeCue, with no audio
files. |
MazeEffects | Small effects (sparkles, dust, confetti) from a pool of primitives, for
every MazeMoment. |
DemoLook, DemoHdrp | The demo's colours and materials for the active
render pipeline, and the HDRP volume that gives it the same look. |
Building blocks
The pieces the generator is made of are public too, for anyone who wants to build their own pipeline. Most
projects never need them.
| Type | What it does |
MazeBuilder | Build(settings, seed) makes one maze from clamped settings
and an exact seed, which is what every MazeGenerator method calls. |
Passages | The open sides of every cell over a topology, plus holes, with
Open, Close, IsOpen, Degree and
OnRim. |
Carver | Carves a perfect maze through every playable cell with a
CarveAlgorithm. |
HolePlacer, BoardShape, Weave, GimmickPlacer | Holes,
shapes, crossings and gimmicks. |
GraphSearch | Breadth-first search, shortest paths and connectivity over
passages. |
Rng | The small deterministic random generator, seeded from a string. |