py_maze py_maze
Source

Scripting py_maze

Everything for the run that is not being watched: a quiet maze, a JSON document, standard input and output, and a status code per failure.

Everything below is for the run that is not being watched: a maze generated for another program to read, a maze handed in from one, and a status code that says what happened without anything having to read the message. None of it needs a dependency and none of it starts a service.

A Quiet Run

--quiet keeps standard output to the maze. The banner, the seed line, the saved: line and the play prompt are all left out, so what is left is the maze and nothing else:

python -m py_maze -d easy --seed 2024 --quiet

Output:

start
* ***********
* *     *   *
* *** * *** *
*   * *     *
*** * *******
*   *       *
* *** ***** *
*   * *   * *
*** * * *** *
*   *   *   *
* ******* * *
*         * *
*********** *
end

The maze is the one a loud run prints, start and end markers included: --quiet takes lines away and changes none. A quiet run does not offer to play, there being nobody at the prompt to answer.

Writing JSON

--format json writes the maze as a document instead of a picture, for a program that would rather not read characters. It says outright what a picture leaves to be worked out:

python -m py_maze -w 2 -H 2 --seed 2024 --solve --format json

Output (laid out here, written on one line):

{
  "py_maze": 1,
  "seed": 2024,
  "entrance": [1, 0],
  "exit": [3, 4],
  "collectibles": [],
  "solution": [[1, 0], [1, 1], [1, 2], [1, 3], [2, 3], [3, 3], [3, 4]],
  "grid": [
    [true, false, true, true, true],
    [true, false, true, false, true],
    [true, false, true, false, true],
    [true, false, false, false, true],
    [true, true, true, false, true]
  ]
}

grid is the maze itself, a list of rows of true and false with true for a wall. solution is null unless --solve or --animate asked for one, and collectibles is empty unless --collectibles scattered any. Every key is specified in the save file format.

A JSON run is quiet whether or not --quiet is given, since a document with Generating maze... in front of it is not a document any more. The same document is what --save writes under --format json, so a file and a pipe carry the same bytes.

Standing in a Pipeline

- is standard input to --load and standard output to --save, as it is to most of the tools py_maze would be piped into. A maze can be written out of one run and read into the next without a file in between:

python -m py_maze --seed 2024 --save - | python -m py_maze --load - --solve

Each half is quiet in the way it has to be. --save - puts the save file on standard output and prints nothing else there, the maze included: the file is already the maze, and drawing it again would corrupt what the next command reads. --load - reads the maze off standard input and does not offer to play, that stream being the maze rather than the keypress a prompt would read.

Warnings still go to standard error, so a maze capped to the terminal says so without anything landing in the pipe.

Loading a Maze Drawn by Something Else

A maze drawn by another tool carries no # py_maze save header and is unlikely to be drawn with * and the space. --wall-char and --open-char say how it was drawn, and the rest follows:

python -m py_maze --load drawn.txt --wall-char '#' --open-char '.' --quiet

drawn.txt:

#.#####
#.....#
#####.#

Output:

start
* *****
*     *
***** *
end

The two options default to the characters py_maze draws with, so a save file with its header cut off loads with no options at all. They apply to reading only: a maze is always written with *, the space and $, which is why the picture above comes back in py_maze’s own characters and can be re-saved, solved or played like any other maze.

A file carrying the header is read with the characters the format fixes, whatever the options say. The header settles the question, and the options are for the file that has nobody to speak for it.

Status Codes

A run that could not do what it was asked says so on standard error and exits with a code for what went wrong, so a script can tell the three apart without reading the message:

Code What happened
0 The run finished
2 An option the command line will not take
3 A file that is not a maze this build can read
4 A file that could not be read, or written
5 A maze with no way from the entrance to the exit

Code 3 also covers a loaded maze fewer than 3 characters wide. The entrance is cut in the second column and the exit in the second from last, and three characters is what it takes for the exit column to stop falling left of the entrance column: at two the exit column falls left of the entrance, both of them on the border, and at one the entrance column is off the row altogether. The solver, the JSON document and the game all read the two out of the grid, so the maze is refused once, before any of them sees it, rather than faulting in whichever the run reaches first.

Code 5 is reported only when a solution was asked for. A generated maze always has a way through, so it is a loaded one that can lack it, and --solve or --animate is what looks. The maze is still printed before the run exits, so a script gets the maze and the news together.

The names are on the package too, as py_maze.EXIT_OK, py_maze.EXIT_USAGE, py_maze.EXIT_SAVE_FILE, py_maze.EXIT_FILE_ERROR and py_maze.EXIT_NO_WAY_THROUGH.