Guildmaster: A pod ranking and score tracking system
▄▄ ▄▄ ▄▄
██ ▀███ ▀███ ██
██ ██ ██
▄█▀████████ ▀███ ▀███ ██ ▄█▀▀███ ▀████████▄█████▄ ▄█▀██▄ ▄██▀████████ ▄▄█▀██▀███▄███
▄██ ██ ██ ██ ██ ██ ▄██ ██ ██ ██ ██ ██ ██ ██ ▀▀ ██ ▄█▀ ██ ██▀ ▀▀
▀█████▀ ██ ██ ██ ██ ███ ██ ██ ██ ██ ▄█████ ▀█████▄ ██ ██▀▀▀▀▀▀ ██
██ ██ ██ ██ ██ ▀██ ██ ██ ██ ██ ██ ██ █▄ ██ ██ ██▄ ▄ ██
███████ ▀████▀███▄████▄▄████▄ ▀████▀███▄████ ████ ████▄████▀██▄██████▀ ▀████ ▀█████▀████▄
█▀ ██
██████▀
Guildmaster is a tool for game tracking in MTG or other free-for-all style games.
Code can be found on Github: guildmaster.

Getting Started#
Guildmaster is a small command-line application that reads game results from a CSV and computes Elo-style ratings for players in multi-player (free-for-all) games.
Key features#
- Elo-based ratings for multiplayer free-for-all games
- Interactive Terminal UI for browsing rankings
- Simple CSV input (winner-first ordering)
Installation#
Requires Go 1.25+. Clone and build:
git clone https://github.com/dylanlott/guildmaster.git
cd guildmaster
go build -o guildmaster
Usage#
# Basic console output
./guildmaster -path=./mtgscores.csv
# Interactive TUI
./guildmaster -path=./mtgscores.csv -tui
CSV format example#
The expected CSV format lists players in finishing order (winner first). Example:
,2024-01-15,Alice,Bob,Charlie,Dave
,2024-01-22,Bob,Alice,Dave,Charlie
Elo scores#
Elo scores are a measure of relative skill in zero sum games. Elo scores are maybe most popularly known from chess, where they are the de facto standard for measuring player ratings.
They’re also quite commonly used by online multiplayer video games like Starcraft 2, however most platforms don’t publish their exact algorithm, and most don’t use Arpad Elo’s exact formula, instead tweaking it to their own preferences and use cases - just like we’re going to do.
Measuring skill objectively is difficult. Especially in a game like Magic where variance, deck selection, and social dynamics can drastically impact results.
This is where Elo scores come in. Elo scores track a game history and make a next-game win likelihood prediction based on that history. Each game factors in the opponents score and whether that player won or lost the match. To keep an accurate history, the score keeper must track rankings from game to game, and games must be recorded and analyzed in proper order. However, also means that we can make (admittedly rough) predictions backed by data about who will win in a given matchup.
The important takeaway is that for two given Elo scores you can calculate a delta between them and thus the probability of each player winning.
Elo scores start at some arbitrary point - some chess leagues start at 1500, others at 1000, and others still at 1250. The starting point doesn’t matter as much as one might think. A player’s score will rapidly approach where they really should be, often times within only a few games.
Implementation details#
Guildmaster’s code sets the following defaults (see the project source on GitHub):
- Default starting rating: 1500
- K factor: 40 (keeps ratings responsive)
- D factor: 800 (used by the underlying Elo implementation)
Games are scored by performing pairwise head-to-head updates between adjacent finishers. For an N-player game the algorithm treats the final standing as a sequence of wins and losses between successive players (1st beats 2nd, 2nd beats 3rd, etc.). Table-zap (simultaneous eliminations) are handled by scoring sequential losses in turn order to preserve consistency with historical logs.
Two-Headed Giant (2v2) is supported by treating a team as a single player with a shared rating and a / separated value.
You can try the included example CSV: content/posts/guildmaster/mtgscores-example.csv.
Modeling MTG games#
Commander games usually consist of 4 players, but Elo only calculates one player’s skill level as compared to anothers. To adapt, we must simulate each game as a series of head-to-head outcomes based on final standings.
To adapt, Guildmaster makes a pairwise comparison of each player against the other players based on their final standing in the game.
For example, the 2nd place player loses a game to the 1st place player, but wins a game against the 3rd and 4th place players. This reflects how the free-for-all nature of the game.
When one player eliminates multiple opponents simultaneously (a “table zap”) it is scored as sequential losses for each other player in turn order from the zapper. This is imperfect, but it allows retroactive consistency with existing game logs.
K factor#
The K factor in Elo ratings is essentially the sensitivity knob. A higher K factor means more reaction from the same inputs. Turn it too high, and your score could drastically drop after just a few losses, which doesn’t intuitively line up with our subjective expectations of skill.
On the other hand, set it too low and your score could lag in representing what your actual skill level is, creating a frustrating lack of challenge for a player and making it difficult to see meaningful progression.
Some Elo implementations change the K factor based on the number of games a player has played. In our case, we’re going to simplify K factor handling and set it to a straight 40 all the time. As a reference point, 32 is commonly used for chess players with less than 30 games under their belt, and Arpaud Elo set K equal to 10 in the original Elo formulation.
A K value of 40 keeps our algorithm springy and reactive, which we want in a game where the politics and meta matter as much as a player’s deck and play style, and where players might play a burst of 3 or 4 games in a day and then go months without any others.
Turn Order#
This scoring system is ignorant to any concept of turn order other than when a table zap is recorded and players lose in the respective turn order. Otherwise, we have no information about who went first in the math and if the turn order ever changed. Both of these are good data points to consider for future improvement, as turn order has significant and increasingly outsized effects as the competition level increases.
Future Improvements#
- Table zap detection and refinement
- First blood tracking
- Commander cast count
- Turn order statistics
Prior Art#
Try it locally#
For more examples and details, see the project’s README on GitHub: guildmaster on GitHub