Command-line options
Every flag py_maze takes, the short form beside it, what it defaults to and what it changes.
| Option | Short | Default | Description |
|---|---|---|---|
--width |
-w |
from the preset | Width of the maze in cells (minimum 2) |
--height |
-H |
from the preset | Height of the maze in cells (minimum 2) |
--difficulty |
-d |
normal |
Preset maze size: easy, normal or hard |
--algorithm |
-A |
backtracker |
How the maze is carved: backtracker, prim or division |
--braid |
-b |
0 |
Open this share of the dead ends, from 0 for none to 1 for all |
--seed |
-s |
chosen at random | Seed for the maze generator |
--collectibles |
-c |
0 |
Scatter this many pickups through the maze |
--mode |
-m |
plain |
How the maze is played: plain, chase or quest |
--chase-point |
55 |
Share of the maze walked before the chase begins, from 20 to 90 |
|
--chase-speed |
2 |
How fast the chaser moves, from 0 for the slowest preset to 5 |
|
--quest-stages |
3 |
How many doors a quest shuts the way on with, from 1 to 9 |
|
--save |
-o |
Write the maze to a file so it can be played again, or to standard output as - |
|
--load |
-l |
Play the maze saved in a file instead of generating one, or in standard input as - |
|
--wall-char |
* |
Character a wall is drawn with in a loaded file that carries no py_maze header | |
--open-char |
space | Character an open cell is drawn with in the same | |
--format |
-f |
text |
How the maze is written: text, the picture, or json, a document |
--solve |
-S |
off | Print the solution path overlaid on the maze |
--animate |
-a |
off | Step through the solver’s search on screen |
--stats |
off | Print what the maze measures under it: cells and open over the drawn picture, then dead ends, junctions, longest corridor, solution, the share of the maze it walks and a difficulty score |
|
--quiet |
-q |
off | Print the maze and nothing else: no banner, no seed line, no prompt |
--version |
-V |
Show the installed version and exit | |
--help |
-h |
Show usage and exit |
Note that the short flag for height is a capital -H. Lowercase -h is
reserved by argparse for --help. The same goes for -S and -s: capital
-S solves the maze, lowercase -s seeds it, and capital -A picks the
algorithm. The short flag for --save is -o, as in an output file, since
-s is already the seed. --wall-char and --open-char have no short
flags: they are read by a loader rather than typed at a prompt. -m is the
mode; the chase options and --quest-stages have no short flags, being
settings for a mode rather than everyday typing. --stats has none either, there being no letter
left that reads as it: -s is the seed and -S solves.
--stats prints under the maze rather than into it, so the picture is the
one a run without it draws. A quiet run still reports the measurements,
because a flag that was asked for is not what --quiet drops, and this is
how --solve is already read. A --format json run prints the document
alone, carrying the same measurements under its stats key instead of
printing them underneath.
A run that braided reports one tally the others do not: how many dead ends
--braid opened, beside how many are left. Reading a maze
sets out every measurement, and what the difficulty score is made of.
A maze is drawn with walls between cells, so a maze of W by H cells
renders as W * 2 + 1 characters wide and H * 2 + 1 characters tall.
--stats counts cells and open over that drawn picture rather than over
the W by H that was asked for, so python -m py_maze -d easy --stats
reports cells 169 for a maze of 6 by 6 cells: 13 characters each way, walls
counted alongside the positions the player can stand on. Every other tally
reads the same picture, which is the maze the solver crosses and the player
walks.
Values below 2 cells cannot produce a maze with an interior path, so they are rejected:
python -m py_maze -w 1
Output:
usage: py_maze [-h] [--width WIDTH] [--height HEIGHT]
[--difficulty {easy,normal,hard}]
[--algorithm {backtracker,prim,division}] [--braid [SHARE]]
[--seed SEED] [--collectibles COUNT] [--mode {plain,chase}]
[--chase-point PERCENT] [--chase-speed PRESET] [--save FILE]
[--load FILE] [--wall-char CHAR] [--open-char CHAR]
[--format {text,json}] [--solve] [--animate] [--stats]
[--quiet] [--version]
py_maze: error: argument --width/-w: maze dimensions must be at least 2 cells, got 1
Difficulty Presets
Each preset is just a maze size, so picking one is a shorter way of passing
--width and --height:
| Preset | Size in cells | Rendered size |
|---|---|---|
easy |
6 by 6 | 13 by 13 characters |
normal |
9 by 11 | 19 by 23 characters |
hard |
16 by 20 | 33 by 41 characters |
python -m py_maze --difficulty hard
normal is the size py_maze has always generated, so a run with no options
is unchanged. Either dimension can still be set by hand, and doing so
overrides that half of the preset:
# a hard maze, but only 8 cells tall
python -m py_maze -d hard -H 8
Fitting the Terminal
A maze larger than the screen scrolls out of view and cannot be played, so sizes are measured against the terminal before the maze is generated. When a requested size does not fit, it is capped to the largest one that does and a warning explains the change:
python -m py_maze -w 60 -H 60
Output (on an 80 by 24 terminal):
warning: --width 60 needs 121 columns but only 80 are available; using 39
warning: --height 60 needs 121 rows but only 19 are available; using 9
Five rows are reserved for the start and end markers, the status line, the
spacer and the controls line, which is why the height allowance is smaller
than the terminal is tall.
Those five lines stand six rows tall on a terminal too narrow for the controls line, which carries it onto the row below. The reserve counts the rows rather than the lines, so a narrow terminal gives up one more cell of height and the maze it carves is still one the terminal can show whole:
python -m py_maze -w 60 -H 60
Output (on a 50 by 24 terminal):
warning: --width 60 needs 121 columns but only 50 are available; using 24
warning: --height 60 needs 121 rows but only 18 are available; using 8
The line is the mode’s, so a quest reserves the rows its own longer line needs: the plain line is 60 characters and the quest one is 79, and there are widths where the two differ by a row of maze.
The status line is counted the same way, and it is the other line of those
five that can outrun the terminal. How wide it will be is settled before the
maze is carved: a collectible tally is on it whenever --collectibles asks
for any, and an inventory tally whenever the mode fills one, so
--mode quest --collectibles 9 draws 47 characters before a key has been
pressed. On a terminal narrower than that it takes a second row, and the
reserve is seven rows rather than six:
python -m py_maze -w 60 -H 60 --mode quest --collectibles 9
Output (on a 40 by 24 terminal):
warning: --width 60 needs 121 columns but only 40 are available; using 19
warning: --height 60 needs 121 rows but only 17 are available; using 8
Only the maze rows are measured against the terminal’s columns, which is why
these two lines are the ones that wrap: --width caps a maze row to the
columns there are, and nothing caps the foot. The start and end markers
and the spacer never reach the edge of a terminal with room for a maze in it.
Two limits apply to the capping:
- Mazes are never capped below the 2 cell minimum. If the terminal cannot hold even the smallest maze, the requested size is generated as asked and the warning says the maze will not fit on screen.
- Nothing is capped when the output is piped or redirected, since there is no terminal to fit. Writing a large maze to a file works exactly as before:
python -m py_maze -w 60 -H 60 > maze.txt
A difficulty preset is capped the same way, so --difficulty hard on a small
terminal generates the largest hard-ish maze that fits.
Game Modes
A mode is the maze that is already there played differently: the same grid, the same solver, the same picture and the same keys. A run naming no mode plays exactly as py_maze always has.
| Mode | Playing it |
|---|---|
plain |
Walk from the entrance to the exit |
chase |
The same walk, with something following you |
quest |
The way on is shut until you find what opens it |
python -m py_maze --mode chase
An option belonging to a mode is ignored by every other one, so passing
--chase-point to a plain game does no harm and changes nothing.
Game modes is the page for all three: what each one plays like, what each one draws and the options belonging to each. What follows here is how the options themselves read a value.
Chase Mode
Nothing follows the player at first. Once they have walked far enough into
the maze, a chaser appears at the entrance and starts after them, drawn with
an X. It walks the route the solver finds from itself to wherever the
player is standing, so it never crosses a wall and it turns when they turn.
Being caught ends the run the way the exit does: the same tallies, with a line naming which of the two happened.
--chase-point is how far in the chase begins, as a share of the maze the
player has walked. It is read off the solution measured as the straight runs
it is made of, so a route of four runs of 4, 2, 5 and 3 steps totals 14 and
the default of 55 begins the chase 7.7 steps along it:
# start the chase a fifth of the way in, and give it the fastest preset
python -m py_maze --mode chase --chase-point 20 --chase-speed 5
--chase-speed names one of six preset speeds rather than a delay. The
preset is the moves the chaser makes in a second: 0 is one a second,
rising by one to 5 at six a second.
| Given | Read as |
|---|---|
| A number inside the range | Itself |
| A number under the range | The bottom of it, 20 or 0 |
| A number over the range | The top of it, 90 or 5 |
| A decimal | The nearest whole number, 60.5 becoming 61 |
| Something that is not a number | A notice, and the run carries on without it |
The last of those is not an error and does not stop the run:
python -m py_maze --mode chase --chase-point a34
Output on standard error:
py_maze: --chase-point: 'a34' is not a number, carrying on without it
Quest Mode
The way on is shut by a door, drawn #, and what opens it is in a box,
drawn &, hidden in the part of the maze the player can already reach.
A guide, drawn @, waits just inside the entrance and says what the next
errand is when n is pressed.
--quest-stages is how many door-and-box pairs the chain runs to, from 1
to 9:
python -m py_maze --mode quest --quest-stages 5
It shapes the maze before a word is drawn, the way --collectibles and
--braid do, so unlike the chase options it is refused rather than settled
into its range:
python -m py_maze --mode quest --quest-stages 40
Output on standard error:
py_maze: error: argument --quest-stages: a quest runs from 1 to 9 stages, got 40
The maze is then played with the default chase point, exactly as though the
option had not been given. The notice goes to standard error, so a --quiet
run’s standard output is still the maze and nothing else.
Nothing is walked by the chaser until the player is at least a fifth of the way in, which is what stops it reaching them the moment it appears, and the default speed is one a player who keeps moving matches without hurrying. Standing still is what a chase punishes.
Status codes
A run that could not do what it was asked exits with a code naming what went wrong. Those are tabled under Scripting py_maze, along with everything else a script needs.