ββ ββ ββ
ββ ββββ ββββ ββ
ββ ββ ββ
βββββββββββ ββββ ββββ ββ βββββββ ββββββββββββββββ ββββββ ββββββββββββ ββββββββββββββ
βββ ββ ββ ββ ββ ββ βββ ββ ββ ββ ββ ββ ββ ββ ββ ββ βββ ββ βββ ββ
βββββββ ββ ββ ββ ββ βββ ββ ββ ββ ββ ββββββ βββββββ ββ ββββββββ ββ
ββ ββ ββ ββ ββ βββ ββ ββ ββ ββ ββ ββ ββ ββ ββ βββ β ββ
βββββββ βββββββββββββββββββββ ββββββββββββββ ββββ ββββββββββββββββββββ βββββ ββββββββββββ
ββ ββ
βββββββ
Guildmaster is a lightweight Go application that calculates and tracks player rankings using the Elo rating system. The tool reads game results (particularly Magic: The Gathering games) from CSV data and computes updated ratings using a pairwise Elo scoring method suitable for multiplayer ranked games.
- Elo Rating Calculation: Implements the Elo rating system to provide an objective measure of player skill
- CSV Data Processing: Reads game records from a CSV file to calculate ratings
- Player Performance Tracking: Maintains and updates player ratings based on game outcomes
- Flexible Input: Supports a variety of game formats where players are ranked from winner to losers
- Terminal User Interface: View rankings in an interactive TUI with navigation controls
Guildmaster scores multiplayer games by treating each finished game as a set of pairwise matches derived from the final ranking. For a game with N players, every higher-placed player is considered to have "won" against every lower-placed player in that same game. Examples:
- 1st place beats 2nd, 3rd, 4th, ...
- 2nd place loses to 1st but beats 3rd, 4th, ...
- 3rd place loses to 1st and 2nd but beats 4th, ...
All pairwise expected scores are computed using Elo with the configured D constant and K factor. By default Guildmaster uses:
- K = 40
- D = 800
Updates are calculated from a snapshot of player ratings before the game, accumulated as deltas for each player (summing every pairwise result), and applied once per player. This prevents order-dependent updates and ensures deterministic results when combined with the stable sorting tie-breaker (player name) used when presenting rankings.
This approach gives more consistent and interpretable rating changes for multiplayer events compared to only scoring adjacent placements.
- Go 1.26+ for direct local builds/runs
- Docker (optional, used automatically by
make build,make test,make server, and related targets when local Go is too old) - Dependencies:
- github.com/kortemy/elo-go
- Make
# Clone the repository and navigate to that directory
git clone https://github.com/dylanlott/guildmaster.git
cd guildmaster
# Build the application.
# If your local Go is older than 1.26, the Makefile falls back to Docker.
make build
# Run the scoring algorithm
make run
# Run the terminal application (requires a local Go 1.26+ toolchain)
make tui-
Prepare your game data in a CSV file with the following format:
- First column: Empty (or can contain identifier)
- Second column: Date of the game
- Subsequent columns: Players in order of finish (winner first)
-
Run the application:
# Using the default CSV file (mtgscores.csv)
./guildmaster
# Using a custom CSV file
./guildmaster -path=path/to/your/data.csv
# Display rankings with Terminal User Interface
./guildmaster -tuiThe -tui flag enables an interactive Terminal User Interface for viewing player rankings:
- Navigate through the rankings using the up and down arrow keys
- Exit the TUI by pressing q or Ctrl+C
This provides a more interactive way to browse player rankings, especially when dealing with large player pools.
Run the app in TUI mode:
go run . -tuiThe expected CSV format looks like:
,2022-01-01,PlayerA,PlayerB,PlayerC
,2022-01-08,PlayerB,PlayerA,PlayerDWhere:
- The first column is empty
- The second column contains the date
- The remaining columns list players in finishing order (winner to losers)
The server under cmd/server is now a small persistent web app backed by SQLite. It serves server-rendered pages, session auth, pod management, and a JSON API.
Key routes:
GET /-> leaderboardGET /games/GET /games/:id-> game history and detailGET /players/:name-> player profileGET /pods-> pod list and pod detail flowsGET /submit-> privileged submit UI for owner/admin accountsGET /admin/users-> role management UI (admins can view, owners can change roles)GET /api/scores-> current scores as JSONGET /api/players/:name-> player profile JSONGET /api/games/GET /api/games/:id-> game history JSONPOST /api/games-> submit a game (session owner/admin + CSRF, orAuthorization: Bearer $GUILDMASTER_ADMIN_KEY)POST /api/refresh-> refresh from Sheets with the same privileged auth
- Browser auth uses username/password login with a persistent session cookie.
- Roles are
member,admin, andowner. - The first user in a fresh database becomes
owner. - Owners can promote/demote users at
/admin/users. - Guildmaster prevents removing the last remaining owner.
- Session-authenticated POSTs require a CSRF token.
See .env.example for the current config surface:
SPREADSHEET_IDSCOREBOARD_API_KEYGUILDMASTER_ADMIN_KEYDATABASE_PATHPORT
Run the server locally:
# from the project root
make server
# or
go run ./cmd/serverOpen http://localhost:8080 to use the web UI.
The application:
- Reads the game data from the specified CSV file
- Initializes each player with a base Elo rating
- For each game, updates player ratings based on their performance using pairwise outcomes
- Calculates and displays the final Elo ratings for all players
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
