Introduction
This project started out as a way to learn Rust by developing a game.
The game is loosely based on the game of Quoridor but it deviates enough from it to call it a unique game by itself which is why I gave it a different name. The game is what the name suggests: You need to find your way through corridors to reach your goal and win the game. It is classified as a turn-based strategy game.
The documentation has been set up in four main sections (see left bar):
- For Users. You find here how you can install a game client.
- For Players. You find here how you play the game.
- For Administrators. You find here information on installing and maintaining a game server.
- For Contributors. Here you find the rules of engagements if you want to contribute to this project.
Installation
This is the Installation section for the various components:
CLI Client
The CLI client is available for Linux, MacOS and Windows.
Installation
Linux
For Linux the following options are available:
MacOS
For MacOS the following options are available:
Windows
For Windows the following options are available:
Server
ntfy.sh
Corridor Quest optionally supports a ntfy.sh service to send notifications to. You can use any ntfy.sh service you want.
If you want your own ntfy.sh service for your installation you need to configure that yourself. Full documentation for self serving ntfy.sh is here.
We run a ntfy.sh service on https://ntfy.corridor-quest.net which is for our own game service that lives on https://corridor-quest.net:3001
admin tool
Support
This is an Open Source hobby project. It is completly free to use. The consequence of this is that support is not the same as from some vendor of a commercial product. There is no SLA.
That being said, here are some pointers for getting help
Discord
We have a discord channel where we can discuss things [here]
Issues
I you find an issue -> please file an issue. We use a github like issue system.
- For issues in the documentations go [here]
- For issues in the CLI Client go [here]
- For issues in the Server go [here]
For issues you do not know where to file them -> regard them as Server issues.
Contribute
If you want to contribute to fix issues, great! We welcome this. Go [here] for guidance.
Game Rules
As mentioned in the intro, the game is heavily inspired by the game of Quoridor. The basic game rules are more or less the same. See the wikipedia page on Quoridor for background. The game rules are enforced by the system.
Note: Any visual information given here, including the example boards, are based on the CLI version of the game
Game board
Corridor Quest is played on a game board consisting of squares and edges between these squares. The game board can be a different size for each game but width and heigh are always the same. Valid board sizes are 5, 7, 9, 11, 13, 15 and 17 with the default being 9. The coordinate system is chess notation. So, d3 means the square at column 'd', row '3'. The game board’s orientation is the same for everyone. The color of the board’s outer edge is to indicate the player who’s turnout is (see later)
Pawns
Each player is represented by a pawn which begins at the center square of one edge of the board. As a board always has 4 edges, a maximum of 4 players can play a game. However, it is also possible to play a 2 player game in which case the players always start at opposite sides (north and south). Although, in theory, a 3 player is possible, it is not implemented as it gives unequal chances to the 3rd player. Pawns are color coded and use triangle shaped icons to represent direction and distinction for those that have trouble with colors.
Objective
The objective is to be the first player to get your pawn to any space on the opposite side of the game board from which you begin. The triangle shape of the pawn is a great hint to where its goal is. So, if your pawn is North you win when you are the first player to place your pawn on any of the squares on the most southern row of the board.
Walls
A wall is an object that can be placed between squares with the purpose of blocking a pawn's ability to move between those squares. Each player gets an initial amount of walls assigned to place them on the board. The exact initial amount depends on the board-size chosen when a new game is started. A wall has a length of 2 squares and can be placed either horizontally of vertically, but must stay fully on the board.
[example board with walls]
A wall coordinate also follows chess notation syntax, similar to the pawns. However a letter is added as a suffix to indicate the walls orientation: 'v' for vertical and 'h' for horizontal. For example, a wall placed at coordinate 'c3v' will be placed on the edges between squares 'c3', 'd3' and 'c4', 'd4'. The coordinate for a wall is the square that is closest to square 'a1'. Walls are, in the basic game mode, color coded in the same color as the pawn of a player. This means it is known during and after game play which player placed which wall, if any.
Walls have placement rules. A wall cannot be placed where another wall already is, either fully or partially. Also a horizontal wall cannot intersect a vertical wall. However, wall are allowed to align in any direction. The examples shown give all ways walls are legally placed. One important rule is that the placement of a wall may never cause any player to be blocked from reaching its objective at the moment of placing. The system will validate all these rules before a wall placing is approved. Once a wall is placed it can neither be moved nor removed.
Move
A player can choose to move its pawn from one square to another square, but only in a strait line (north, east, south or west) and only one square per turn. The target square must by reachable:
- The edge between the current square and the target square must not have a wall placed in between.
- The target square must not be occupied by another player's pawn.
Jump
A player can jump 'over' another player's pawn if that pawns square is adjacent to the square of the players own pawn. One can only 'jump over a pawn' in a straight line (north, east, south, or west) if the path to the target square is unobstructed. This means:
- There is no wall placed on edges between the square where one's own pawn is and the square to jump to.
- The target square is not occupied by another pawn. You can only jump over a single pawn. If there are two pawns from opponents in a row you cannot jump over both.
- The target square is beyond the boards edge. You cannot jump over another pawn and end up outside the board.
Only if the jump path is obstructed is it allowed to jump diagonally, where the target square is on either side of the obstructing pawn and that square is not also obstructed by the same rules.
Finally and just to state the obvious: A player cannot jump over a wall.
Game Play
When a game starts, pawns are assigned to players at random. The first player is chosen at random as well.
A player must either move/jump its pawn or place a wall but not both. The game only progresses if either action is performed.
The next player is the one according to clock-wise orientation (north -> east -> south -> west).
There is no limit in the number of moves.
Although there is also theoretical no time limit for a player, stalling players are recognized by the system and encouraged to make a move and if they don't, will eventually be removed.
Users & Players
To be able to play a game, one must first become a User. This means you need to sign-up to become one first.
Only Users can participate in games as a player and interact with the system in general.
Some users are System Admin. They have abilities to manage many things. However,they can also participate in games as players like any ordinary user.
The user that creates a new game is automatically the Game Admin and its first player. Being game admin gives you some extra options in game management.
Game States
A game can be in any of the following states:
- Waiting: A game exists but waits for enough players to join it.
- Started. The game has started and game play is possible.
- Cancelled. The game has been cancelled. Game play is no longer possible.
- Finished. The game has a winner.
Any user can start a new game, which is then automatically placed in the Waiting mode. The new game is assigned a unique id called Game ID. The user that creates a new game, becomes the game's administrator as well as its first player.
Users can join games that are Waiting until there are enough players after which the game is automatically Started.
Players can always withdraw from a game. If the game was still Waiting it will need to wait for one more player again. If the game was already started, the game is Cancelled and game play is stopped. The game admin can always cancel a game, even a Waiting one. Cancelled games will be removed after some time by system maintenance.
The first player that has reached its objective is the winner and the game is then Finished. Scores will be calculated and stored for each player. All finished games remain available in full detail for users for later viewing.
Game visibility
A game is by default Public but can be made Private by the game admin.
When a game is Public, it is visible to all users. If it is Private it is only visible to its players.
Note: Once a game is Started the setting can no longer be changed.
Invites
A user that creates a new Public game can optionally also invite other users. A unique invitation code is made available and users can use that code to join the game. The user can also simply communicate the Game ID to other users or wait for other users to join, as there is a way to get a list of Waiting games to join.
For a Private game an invitation code is always generated as the unique access key to the otherwise hidden game.
The user can decide to have the system send the invitation code to the invitees by email while creating the game or to communicate the invitation code by any other way of communication outside the system.
Messaging
The system maintains an in-game messaging system.
All game state changes and game actions generate a message to inform others.
Players can also create messages at any time while the game is played, either as an in-game broadcast (readable to all) or a Direct Message (DM) to one player (only readable by sender and receiver). There is a system-wide maximum of messages a player can create per game of 100 by default. (configurable by a system administrator).
Players can retrieve all in-game messages and their own DM's.
Users not participating can also retrieve in-game messages, but broadcast-only and only of Public games.
Wall Modes
There are two special game modes that are defined by the game administrator (the user that creates the game) and cannot be changed once the game is Started.
Masked Walls
Normally, each player has insights into these important pieces of information:
- Which player has placed which wall on the game board through the coloring of the walls.
- How many walls each player has left to be placed.
- An in-game message created on each wall placing.
A game with Masked Walls mode enabled is different in exactly these aspects:
- Walls on the game board are visible but have all the same neutral color, so players do not know who placed which wall (including their own placed walls)
- How many walls each player has left to be placed is hidden.
- There will not be an in-game message when a wall is placed.
The only way to keep track of who placed a wall is to keep notes after each and every move. However, there is a feature called private wall trading that might make your notes incomplete..
This is only during game play. When the game is finished all wall information is revealed as normal.
Tip: When a player's turn is completed everyone is able to determine what the player did by examining what has changed on the board-game.
Invisible Walls
When this setting is enabled placed walls are completely invisible for the other players on the game-board, but are active as usual. This means players can only know where walls of other players are by bumping into them.
To aid players in their mental model of placed but invisible walls, every player is able to place and remove markers to note where they think (parts of) walls of opponents are. The coordination notation system or markers are the same as with walls. However, a marker is not the same in size as a wall: A wall is placed between 2x2 squares but a marker is placed between 2 squares. So, a marker is half in size of a wall. Players can place or remove their own markers at any moment during game play. Players can also place as many markers as they think they need. Markers can only be used in this game mode. Players can only see their own walls and markers on the game-board during game-play. Users that are not players can only see pawns on the game-board.
When a game is finished, markers are retained and both users and players can see all walls and all markers. Walls and markers are colored. Markers are 'overlayed' over walls where a marker is placed on top of a wall. This gives players after-gameplay insights into their guessing performance.
Note that in Invisible Walls mode the amount of still available walls per player is still displayed as normal and provides important information.
Also note that the Masked Walls and Invisible Walls game modes are independent from each other. When both settings are enabled Walls are invisible on the game-board and also the available walls per player is no longer available.
Wall Trading
Independent of game mode it is possible for players to trade walls among each other. This is an extremely strategic option that players can exercise at any moment during game play.
Wall trading is not free! For each game type the cost of a wall is determined at game start. This cost is used to calculate the final score of each player when the game is finished. See the scoring page for how the scoring system works.
Wall trading is a two phase process:
- One player makes an offer to either buy or sell a wall. Each transaction can only be exactly one wall and when the player makes a sell offer it must have that wall available. The offering will create an in-game message so other players know there is an offer on the table. However, the offering player can choose to make the offering private by creating a direct message (DM) to a specific opponent (only meaningful in a 4 player game).
- Players that can read the offering message can make a trade that is compatible with the offer. A sell offer is transacted by a buy trade and a buy offer is transacted by a sell trade. If a trade is made one wall is moved from the available walls of the seller to the available walls of the buyer. On top of that the wall cost will be deducted from the score of the buying player and added to the score of the selling player.
Note that multiple offers can be done by different players in the same game. Also, a player can have an offer outstanding while trading with another player. A player cannot have multiple offers outstanding.
The strategic aspect is that although the selling of a wall would help an opponent, one can increase their own score doing it and decrease the opponents score at the same time. Each player in the (possible) transaction must make up their mind if this is good for them or not. Offers can go unanswered. Offers can be withdrawn.
Game Variants
The game keeps scores (Quoridor does not). However, it proved difficult to implement, because you only introduce scores if you can compare scores between players. So, scores need to be comparable. The different variations we offer in game play have profound implications on scoring results such that scores in different variations are not comparable. So, things like highscores would then be meaningless.
This is why there is the concept of a Game Variant. Each unique combination of the following game settings is a game variant:
Scoring is comparable within each game variant. You will see this throughout the game. Highscores are given per variant. Leaderboards are per variant.
Notifications
As Corridor Quest is a turn based game that has no time limits it might take some time for each player to make progress in a game. Therefore, getting a notification when something happens in a game is essential. There are various facilities available to receive notification for various events.
event types
The following event types are available:
- 'turn' - occurs when a game the user is a player in has progressed and it is now the user's turn.
- 'action' - occurs when a game the user is a player in has an action: a pawn is moved or jumped or a wall is placed.
- 'game' - occurs when the status of a game the user is a player in is changed (active/finished/cancelled).
notification by mail
Users had to give their mail address when they signed up. This email address is used to send the notifications. A user can enable or disable email notification for each event type:
- 'turn' - Default: enabled
- 'action' - Default: disabled
- 'game' - Default: enabled
notification by ntfy.sh
ntfy.sh is a notification service with clients for all major platforms. It is a pub/sub mechanism: The game service 'pub'lishes notifications on topics (like games and users) and everyone using the app can 'sub'scribe to these topics to receive notifications on them. It is a great way to receive a push notification when something happened that you want to be notified about.
The service works by subscribing to topics. When an event occurs that concerns this topic you will receive a notification of this event. Because of the nature of this service not only users can subscribe to topics but everyone else, also non-users (spectators).
The topics that the game service send notifications about are:
- game - The topic is
game_[x]where [x] is the game-id for Public games and the invitation-code for Private games. - user - The topic is
[username]. Any game related event concerning this user will send a notification to this topic. Note: For privacy reasons, events from Private games are not published to user topics, only to the game topic. Also, messages, broadcasts and DM will not be sent to the user topics. - DM - Direct Messages send to you directly in a game are treated differently due to the privacy aspect of it. You are able to configure a secret DM topic on your user account (see the user documentation of the client you are using). Only if you have set this, DM's will be send to this special topic.
The events that cause a notification to a topic are the same events as described in the event types section above. They are sent to the game topic and to the username topic.
In-game messages are also sent to the game_[x] topic with a tag of 'message' and can thus also be muted seperatly if needed.
Administrators can broadcast a system-wide message to all Active and Waiting games. This will also be sent as a notification to these games.
ntfy.sh supports tagging. The following tags are used:
- 'turn' - for events of event type 'turn'
- 'action' - for events of event type 'action'
- 'game' - for events of event type 'game'
- 'message' - for in-game messages
- 'broadcast' - for broadcast messages
- 'invitation' - for invitations for joining a game
- 'maintenance' - for messages that are cuased by system maintenance tasks
Ntfy.sh clients typically support different treatments per tag, such as muting. This way you can exactly configure which events get through and which not.
ntfy.sh supports priorities for notifications. Each tag has it's own priority:
- turn: urgent
- game: high
- action: default
- message: low
- broadcast: high
- DM: default
- maintenance: default
Scoring
Your final score is a single number:
We look at the shortest path your pawn still needs to reach its goal line (counted in squares). – The shorter the path, the more points you get for progress.
We look at how many walls you have placed. – Every wall you place slightly lowers this part of the score.
The game mixes these two parts with a weight that depends on board size and number of players, then rounds the result to the nearest whole point.
If you are the first to reach your goal line you automatically get the highest score on the board (even if the raw math would have placed you lower), so a win always beats a non-win.
How your score is calculated
Progress part progress = 1 − (current_distance / start_distance) start_distance = board_size − 1
Wall-efficiency part walls = 1 − (walls_placed / initial_walls)
Weights Look up progress_weight and wall_weight = 100 − progress_weight from the table for your board size and player count.
Base score before rounding base = (progress_weight × progress + wall_weight × walls) / 100
Final score
If you are the winner: final = max(rounded(base), highest_opponent_rounded + 1) Otherwise: final = rounded(base)Welcome to the wiki.
Clients
CLI Client
As a person who has worked a decent part of his live in Open Source the CLI client was (of course!) the first client made. It might be a bit alien to normal computer users these days that only know about the web and graphical user interfaces and apps and such, but the CLI was way before that time and is still widely used. If you do not know it, I invite you to try it out. See it as an adventure and an opportunity to learn something new!
CLI stands for Command Line Interface. It uses a terminal application to enter commands and, in the case of Corridor Quest, send those commands to a game server reachable over a network (typically the Internet). Other types of interfaces exist, like the GUI (Graphical User Interface) or the WebUI (Web User Interface) and Corridor Quest can and will have several other types of clients of various interface types to interact with the system. This section however, is about the CLI client.
The CLI client is meant for people that have a desktop/laptop Operating System like Linux, MacOS or Windows. All these operating systems have a tool available called a 'Terminal'. You have access to the CLI client using this 'Terminal'.
Before you can use the CLI client you first need to install it.
General behaviour
The CLI client is called cq and follows normal semantics for CLI applications. At any level you can specifiy the argument --help to get help at that level. Example:
> cq --help
Usage: cq <COMMAND>
Commands:
signup Signup to a Corridor Quest server (create a user)
login Login to a Corridor Quest server
logout Logout from a Corridor Quest server
server Operations on the game server
user Operations on your user account
game Operations on Games
So, for example, entering cq user show wil give you information about yourself when you are logged in:
> cq user show
Username: alice
Email Address: alice@example.local
Notification method: Email
Notify on Turn: true
Notify on Action: false
Notify on Game: true
Active session
Password changed at: 2025-11-06 20:35:52.155293 UTC
'User' is the subject and 'show' the command. This is how it mostly works. Some often used commands, like login, etc, have been made available at the root level instead of under the 'user' where they semantically would belong. this is done for conviniance.
Commands can have arguments, their purpose is to give data in the context of the command. For example: cq user description "Alice from Wonderland" will change the description of the logged in user. "Alice in Wonderland" is the argument here.
> cq user description --help
Change User Description
Usage: cq user description <NEW_DESCRIPTION>
Arguments:
<NEW_DESCRIPTION> The new Description for the user
Options:
-h, --help Print help
Commands can have options. Their purpose is to give context to a command. For example: cg game show --game-id=43 here the subject is 'game', the command is 'show' and the only and mandatory option is --game-id with value '43' which is to provide the game-id of the game to show. Option have long and short forms. the short form for --game-id is -g, so cg game show -g1 is the same. Note that the long form always begins with '--' and the short form with '-'. cg game show --help (or -h) provides you with all options in their long and short form:
> cq game show --help
Show Game
Usage: cq game show --game-id <GAME_ID>
Options:
-g, --game-id <GAME_ID> The ID of the game to show
-h, --help Print help
auto-complete
The cli client supports a feature called auto-complete. You can, for example, only enter the letter 'cq' and then enter tab. Then it will present the commands to choose from. The you can enter the first letter or letters of a command and press tob again and it will auto complete the command. This also works for options. just enter '--' and press tab. Just play a bit with it to get a feel for it. It is a great aid for fast and error free entry.
If auto-complete does not work for you, something went wrong during the installation. Reach out for troubleshooting.
Server
Info
> cq server info --help
Get information about the server
Usage: cq server info [OPTIONS] --server-url <SERVER_URL>
Options:
-s, --server-url <SERVER_URL> the url of the game service
-m, --motd the message of the day
-a, --admin the email-address of the main system administrator
-n, --nfty the url of the nfty.sh service
This command gives you general information about the game server. You do not need to be logged in. Choices are in the command help above. The administrator email address is for occassions where you have yourself locked out and need help.
User
% cq user --help
Operations on your user account
Usage: cq user <COMMAND>
Commands:
password Change password
email-address Change Email Address
verify Validate security sensitive actions
description Change User Description
username Change User Username
show Show User
notify Change notification settings in participating games
ntfy_dm_topic Change the ntfy.sh topic for DM's of the user (keep secret)
profile Operation on profiles
theme Change user theme
password
> cq user password --help
Change password
Usage: cq user password [OPTIONS] <NEW_PASSWORD>
Arguments:
<NEW_PASSWORD> The new password of the user
Options:
-s, --server-url <SERVER_URL> The server url
-u, --username <USERNAME> The username of the user
You use this command to change your password. This is a special command in two ways:
- If you have forgotten your password you can not log in. So, for this command you do not need to be logged in. For that reason, you must give the username as a mandatory option
- This is obviously a security sensitive operation. For this reason a verification code is send to the known email address of this user. Only when this verification code is entered using the verify command from the same computer and user will the password change be completed.
email-address
> cq user email-address --help
Change Email Address
Usage: cq user email-address <NEW_EMAIL_ADDRESS>
Arguments:
<NEW_EMAIL_ADDRESS> The new Email Address of the user
You use this command to change your email address. You need to give the new email address as an argument. This command, like password, is also special:
- Unlike password you must be logged in for this command.
- Like password you will be send a verification code to the NEW email-address. We send it to the new email-address in case the old email-address is no longer working for some reason. An informational email is send to the OLD email-address. Only when the received verification code is entered using the verify command from the same computer and user will the email-address change be completed.
verify
cq verify --help
Validate security sensitive actions
Usage: cq verify --code <VERIFICATION_CODE>
Options:
-c, --code <VERIFICATION_CODE> The verification code
The verify command is used to verify security sensitive operations. There are three:
- Signup
- Password change
- Email address change
When any of the above commands are performed, an email is send to the (new) email address with a unique verification code and this code needs to be entered using this command to confirm the requested action. When the verification code entered is wrong then the user can retry max 2 times. After that the account is locked and the user needs to contact a system administrator.
description
> cq user description --help
Change User Description
Usage: cq user description <NEW_DESCRIPTION>
Arguments:
<NEW_DESCRIPTION> The new description for the user
Each user has an optional description. You can put anything you like in here, but please keep it decent.
username
> cq user username --help
Change User Username
Usage: cq user username <NEW_USERNAME>
Arguments:
<NEW_USERNAME> The new Username of the user
We acknowledge a username is for some an important way to express themselves. As such we provide the option to change it. You do need to be logged in for this though. There is no reason to re-login. New sessions do need to login using the new username. Of course the new name must be unique. All references to the old name in the system are changed to the new name.
This change will notify subscribers to the old username topic in the ntfy.sh notification service (if configured).
show
% cq user show --help
Show User
Usage: cq user show [OPTIONS] [USER_NAME]
Arguments:
[USER_NAME] Optional: The name of the user
Options:
-s, --highscores Include the users highscore for each game variant
When done without a username argument it will show user related data about yourself. If you do give a username, you get to see that users information, but only what is publicly available.
This command has an option '--highscores' which will provide a table with the highest score this user has reached in each [game variant], together with the id of the game where the user did that.
notify
> cq user notify --help
Change notification settings in participating games
Usage: cq user notify <--game|--action|--turn>
Options:
-g, --game Toggle game status change notifications (active/finished/cancelled)
-a, --action Toggle action notifications (any pawn move or wall placement)
-t, --turn Toggle notification when it is your turn
As the game is a turn based game, it might take some time, like with chess, that your opponents will make a move. So, the system provides various ways to notify you when something of interest is happening so you can do more meaningfull things with your life than waiting on others. You can define what is of interest for you: what do you want to be notified about? The help text for the command is quite clear on the possibilities.
'cq user show' will show you what the current settings are and with the nofify command you can toggle the setting.
ntfy_dm_topic
> cq user ntfy-dm-topic --help
Change the ntfy.sh topic for DM's of the user (keep secret)
Usage: cq user ntfy-dm-topic --ntfy-dm-topic <NTFY_DM_TOPIC>
Options:
-n, --ntfy-dm-topic <NTFY_DM_TOPIC> The topic of the user for DM's in ntfy.sh (keep secret)
Set or change the topic for ntfy.sh notifications for DM's. As DM's (Direct Messages) are private, you schould choose a topic that is hard to guess. Regard it as a password or secret. If you do not set this topic, DM's will not be send to ntfy.sh.
profiles
> cq profile --help
Operation on profiles
Usage: cq profile [COMMAND]
Commands:
list List Profiles
switch Switch Profile
When a user interacts using the CLI client a 'default' profile is stored on the local filesystem in the OS specific application folder. This profile is a file that holds the state of the user, like the gameserver's URL, the username, acces tokens, etc.
With some commands, like with signup and login it is possible to specify a separate profile using the -P or --profile argument. this gives the possibility log-in multiple times using different combinations of username and server. There are two different ways to switch to another profile:
- Using the
cq profile switch <name>command. this is a permanent switch which means all subsequent commands will use that profile. - Using an environment variable CQ_PROFILE. For example
CQ_PROFILE=<name> cq user showis executing the commanduser showwith profile. This does not switch the profile permanently.
The profile system gives users the possibility to play games on multiple gameservers from the same client or play as different users on the same game server (although that will typiccally only be used for testing purposes)
theme
> cq user theme --help
Change user theme
Usage: cq user theme <NEW_THEME>
Arguments:
<NEW_THEME> The new theme of the user [possible values: dark, light, translucent]
The CLI client provides a number of themes. a theme is a set of colors for the various objects in the game: pawns, walls, borders, etc. This is to provide the user options depending on the situation in which the player plays the game, such as at night or in a terminal that has an unconvient foreground/background setting. Possible values are: 'dark', 'light' and 'translucent'. You need to try them out and choose one to your liking.
Signup
> cq signup --help
Signup to a Corridor Quest server (create a user)
Usage: cq signup [OPTIONS] --server-url <SERVER_URL> --user-name <USER_NAME> --email-address <EMAIL_ADDRESS> --password <PASSWORD>
Options:
-s, --server-url <SERVER_URL> the url of the game service
-u, --user-name <USER_NAME> the unique identifier of the user
-d, --description <DESCRIPTION> An optional description of the user
-e, --email-address <EMAIL_ADDRESS> Email Address of the user
-p, --password <PASSWORD> The password for the user
-n, --ntfy-dm-topic <NTFY_DM_TOPIC> The optional ntfy DM topic
-P, --profile <PROFILE> The optional profile name
-h, --help Print help
To be able to play games you need to become a user of the game system. To become a user you need to sign-up. To sign-up you need the following information:
- Username: must be unique within the system and maximum 10 long, among some other rules that will be checked by the system. If there is something wrong with your choice of username, you will be told what is problem is.
- Email-address: must be unique within the system and is checked to be a valid email address format. You need to be reachable at this address or signup cannot be completed.
- Description (optional): This is how you would like to describe yourself. It may be you full name, some fantasy name, or anything you like. Max 50 long. Tip: keep it decent.
- Password: This is the secret string you need to provide when you log into the system to prove you are who you claim to be. It has the usual restrictions.
- ntfy_dm_topic: This is an optional field. it is used to set a unique secret topic name to be able to send DM messages to you using the optional ntfs.sh notification service.
- Server (optional): This game is implemented as a Client-Server system. This is done to facilitate many clients from one central system across the network. However, there can be multple game servers available on the network. So, you need to make a choice to which game server you want to sign-up. If you do not give this information the client will choose the default living on https://corridor-quest.net:3000.
When you signup an email is send to the specified email address with a verification code. See Verify.
You can optionally give a profile name. See Profiles for an explanation.
Login
> cq login --help
Login to a Corridor Quest server
Usage: cq login [OPTIONS] --server-url <SERVER_URL> --user-name <USER_NAME> --password <PASSWORD>
Options:
-s, --server-url <SERVER_URL> The url of the game server (ie https://game.example.com:1234)
-u, --user-name <USER_NAME> The username
-p, --password <PASSWORD> The password
-P, --profile <PROFILE> The optional profile name
-h, --help Print help
When you are a user (you have signed up to a game server), you can login. This creates a session, which is a shared state between the client where you logged in and the game server. It is not possible to start another session between this user and the game server while the user is already logged in.
Most commands can only be done when the user is logged in.
As you can see above you need to provide 2 arguments: your username and your password. If you do not specify the password you will be prompted.
Logout
> cq logout --help
Logout from a Corridor Quest server
Usage: cq logout
This command logged you out of the game service in the session file. The session file wil also be removed.
Game
> cq game --help
Operations on Games
Usage: cq game <COMMAND>
Commands:
show Show Game
new New Game
list List games
join Join game
change Change a game (GAME ADMIN only)
cancel Cancel (participation in) a game
message Operation on game messages
move Move your pawn
jump Jump your pawn over another pawn
place Place a wall
mark Mark an edge
unmark Unmark an edge
offer Offer a wall
trade Trade a wall offer
leaderboard Show Leaderboard
show
> cq game show --help
Show Game
Usage: cq game show --game-id <GAME_ID>
Options:
-g, --game-id <GAME_ID> The ID of the game to show
This command will most likely be the most used command of all. The reason is simple: it gives you a full rendering of the current situation in the game. Here is an example of an active 4 player game:
[TBC]
new
> cq game new --help
New Game
Usage: cq game new [OPTIONS]
Options:
--invite [<USER_NAME>]
Specify one or more times with a user name to send an invitation code to those users
-p, --private
Make this game private. Only players will see it, not others
-n, --number-of-players <NUMBER_OF_PLAYERS>
Number of players (2 or 4) [default: 2]
-b, --board-size <BOARD_SIZE>
Board size (5 to 17 uneven) [default: 9]
-m, --masked-walls
Players can not see who has placed walls and how many are left
-x, --invisible-walls
Placed walls are invisible
With this command a user can create a new game. As can be seen from the help output above you can specify the game variant, the visability and invite other users to this game. If you specify '--invite' the invitation code will be returned. If you specify usernames the system will send these users an email with the invitation code. This is optional, you can choose to send the invitation code to other users through any other menas.
list
> cq game list --help
List games
Usage: cq game list [OPTIONS]
Options:
-s, --status <STATUS> filter on game status [possible values: waiting, active, finished, canceled]
-u, --user <USER> filter on user, use 'me' for filtering on yourself
-g, --game <GAME> filter on player relation [possible values: admin, player, won, current]
With the list command you can get a listing of games. You can filter on game status and/or a user and the relation of that user as a player in these games. For example: 'cq game list -u me -g won' gives back all games that you have won.
> cq game list
Game ID Status Players Size Private? Masked? Invisible? Won By Players
4 Active 2 5X5 No No No ? alice, >bob
3 Active 4 9X9 No No No ? bob, >alice, carol, dave
2 Finished 2 13X13 No No No dave carol, dave
1 Finished 2 5X5 No No No carol carol, bob
> is current player
The list gives back the status, game variant, who has won the game (if _finished) and the players in the game.
Note: As long as the game has status Waiting the game admin (the first layer of the game that has also created the game) can change most of this using the change command.
join
> cq game join --help
Join game
Usage: cq game join [OPTIONS] --game-id <GAME_ID>
Options:
-g, --game-id <GAME_ID> The ID of the game to join
-i, --invitation-code <INVITATION_CODE> The invitation code of an invite-only game to join
With the join command you can join an existing game that is still in the Waiting state. If it is an invite only game wou also need to provide the received invitation code.
change
> cq game change --help
Change a game (GAME ADMIN only)
Usage: cq game change [OPTIONS] --game-id <GAME_ID>
Options:
-g, --game-id <GAME_ID>
The ID of the game to change
-n, --number-of-players <NUMBER_OF_PLAYERS>
Change Number of Players (2 or 4)
-b, --board-size <BOARD_SIZE>
Change Board Size (5 to 17 uneven)
-m, --masked-walls
Toggle Masked Walls setting
-x, --invisible-walls
Toggle Invisible Walls setting
-p, --private
Toggle Private setting
--invite [<USER_NAME>]
Specify one or more times with a user name to send an invitation code to those users
The game admin can change the above aspects of a Waiting game. The mandatory '--game-id' option specifies the game to change. If there are more than two players waiting in a 4 player game and the change is to a 2 player game, this is refused. If exactly 2 players are waiting, the game is started.
cancel
> cq game cancel --help
Cancel (participation in) a game
Usage: cq game cancel --game-id <GAME_ID>
Options:
-g, --game-id <GAME_ID> The ID of the game to cancel
If the game is Waiting and you are not the game admin, you are simply removed as a player. If the game is Active or you are the game admin, the game is Cancelled. This means playing is no longer possible and the game will be removed from the system during automatic regular and periodic system maintenance.
message
> cq game message --help
Operation on game messages
Usage: cq game message <COMMAND>
Commands:
send Send a message to a game
list List messages of a game
mute Set the mute state for a player in a game
move
> cq game move --help
You can use 3 methods to specify the move:
1. algebraic notation. For example: 'e4' means column e, row 4
2. compass notation ('north', 'south', 'west', 'east')
3. directional notation ('up', 'down', 'left', 'right')
Usage: cq game move --game-id <GAME_ID> <TO>
Arguments:
<TO>
The position to move to
Options:
-g, --game-id <GAME_ID>
The ID of the game to move the pawn
One of the actions you can do when it is your turn in the game is 'move' your pawn according to the game rules. As you can see from the command's help output above there is 3 ways to specify a move. Most people will most likely use the relative directional options (compass and directional), as they are the most intuitive. The absolute chess based notation is mostly used by automatic interfaces.
jump
> cq game jump --help
You can use several methods to specify the jump:
1. algebraic notation. For example: 'e4' means column e, row 4
2. compass notation ('north', 'south', 'west', 'east')
3. compass diagonal notation ('north-west', 'north,east', 'southwest', 'east-south', etc)
4. directional notation ('up', 'down', 'left', 'right')
5. directional diagonal notation ('up-left', 'up,right', 'down-left', 'downright', etc)
Usage: cq game jump --game-id <GAME_ID> <TO>
Arguments:
<TO>
The position to move to
Options:
-g, --game-id <GAME_ID>
The ID of the game to move the pawn
Like with move there are several ways to specify the location to move to and they are explained above in the command help output. There are specific game rules for jumping.
place
> cq game place --help
General:
You need to use algebraic notation for the placement.
For example: 'e4v' means column e, row 4, orientation vertical
You always use the top-left most coordinate, for example:
e4v means a wall vertically placed between columns e and f spanning rows 4 to 5
'c3h' means a wall horizonally placed between rows 3 and 4 spanning columns c to d
Usage: cq game place --game-id <GAME_ID> <ON>
Arguments:
<ON>
The top-left coordinate to place the wall (example: e4v)
Options:
-g, --game-id <GAME_ID>
The ID of the game to move the pawn
For wall placement only the extended chess notation can be used. There are specific placement rules for walls.
mark
> cq game mark --help
General:
You need to use algebraic notation for marking an edge.
For example: e4v means column e, row 4, orientation vertical
You always use the top-left most coordinate, for example:
e4v means a wall vertically placed between columns e and f
c3h means a wall horizonally placed between rows 3 and 4
Usage: cq game mark --game-id <GAME_ID> <COORDINATE>
Arguments:
<COORDINATE>
The top-left coordinate to mark an edge (example: e4v)
Options:
-g, --game-id <GAME_ID>
The ID of the game to mark an edge
This command is only available if the game is active and has Invisible Walls enabled. This means opponents walls are not visible on the game-board. To help you to keep track of where you think their walls are, you can mark edges. A mark is only one square in length as opposed to walls, which are 2 squares in length. Whenever you show the game you will see only your walls and your marks. You can mark edges whenever you like: you do not have to wait until it is your turn. You can also mark as many edges as you like. You cannot place a mark on on of your own walls. Also, when you place a wall where marks are, those marks will be removed. See marks as a personal notepad. When the game is Finished, invisability mode is disabled and you see all walls again, color coded by player and with you marks as an overlay, so you can see which marks you had right and which not.
unmark
> cq game unmark --help
General:
You need to use algebraic notation for unmarking an edge.
For example: e4v means column e, row 4, orientation vertical
You always use the top-left most coordinate, for example:
e4v means a wall vertically placed between columns e and f
c3h means a wall horizonally placed between rows 3 and 4
Usage: cq game unmark --game-id <GAME_ID> <COORDINATE>
Arguments:
<COORDINATE>
The top-left coordinate to mark an edge (example: e4v)
Options:
-g, --game-id <GAME_ID>
The ID of the game to unmark an edge
This is the opposite of mark. You can remove previously made marks. Here too, you can do this any time.
offer
> cq game offer --help
Offer a wall
Usage: cq game offer --game-id <GAME_ID> <TRADING_MODE> [TRADING_PARTNER]
Arguments:
<TRADING_MODE> Trading mode (Selling or Buying) [possible values: buying, selling]
[TRADING_PARTNER] Trading partner
Options:
-g, --game-id <GAME_ID> The ID of the game to offer a wall
This command is part of the wall trading functionality. You can either offer to buy or offer to sell. You can only sell if you have walls availabile to sell. This will create an in-game broadcast message
You can optionally specify an opponent in the game as a trading partner. This will make the offer private and the offering message is only visible for the trading partner. This is known as a Direct Message (DM).
trade
> cq game trade --help
Trade a wall offer
Usage: cq game trade --game-id <GAME_ID> <TRADING_MODE> <TRADING_PARTNER>
Arguments:
<TRADING_MODE> Trading mode (Selling or Buying) [possible values: buying, selling]
<TRADING_PARTNER> Trading partner
Options:
-g, --game-id <GAME_ID> The ID of the game to trade a wall offer
This command is part of the wall trading functionality. If an opponent has made an offer to either buy of sell, you can make the trade. If the offer is to buy and you want to trade, you need to have a wall available to sell it.
If the offer has been made in private to you, you need to specify the trading partner being the offering opponent.
Making a trade has a cost that will be incorporated in the final score when the game is finished. The specific cost depends on the game variant. The show command for the game disaplays the wall cost.
leaderboard
> cq game leaderboard --help
General:
Leaderboards are per unique combination of board size,
number of players, masked walls and invisible walls.
This is because scores must be comparable for a leaderboard
Usage: cq game leaderboard [OPTIONS]
Options:
-b, --board-size <BOARD_SIZE>
Board size (5 to 17 uneven)
[default: 9]
-n, --number-of-players <NUMBER_OF_PLAYERS>
Number of players (2 or 4)
[default: 2]
-m, --masked-walls
Masked walls
-i, --invisible-walls
Invisible walls
This command show the max 10 users with the heighest scores in a Game Variant. The options are to specify the game variant with a default for each one.
Server Configuration
The server component is delivered with a dotenv file. This file describes all the environment values one can configure to influence the way the server runs. Where defaults are this is also documented inside the file. These values need to be created/overruled as environment values before the server starts. Depending how the server component runs there are specific ways to do this:
- For RPM/DEB based: a systemd unit file is deployed that will read the dotenv file. consult the unit file for the exact location of the doting file.
- For OCI Container based: Both Podman and Docker provide several ways to load env's from files and even separate some out as secrets.
- For Kubernetes based: create a config-map that contain all the envs. Also one can use the secrets facility of Kubernetes.
Note: which of the envs need to be regarded as secrets is up to the administrator
Initial Admin User
There is a special procedure to be followed to create the first system administrator:
-
When the server starts up for the first time (so with an empty database) or when the env INIT is true, the admin user is (re)created in the database with the username and email address from the env's and a randomly generated password.
-
After this you can use the command cq user password on this user to change the password to something you known. A verification code will be send to the admin user's email address and needs to be entered using the cq user verify command
Admin tool
Installation
Operation
User Management
Player Management
Server Management
Notification
ntfy.sh
Cossidon Quest supports ntfy.sh.
Corridor Quest tries to read these environment variables:
- NTFY_URL. This is the full url, so including port, to the ntfy service
- NTFY_USER. This is the username to use to publish
- NTFY_TOKEN. This is the token to use
If NTFY_URL is present and either NTFY_USER or NTFY_TOKEN is not, it is assumed the ntfy service has not implemented access control.
Architecture
Introduction
Corridor Quest (cq in short) is developed as a client server application.
Server component
The server component executable is called cq-server. It is a cloud native implementation. it has an API first design and the API is RESTful. This means you can run multiple instances of the server component behind a proxy / load balancer. It is also available as a container image so you can run it using Podman for example. It also comes with a helm chart to be able to run it on Kubernetes.
The server component requires a PostgreSQL database. It has been tested with PostGreSQL v18 only currently. The location of the database is define by an environment variable DATABASE_URL which defines all needed information in its string. An example is:
The API is self documenting using OpenAPI and Swagger UI. the relative url's for the API documentation are as follows:
- for Swagger UI: /api-docs (for example: https://cq.example.com:3000/api-docs)
- for the OpenAPI JSON spec (for example: https://cq.example.com:3000/api-docs/openapi.json)
The server accepts either http or https inbound connections as controlled by environment variable REQUIRE_HTTPS. https is strongly advised as passwords are otherwise send in clear text over the wire. https configuration can be in one of three ways:
- Direct https with a valid certificate
- X-Forwarded-Proto header (common in reverse proxies)
- RFC 7239 Forwarded header
Server logging is sent to standard out. Log level is controller by an environment variable LOG_LEVEL. Meaningfull values are INFO and DEBUG.
Repositories
API
Coding Standards
Vibe coding policy
In the beginning I used LLM's only sparingly. Mainly as a tutor to learn Rust concepts which I then would apply myself. As the development progressed I used it more and more, also as a research tool.
I used VS Code as my IDE and experimented with some LLM extensions. That is where things went wrong. I gave them access to the codebase and they made a mess of it. That was a good lesson. I only used LLM's in a chatbot since and copy-pasted what I deemed was good and adjusted as needed. That was painful but safe.
About halfway development I got the change to use cursor.ai with Claude Code and never looked back (for coding). I set it up such that it would never make code changes by itself, but giving it full visibility into your codebase is a godsend.
As my trust in Claude Code grew I let it do tedious work in the codebase. Boilerplate stuff mainly. For design work I use it as a tutor/mentor again. I also use it as a research tool to investigate what is idiomatic in certain topics, like api design and such.
This is not what I would define as vibe coding. That is letting an LLM build everything from a description you give. As a 'safe' experiment I did let cursor.ai completely implement all unit and api tests for the server component. IT is safe as it does not touch the main code, and it takes the tedious work of maintainting test from me. It produced more code than the game itself, but that kind of makes sense as there are a lot of corner cases and api endpoints to test.
cq-server
cq-server is structured per resource (user, game, player,etc) with 3 layers:
- The API layer
- The Service layer
- The DB layer
when an api call comes in it is handled by the router at the start of the resource specific rust file inside the handlers folder. This router calls the handler function in the same file. This handler function typically has the following structure:
- api-doc
- the function signature with garde's where possible
- the access check. this will check is the user has permission to call this api endpoint and if it does, fetches the user record of the requester.
- call the service in the service layer
- format and return results if appropriate
The service layer for each resource contains all service functions for that resource. Aservice is typically structuered like this:
- Validations. run service specific validations. Abort when violated.
- Do transformations that are functionally needed
- Make changed to the DB using calls to the DB layer.
- Report where apprpriate (messages, emails, notification)
- Return to the API layer with return data where appropriate.
The DB layer takes care of all the SQL calls to the DB for whatever the services need.
Each layer can have helper functions. They are typically at the bottom of the files.
Testing
I decided to make tests the domain of 100% vibe coding. the coding assistant has full write access to cq-server/src/tests. Inside are both unit and api tests.
- Unit tests are made for testing the game rules in various (edge) cases.
- API tests are end-to-end tests that test the api, service and database layer in cq-server.
When a change is made somewhere, I make the coding assistant aware of this and ask it to refactor the tests accordingly.
The test frameworks uses a separate test database for a run which will be created as part of the test run. The unit test is made to be very fast as it is in memory. the Api tests are comparatively slow as it tests all layers.