In the last post, we discussed assigning skill ratings to competitors in trading card games. These ratings can be used to make predictions between competitors about which side we believe would win or lose in a match.
This time, we take deck strategy into account.
Unlike with games like chess, trading card games are asymmetric in that each competitor brings different cards to the match. Some strategies have an edge on others. We’ll have to take this into account for our working model.
Why Deck Strategy Matters
In games like chess, both players sit down with the same game pieces. All the game pieces are completely symmetrical in their game abilities and positional states. There are no surprises that can’t already been seen from the start of the game. A player can’t pull out a special piece from a hidden part of the board to use, or sacrifice three pieces to give another one some special, hidden power, until the end of turn.
Trading card games, however, are exactly like this.
They are asymmetrical. Your deck isn’t like my deck, and even if it is, the exact cards and the order in which they are drawn are different.
Chess is a game that sees both players come to the game with pistols. It’s fair in this way.
Trading card games allow players to pick different kinds of weapons: swords, baseball bats, pistols, explosives, etc.
Some weapons to well against another (a sword vs. a baseball bat, for example), while others do less well against another (a sword vs. an automatic rifle).
Trading card games are unfair in this way, but it is something that deck building tries to take into account: if I can’t beat another strategy outright, how can I upset the opponent’s strategy or undermine it to make my win more likely.
Ultimately, all trading card game players know that the deck they choose has some great matchups and some bad ones. We need a way to take this reality into account in a way that games like chess, on which so much of the probability modelling is based, don’t.
Adding Deck Detail to Match Data
In the last post, we evaluated a .csv file that contained synthetic data on 500 competitors playing a total of 12,000 games over a tournament season.
To this data, we now add the deck used by each competitor in each match, as shown below:
You can see the revised .csv file here.
There are a total of 15 decks, numbered 1 through 15, randomly assigned to each competitor in each match.
The randomization is such that as the tournament season continues, older decks fall out of use as new decks come into use. This simulates a change in metagame over the course of the season.
Deck Matchup Probabilities
With the deck detail added to the seasonal tournament data, we can assess how well each deck does against each other deck, independent of the competitors that use these decks.
Comparing decks in Excel, we get the following:
Deck | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0.5000 | 0.5618 | 0.5434 | 0.5050 | 0.6029 | - | - | - | - | - | - | - | - | - | - |
2 | 0.4382 | 0.5000 | 0.5047 | 0.4969 | 0.5288 | 0.5271 | 0.4783 | - | - | - | - | - | - | - | - |
3 | 0.4566 | 0.4953 | 0.5000 | 0.5326 | 0.4907 | 0.4961 | 0.5216 | 0.5245 | 0.5357 | - | - | - | - | - | - |
4 | 0.4950 | 0.5031 | 0.4674 | 0.5000 | 0.4736 | 0.5053 | 0.4793 | 0.4806 | 0.4330 | 0.5063 | 0.4286 | - | - | - | - |
5 | 0.3971 | 0.4712 | 0.5093 | 0.5264 | 0.5000 | 0.5111 | 0.4547 | 0.4832 | 0.5253 | 0.4760 | 0.5595 | 0.4821 | 0.6136 | - | - |
6 | - | 0.4729 | 0.5039 | 0.4947 | 0.4889 | 0.5000 | 0.5095 | 0.5241 | 0.5433 | 0.5085 | 0.5224 | 0.5045 | 0.5137 | 0.4286 | 0.7105 |
7 | - | 0.5217 | 0.4784 | 0.5207 | 0.5453 | 0.4905 | 0.5000 | 0.5116 | 0.4036 | 0.5452 | 0.5891 | 0.4494 | 0.4519 | 0.5306 | 0.4773 |
8 | - | - | 0.4755 | 0.5194 | 0.5168 | 0.4759 | 0.4884 | 0.5000 | 0.5503 | 0.5444 | 0.4795 | 0.5804 | 0.5667 | 0.3684 | 0.6389 |
9 | - | - | 0.4643 | 0.5670 | 0.4747 | 0.4567 | 0.5964 | 0.4497 | 0.5000 | 0.5402 | 0.5039 | 0.4491 | 0.4865 | 0.5439 | 0.7292 |
10 | - | - | - | 0.4938 | 0.5240 | 0.4915 | 0.4548 | 0.4556 | 0.4598 | 0.5000 | 0.5000 | 0.4592 | 0.5565 | 0.5000 | 0.4412 |
11 | - | - | - | 0.5714 | 0.4405 | 0.4776 | 0.4109 | 0.5205 | 0.4961 | 0.5000 | 0.5000 | 0.4745 | 0.4722 | 0.3909 | 0.6250 |
12 | - | - | - | - | 0.5179 | 0.4955 | 0.5506 | 0.4196 | 0.5509 | 0.5408 | 0.5255 | 0.5000 | 0.5083 | 0.6538 | 0.4412 |
13 | - | - | - | - | 0.3864 | 0.4863 | 0.5481 | 0.4333 | 0.5135 | 0.4435 | 0.5278 | 0.4917 | 0.5000 | 0.4649 | 0.3235 |
14 | - | - | - | - | - | 0.5714 | 0.4694 | 0.6316 | 0.4561 | 0.5000 | 0.6091 | 0.3462 | 0.5351 | 0.5000 | 0.5833 |
15 | - | - | - | - | - | 0.2895 | 0.5227 | 0.3611 | 0.2708 | 0.5588 | 0.3750 | 0.5588 | 0.6765 | 0.4167 | 0.5000 |
Here we assume that ties are valued at 0.5 wins.
Taking an example from the table, we can see that Deck 8 has a historical win percentage against Deck 10 of ~55%. Likewise, Deck 10 won against Deck 8 ~45% of the tine.
And as expected, each deck has precisely a 50% win probability against itself. A deck playing against itself will either win, lose, or draw, meaning that the opposing deck, itself, has the opposite outcome.
Half of all wins (win = 1), half of all losses (loss = 0), and half of all draws (draw = 0.5) come out to half of all outcomes. Thus, 50% win probability.
Gamma (Γ) & the Gamma Curve
The deck matchup win/loss percentages can serve in helping us determine how much of an edge to assign to competitors that use these decks.
The PlayerRatings package in R (that we’ve been using to calculate Glicko2 scores and predict win/loss probabilities), provides an open variable called gamma (abbreviated Γ).
- Assigning Γ=0 gives neither competitor an edge.
- If Γ<0, the “Player” (vs. “Opponent”) suffers a negative edge, one that subtracts from his or her probability of winning.
- If Γ>0, the “Player” (vs. “Opponent”) gets a positive edge, one that adds to his or her probability of winning.
But how much Γ should be applied between competitors in a given match?
To help answer this, let’s turn to R with the following code:
# Step 1: Load the PlayerRatings package library(PlayerRatings) # Step 2: Set up two competitors with same rating, deviation, and # volatility startrate <- data.frame(Player=c("A", "B"), Rating=c(1500,1500), Deviation=c(350,350), Volatility=c(0.6,0.6)) # Step 3: Set up a match between these two equivalent players samplematch <- data.frame(Period=1, Player="A", Opponent="B", Result=0.5) # Step 4: Determine final ratings for both players in this match samplefinals <- glicko2(samplematch, status=startrate, tau=0.6) # Step 5: Predict the win probabilities for the first player # with gamma from -1000 to 1000 in 0.1 increments gammacurve <- predict(samplefinals, newdata=data.frame(Period=2, Player="A", Opponent="B"), tng=1, gamma=seq(-1000, 1000, 0.1)) # Step 6: Convert output from Step 5 to a data frame (this will be # useful later) as.data.frame(gammacurve)
What we’ve done, highlighted in Step 5, above, is predict the win probability between two evenly matched competitors.
We didn’t do this just once, but 20,001 times.
Each time we’ve used a different Γ, starting at -1000 and building to 1000 in increments of 0.1. In this range is also included Γ=0, which favors neither side.
We can now visualize this on a plot using R and the ggplot2 package.
# Plot the gamma curve from -1000 to 1000 with the ggplot2 package. library(ggplot2) ggplot(data=gammacurve2, mapping=aes(x=Gamma, y=Win_Prob))+geom_line(color="red", linewidth=1.25)+geom_line(data=gammacurve2, mapping=aes(x=0))+labs(title="Gamma & Win Probability", subtitle="NicholasABeaver.com", caption="Note: Assumes both players have identitical Glicko2 Ratings, Deviations, and Volatility.")
We get the following plot:
As expected, Γ=0 does not favor one competitor or another. The lower Γ gets, the more the “opponent” is favored. The higher Γ gets, the more the “player” is favored.
If we look carefully (and think about what Γ is measuring), the win probability can never reach as low as 0 or as high as 1. Γ closer to 0 has a larger effect than Γ farther away from it.
Γ is logarithmic in a way similar to that of Glicko2 ratings. Each increment of Γ close to 0 has a larger effect than the same increment further away from 0, and increasingly (or decreasingly) so, such that Γ can never reach 0 or 1. Just like win probabilities never reach 0 or 1.
We can export this data as a .csv file, which can serve us as a useful table.
To do that in R, we use the following code:
write.csv(gammacurve, "gamma_curve_1_to_1000_by_0.1.csv")
We can see the output .csv file here.
We’ll use this in the next section to illustrate how Γ helps us account for deck-vs-deck quality.
Putting Glicko2 and Deck Gamma Together
Let’s tie win probabilities and deck quality together to illustrate how they work.
We’ll make the following assumptions:
- Player A uses Deck 9
- Player B uses Deck 7
- Both players have Glicko2 Ratings of 1500
- Both players have Deviations (σ) of 350
- Both players have Volatility (τ) of 0.6
Using our Deck Matchup Probabilities table, we can see that Player A’s Deck 9 has a 59.64% probability of beating Player B’s Deck 7, as shown below:
Looking up a Γ = ~0.5964 on out Γ output table from R, we see the following:
For the matchup of these two decks (9 vs. 7), our Γ = 113.5.
We can now use this in the predict function in R, setting “gamma” equal to 82.
predict(samplefinals, newdata=data.frame(Period=2, Player="A", Opponent="B"), tng=1, gamma=113.5)
The out put is:
[1] 0.5964414
This is what we expect, because, both players have exactly the same skill, the same deviation, and the same volatility.
The only variable that is different is the deck in use by either player (Deck 9 vs. Deck 11). Since Deck 9 has a ~59.64% probability against Deck 11, it makes perfect sense that given this matchup, the probability for Player A to beat Player B is ~59.64%. Everything else about the two competitors is the same.
We can carry out this same process for any two competitors using any two decks by doing the following:
- Find the Ratings, deviation (σ), and volatility (τ) for two given players.
- Find the Decks to be used by each player and consult the Deck Matchup Probability Chart for the decks’ win probabilities.
- Use the decks’ win probabilities to consult the Gamma (Γ) Chart and find the correct Γ to apply to the match.
- Set the predict function with the players’ skill details and correct Γ to find the win probability.
This is a somewhat manual process, which could be automated with software.
But this is another important step in our proof-of-concept.
Next, we’ll add some fine-tuning to our basic model, putting it’s various parts together into a cohesive whole.