Overview¶
Brutal Maze is a thrilling shoot ‘em up game with minimalist art style.

Notable features:
Being highly portable.
Auto-generated and infinite maze.
No binary data for drawing.
Enemies with special abilities: stun, poison, camo, etc.
Somewhat a realistic physic and logic system.
Resizable game window in-game.
Easily customizable via INI file format.
Recordable in JSON (some kind of silent screencast).
Remote control through TCP/IP socket (can be used in AI researching).
Table of Contents¶
Installation¶
Brutal Maze should run on Python version 3.6 and above. If you’re using an Unix-like operating system, you’re likely to have Python installed on your computer. Otherwise, you can download it from python.org.
The game also uses multiple third-party libraries, which is recommended to
be installed using pip
. There is a detailed documentation about getting
this package manager on pypa.io.
Install from PyPI¶
For convenience reasons, every release of Brutal Maze is uploaded to the Python Package Index. To either install or upgrade, open your terminal (on Windows: Command Prompt or PowerShell) and run:
pip install --user --upgrade brutalmaze
This requires the the user scheme scripts directory to be
in your environmental variable $PATH
.
Install from Source¶
If you want to tweak the game or contribute, clone the git repository:
git clone https://git.sr.ht/~cnx/brutalmaze
Then install it using pip
, like so:
pip install --user brutalmaze/
Configuration¶
Configuration Files¶
At the time of writing, this is the default configuration file:
[Graphics]
Screen width: 640
Screen height: 480
# FPS should not be greater than refresh rate.
Maximum FPS: 60
[Sound]
Muted: no
# Volume must be between 0.0 and 1.0.
Music volume: 1.0
# Use space music background, which sounds cold and creepy.
Space theme: no
[Control]
# Touch-friendly control
Touch: no
# Input values should be either from Mouse1 to Mouse3 or a keyboard key
# and they are case-insensitively read.
# Aliases for special keys are listed here (without the K_ part):
# http://www.pygame.org/docs/ref/key.html
# Key combinations are not supported.
New game: F2
Toggle pause: p
Toggle mute: m
Move left: a
Move right: d
Move up: w
Move down: s
Long-range attack: Mouse1
Close-range attack: Mouse3
[Record]
# Directory to write record of game states, leave blank to disable.
Directory:
# Number of snapshots per second. This is preferably from 3 to 60.
Frequency: 30
[Server]
# Enabling remote control will disable control via keyboard and mouse.
Enable: no
Host: localhost
Port: 42069
# Timeout on blocking socket operations, in seconds.
Timeout: 1.0
# Disable graphics and sound (only if socket server is enabled).
Headless: no
By default, Brutal Maze also then tries to read site (system-wide) and user configuration.
Site Config File Location¶
Apple macOS:
/Library/Application Support/brutalmaze/settings.ini
Other Unix-like:
$XDG_CONFIG_DIRS/brutalmaze/settings.ini
or/etc/xdg/brutalmaze/settings.ini
Microsoft Windows:
XP:
C:\Documents and Settings\All Users\Application Data\brutalmaze\settings.ini
Vista: Fail! (
C:\ProgramData
is a hidden system directory, however if you use Windows Vista, please file an issue telling us which error you receive)7 and above:
C:\ProgramData\brutalmaze\settings.ini
User Config File Location¶
Apple macOS:
~/Library/Application Support/brutalmaze/settings.ini
Other Unix-like:
$XDG_CONFIG_HOME/brutalmaze/settings.ini
or~/.config/brutalmaze/settings.ini
Microsoft Windows (roaming is not supported until someone requests):
XP:
C:\Documents and Settings\<username>\Application Data\brutalmaze\settings.ini
Vista and above:
C:\Users\<username>\AppData\Local\brutalmaze\settings.ini
Command-Line Arguments¶
$ brutalmaze --help
usage: brutalmaze [options]
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
--write-config [PATH]
write default config and exit, if PATH not specified use stdout
-c PATH, --config PATH
location of the configuration file
-s X Y, --size X Y the desired screen size
-f FPS, --max-fps FPS
the desired maximum FPS
--mute, -m mute all sounds
--unmute unmute sound
--music-volume VOL between 0.0 and 1.0
--space-music use space music background
--default-music use default music background
--touch enable touch-friendly control
--no-touch disable touch-friendly control
--record-dir DIR directory to write game records
--record-rate SPF snapshots of game state per second
--server enable server
--no-server disable server
--host HOST host to bind server to
--port PORT port for server to listen on
-t TIMEOUT, --timeout TIMEOUT
socket operations timeout in seconds
--head run server with graphics and sound
--headless run server without graphics or sound
First, Brutal Mazes read the default settings, then it try to read site and
user config whose locations are shown above. These files are listed as fallback
of the --config
option and their contents are fallback for other options
(if they are absent default values are used instead). We don’t support control
configuration via CLI because that is unarguably ugly.
If --config
option is set, Brutal Maze parse it before other command-line
options. Later-read preferences will override previous ones.
Gameplay¶
Brutal Maze is a fast-paced hack and slash game which aims to bring players a frustrating, horror-like experience. It tries to mimic real-life logic in a way that truly represents our loneliness, mortality and helplessness in the universe.
The game features a solitary hero in a trigon shape who got lost in world of squares. Unlucky for per, the squares have no intention to let their visitor leave in peace. Together, they form a greater being called The Maze. Naturally, The Maze is dynamically and infinitely generated. As our poor hero tries to find a way out, it releases its minions (we will call them enemies from here) to stop per. Since The Maze sees it all and knows it all, it keeps creating more and more enemies for the hero to fight. It also keeps track of which type of squares can do most damages to our trigon, in order to send out the most effective belligerents.
Your mission is to help the hero go the furthest distance possible, while fighting those aggressive enemies. Extra information below will give you a better understanding of what you fight and how you fight them.
Hero¶
The hero is a regular trigon in Aluminium color (from the Tango palette). Perse has the ability to attack and move (both horizontally and vertically) simultaneously. However, close- and long-range attacks can’t be stricken at the same time. When swinging per blade, our hero may also avoid getting damages caused by per enemies’ bullets.
Like heroes in other hack and slash games, the trigon can heal too, but irony, per HP recovery rate decreases as perse gets wounded. Been warned you have, bravery will only give you regrets.
Enemies¶
Enemies are put into hibernation and blend into The Maze at the time of their creation. When the hero comes across, they become awake and show their (physical) colors. Enemy of each color has an unique power as described below:
- Butter
May strike critical hits.
- Orange (also known as Agent Orange)
May prevent the hero from healing or blocking bullets. Poisoned hero will be drawn as a square.
- Chocolate (a.k.a. MDMA in Disguise):
May make the hero high and shoot uncontrollably. Still, Chocolate is good for your health (and so is MDMA).
- Chameleon
Invisible, only shows itself when attacking or being attacked.
- Sky Blue (a.k.a. Lightning Sky)
May immobilize the hero. If this happen our hero can only see the enemies, including Chameleons, on a blank background. What a blessing in disguise!
- Plum (a.k.a. Plum Wine)
May replicate. Very quickly.
- Scarlet Red (a.k.a. Vampire’s Eye)
Moves faster and drains hero’s HP.
The possibility that an enemy attack with its special power increases when it’s able to prove its effectiveness to The Maze. In other words, the more a kind of enemy hit the hero, the more chance they may use their unique abilities. Lucky for you, squares are unitasking so they have to stop moving to perform attacks. This slows them down a bit, however the ones which fall off the display will respawn elsewhere in The Maze.
Attacks¶
In this game, attack’s damage is contingent on the distance between the attacker and its target. The closer they are, the more damage is caused. There is at least an one-third-second delay between two attacks stricken by any character.
Long-range Attacks¶
While projectiles are often called bullets in the code and the documentation, they are more similar to stones propelled by slingshots, as they don’t fly very far (about 6 times the width of an enemy). Those fired by enemies can fly though walls but the ones shot by the hero turn the grid into a new enemy. A bullet is counted as hitting the target when the distance between the center of the two object is less than the circumradius of a cell.
Close-range Attacks¶
It is needless to explain any further on how this kind of attack works, so we only provide the size of the characters for you to calculate when the strike can wound the target. To do so, the attacker must touch its opponents, or simplistically, the distance between the central points of the two characters must not be any greater than the sum of their circumradiuses. Do the calculations yourself, a square’s side is a fifth of the walls’, and covers the same area as a trigon.
Specially, hero’s closed-range attacks also block opponents’ bullets. If this happens, the hero won’t be able to attack in the next turn.
Manual slashing¶
As the hero always follow the mouse, perse perform close-range attack while doing so. Unlike the automatic ones, there isn’t any delay between two manual slashings.
Remote Control¶
Brutal Maze provides a INET (i.e. IPv4), STREAM (i.e. TCP) socket server which
can be enabled in the config file or by adding the --server
CLI flag.
After binding to the given host and port, it will wait for a client to connect.
Then, in each cycle of the loop, the server will send current details of
each object (hero, walls, enemies and bullets), wait for the client to process
the data and return instruction for the hero to follow. Since there is no EOT
(End of Transfer) on a socket, messages sent and received between server
and client must must be strictly formatted as explained below.
Server Output¶
First, the game will export its data to a byte sequence (which in this case, is simply a ASCII string without null-termination) of the length \(l\). Before sending the data to the client, the server would send the number \(l\) padded to 7 digits.
Below is the meta structure of the data:
<Map height (nh)> <Number of enemies (ne)> <Number of bullets (nb)> <Score>
<nh lines describing visible part of the maze>
<One line describing the hero>
<ne lines describing ne enemies>
<nb lines describing nb bullets>
The Maze¶
Visible parts of the maze with the width \(n_w\) and the height \(n_h\) are exported as a byte map of \(n_h\) lines and \(n_w\) columns. Any character other than 0 represents a blocking cell, i.e. a wall.
To avoid floating point number in later description of other objects, each cell has the width (and height) of 100, which means the top left corner of the top left cell has the coordinates of \((0, 0)\) and the bottom right vertex of the bottom right cell has the coordinates of \((100 n_w, 100 n_h)\).
The Hero¶
6 properties of the hero are exported in one line, separated by 1 space, in the following order:
- Color
The current HP of the hero, as shown in in the later section.
- X-coordinate
An integer within \([0, 100 n_w]\).
- Y-coordinate
An integer within \([0, 100 n_h]\). Note that the y-axis points up-side-down instead of pointing upward.
- Angle
The direction the hero is pointing to in degrees, cast to an integer from 0 to 360. Same note as above (the unit circle figure might help you understand this easier).
- Can attack
0 for no and 1 for yes.
- Can heal
0 for no and 1 for yes.

