RPG Stat Matrix Generator
Desktop Application — Multiplatform
January 2020 - March 2020
Solo Project
January 2020 - March 2020
Solo Project
The Problem
Since the dawning of tabletop RPGs, game masters and players have disagreed on the best way to roll for character stats. In the olden days, players would roll their stats in order. You'd roll 3d6; no take-backs. Players were just as likely to roll abysmally bad stat blocks as they were good ones. At the time, that was just the way the game worked. As tabletop RPGs matured, so did the methods players used to roll their stats. Game masters started letting players assign their stats in whatever order players wanted, followed by the advent of mulligan rules and do-overs for really bad stat blocks. Finally, we developed the tried-and-true 4d6 drop the lowest method which seems to have cemented its place as the compromise of agency and variance.
Meanwhile, on the far end of the agency/variance spectrum, some players have chosen to cast their dice aside in favor of a forbidden method: point-buy. With this method, players purchase their stat points from a table and distribute them however they please. This method is especially popular among hardcore, min-max players who want greater control over their characters' advancement.
While both methods—dice rolling and point-buy—are valid in their own right, neither has ever satisfied my desire for a happy medium of player agency and variance. So, I adapted some old, long-forgotten secrets from days long forgotten and crafted a solution.
I introduce to you: The Stat Matrix Method.
Meanwhile, on the far end of the agency/variance spectrum, some players have chosen to cast their dice aside in favor of a forbidden method: point-buy. With this method, players purchase their stat points from a table and distribute them however they please. This method is especially popular among hardcore, min-max players who want greater control over their characters' advancement.
While both methods—dice rolling and point-buy—are valid in their own right, neither has ever satisfied my desire for a happy medium of player agency and variance. So, I adapted some old, long-forgotten secrets from days long forgotten and crafted a solution.
I introduce to you: The Stat Matrix Method.
The Stat Matrix Method
Using the Stat Matrix Method is simple. Before players start character creation, the game master creates a 6x6 grid of random numbers (I use the aforementioned 4d6 method). When players reach the point of choosing their stats, the game master presents them with the grid and tells them, "You may pick any row, column, or either of the two diagonals. By doing so, you claim those six stats as your own; nobody else can have them. Arrange your stats however you choose." Players then take turns choosing their stat blocks from the matrix locking down rows, columns, and diagonals as they go.
While players can share individual cells between stat blocks, they cannot end up with the same stat block. Everybody ends up with something unique. More importantly, everybody end up with something they chose. Players have agency over the kind of randomness they wish to play with while still not having complete control over their stat block. It's a happy marriage of both worlds. |
So, the Stat Matrix Method is a unique alternative option for game masters who are looking to mix things up. There's just one problem. Generating a new balanced stat matrix takes time. When I first adopted this method, I created my stat matrices in an Excel spreadsheet and used conditional formatting to help balance my grid based on five parameters: stat block sum, average, minimums, maximums, and standard deviation. With about 45 minutes of fiddling, I could turn a randomly rolled 6x6 grid into a balanced matrix. However, I don't want to add 45 minutes to my session preparations whenever new characters need to be made. I needed to automate it. There had to be a better way.
The Stat Matrix Generator
I started my quest to automate the stat matrix balancing process by making a console application that could generate a 6x6 grid of numbers that satisfied whatever parameters I wanted it to have. Since I had already balanced a grid like this by hand, I used the same values I did in my Excel spreadsheet.
My stat matrix generator takes in four parameters and generates a valid matrix using a Hill Climbing genetic algorithm. This approach takes cues from more sophisticated Gradient Descent solutions but without the need for calculus or granularity. I chose to go the genetic algorithm route for a number of reasons.
Brute ForceIf I didn't want to learn and implement something new, a brute force approach to this problem might have been my choice. While not fancy, the amount of guess-and-check work isn't so great that some noise and simple heuristics couldn't have found a solution within a reasonable amount of time.
Before I started working on this at all, I approached some peers in search of suggestions. Two of them were so captivated by the problem that they took it upon themselves to solve it on their own time. Their solutions did incorporate some brute forcing of the search space, but they provided valuable insights nonetheless. |
I learned that if the solution does not utilize enough randomness, simple searches can find wacky answers to satisfying its parameters. For instance, a grid of all 12's will satisfy all of the non-standard deviation parameters at their default values. This is obviously a bad answer, but something like a Sudoku algorithm might trend towards an answer like this without lots of special case handling. I also found that, of the two solutions I looked at, one generated very flat stat blocks with just the bear minimum of variation allowed by the input parameters and the other generated bimodally distributed answers with stats at the very low end and very high end and very little in the middle.
Most importantly, neither of these answers took into account the mechanics of stats in the context of a tabletop RPG system. Using D&D as an example, every other point an individual stat receives turns into a greater stat bonus for the player after they've allocated their stats. As a result, a stat block with high, even values could be strictly better than a similar stat block with high odd values. All this told me I needed something more sophisticated. Something that would allow me greater control over how a stat matrix was generated.
Most importantly, neither of these answers took into account the mechanics of stats in the context of a tabletop RPG system. Using D&D as an example, every other point an individual stat receives turns into a greater stat bonus for the player after they've allocated their stats. As a result, a stat block with high, even values could be strictly better than a similar stat block with high odd values. All this told me I needed something more sophisticated. Something that would allow me greater control over how a stat matrix was generated.
My Solution
After consulting with peers and professors, I decided to make my solution a hill climbing algorithm. This type of algorithm works by randomly mutating a copy of the working stat matrix a number of times (in my case 10) to create 10 matrix candidates. These candidates are then evaluated using a fitness function based on the algorithm's input parameters. The best scoring candidate then becomes the new working matrix and the cycle continues until the working matrix is a valid answer.
What this solution offers me that a brute force approach does not is control over a fitness function. In my evaluation process, I can define what qualifies as a desirable quality for a matrix to have. In my case, my fitness function is actually a penalty function that penalizes matrices for being far away from a desirable answer. Notably, the fitness function (or penalty function in my case) does not evaluate matrices for valid answers; that's handled by another function. I do this so that corner cases like the above mentioned 'oops all 12's' matrix can score really well, but still be rejected for being an invalid answer.
What this solution offers me that a brute force approach does not is control over a fitness function. In my evaluation process, I can define what qualifies as a desirable quality for a matrix to have. In my case, my fitness function is actually a penalty function that penalizes matrices for being far away from a desirable answer. Notably, the fitness function (or penalty function in my case) does not evaluate matrices for valid answers; that's handled by another function. I do this so that corner cases like the above mentioned 'oops all 12's' matrix can score really well, but still be rejected for being an invalid answer.