The Enemies¶
Each enemy exports these properties:
- Color
The type and the current HP of the enemy, as shown in the table below.
- X-coordinate
An integer within \([0, 100 n_w]\).
- Y-coordinate
An integer within \([0, 100 n_h]\).
- Angle
The direction the enemy is pointing to in degrees, cast to a nonnegative integer.
To shorten the data, each color (in the Tango palette) is encoded to a lowercase letter. Different shades of a same color indicating different HP of the characters.
HP |
5 |
4 |
3 |
2 |
1 |
---|---|---|---|---|---|
Butter |
|||||
Orange |
|||||
Chocolate |
|||||
Chameleon |
|||||
Sky Blue |
|||||
Plum |
|||||
Scarlet Red |
|||||
Aluminium |
Note
If a character shows up with color 0
, it is safe to ignore it
since it is a dead body yet to be cleaned up.
Flying bullets¶
Bullets also export 4 properties like enemies:
- Color
The type and potential damage of the bullet (from 0.0 to 1.0), encoded similarly to characters’, except that aluminium bullets only have 4 colors
v
,w
,x
and0
.- X-coordinate
An integer within \([0, 100 n_w]\).
- Y-coordinate
An integer within \([0, 100 n_h]\).
- Angle
The bullet’s flying direction in degrees, cast to a nonnegative integer.
Example¶

Above snapshot of the game is exported as:
19 5 3 180
00000000000000000vvvv0000
v0000000000000000vvvv0000
v0000000000000000vvvv0000
v0000000000000000vvvv0000
vvvvvvvvvvvvvvvvvvvvv0000
vvvvvvvvvvvvvvvvvvvvv000v
vvvvvvvvvvvvvvvvvvvvv000v
vvvvvvvvvvvvvvvvvvvv00000
0000000000000000000000000
0000000000000000000000000
0000000000000000000000000
v000000000000000000000000
v000000000000000000000000
v000000000000000000000000
v000vvvvvvv000vvv0vvv0000
v000vvvvvvv000vvvvvvv0000
v000vvvvvvv000vvvvvvv0000
v000vvvvvvv000vvvvvvv0000
v000000vvvv000000vvvv0000
v 1267 975 47 0 1
p 1817 1050 45
g 1550 1217 45
a 2250 1194 45
p 2050 1017 45
e 1850 950 358
x 2126 1189 361
e 1541 1020 167
v 1356 1075 49
Client Output Format¶
Every loop, the server receives no more than 7 bytes in the format of
<Movement> <Angle> <Attack>
. Again, these values need to be
specially encoded.
Movement¶
This is the most awkward one. As we can all imagine, there are nine different
directions for the hero to move. Were they represented as two-dimensional
vectors, at least three characters would be needed to describe such
a simple thing, e.g. 1 0
for \(m = (1, 0)\), and in the worst-case
scenario \(m = (-1, -1)\), we would need five: -1 -1
. 40 bits are used
to carry a four-bit piece of data, freaking insane, right? So instead,
we decided to slightly encode it like this:
Direction |
Left |
Nil |
Right |
---|---|---|---|
Up |
0 |
1 |
2 |
Nil |
3 |
4 |
5 |
Down |
6 |
7 |
8 |
Angle¶
Direction to point to hero to, might be useful to aim or to perform a close-range attack manually. This value should also be converted to degrees and casted to a nonnegative integer.
Attack¶
Attack can be either of the three values:
Do nothing
Long-range attack
Close-range attack
Simple, huh? Though be aware that this won’t have any effect if the hero can yet strike an attack (as described in above section about The Hero).
Pseudo-Client¶
Create an INET, STREAMing socket
sock
Connect
sock
to the addresshost:port
which the server is bound toReceive length \(l\) of data
If \(l > 0\), close
sock
and quitReceive the data
Process the data
Send instruction for the hero to the server and go back to step 3
Your AI should try to not only reach the highest score possible, but also in the smallest amount of time. For convenience purpose, the server will log these values to stdout.
There are samples of client implementations in different languages in the client-examples directory (more are coming).
Copying¶
This listing is our best-faith, hard-work effort at accurate attribution, sources, and licenses for everything in Brutal Maze. If you discover an asset/contribution that is incorrectly attributed or licensed, please contact us immediately. We are happy to do everything we can to fix or remove the issue.
License¶
Brutal Maze’s source code and its icon are released under GNU Affero General Public License version 3 or later. This means if you run a modified program on a server and let other users communicate with it there, your server must also allow them to download the source code corresponding to the modified version running there.

Other creative works retain their original licenses as listed below.
Color Palette¶
Brutal Maze uses the Tango color palette by the Tango desktop project to draw all of its graphics. The palette is released to the Public Domain.
Sound Effects¶
Sound Effects Artist—Tobiasz ‘unfa’ Karoń
Sound Effects Artist—HappyParakeet
Sound Effects Artist—jameswrowles
Sound Effects Artist—MrPork
Sound Effects Artist—suspensiondigital
Sound Effects Artist—gusgus26
Sound Effects Artist—braqoon
Sound Effects Artist—Qat
Sound Effects Artist—pepingrillin