Lady luck: a web based Vassal X-Wing stats calculator

Han Solo: Oh yeah, well I call it luck

Obi-Wan: In my experience, Captain Solo, there’s no such thing as mere luck.

Back in October Paul Heaver threw together a little program during his lunch time break that, when given a Vassal chat log, counts up the number of hits, crits, blanks, etc that happened in the game.  Here was the basic output:

System lines: 62
Player Paul Blanks 9 Focuses 11 Hits 22 Crits 17
Defense  Blanks 8 Focuses 3 Evades 8
Other player Blanks 9 Focuses 9 Hits 18 Crits 2
Defense  Blanks 9 Focuses 8 Evades 15

I thought that was awesome – a way to quantify X-Wing luck! – and had some thoughts on how to extend the idea.  My first thought was to simply to put in the expected versus actual values to see how much better/worse your luck versus what was expected.  Paul sent me his source code, which I ended up re-writing as a little python script to output something like: 

Player KelvanTiberius
Total rolls: 69
Hits: 22 (Expected: 25.875)
Blanks: 18 (Expected: 17.25)
Eyes: 21 (Expected: 17.25)
Crits: 8 (Expected: 8.625)
Total rolls: 23
Evades: 5 (Expected: 8.625)
Blanks: 9 (Expected: 8.625)
Eyes: 9 (Expected: 5.75)

Player Val
Total rolls: 46
Hits: 19 (Expected: 17.25)
Blanks: 14 (Expected: 11.5)
Eyes: 9 (Expected: 11.5)
Crits: 4 (Expected: 5.75)
Total rolls: 72
Evades: 24 (Expected: 27.0)
Blanks: 28 (Expected: 27.0)
Eyes: 20 (Expected: 18.0)

It was a pretty simple change that got me thinking down two different avenues: first, it would be nice to open up the program as a little web service so people could easily run and share stats on their own games.  Second, wouldn’t it be sweet to actually see a player’s luck progress on a turn-by-turn basis ?  Then I could see the game’s luck swinging around, informing where some critical parts of them game happened.  (Who hasn’t experienced a moment in an X-Wing game where you felt the entire game hinged on a single lucky roll?)

So I mocked up a spreadsheet on google docs and asked some of the math-ier residents on this site to ponder it with me (thanks fuamatu, jake p, theorist, and mu0n for your thoughts!).  We ended up deriving a mechanism for calculating luck on a attack-set by attack-set basis (where an attack set is the first player attacks, and the second player defends) that I then re-implemented in Python.  I plotted the results using some nice line plotting modules (matplotlib), wrapped the whole thing in a web service (flask + PythonAnywhere), stashed everything in an open source code repository (github), and now present to you: version 0.1 of the the Lady Luck website!


In the last section of this post I’ll talk about the basic math behind the luck calculations, but let’s jump right in and talk about the site itself.

If you want to enter a game, just head over to  You’ll see a text area to enter your game into:

In Vassal select all of the text in the chat window (Control-A and then Control-C on Windows, or Command-A and then Command-C on a mac) and then paste it into this text area.  Paste it in (Control/Command V) and click the ‘Generate stats’ button.  You can also put in which player won the game, if you like.

Some notes on the chat log:

  • The latest version of Vassal X-Wing allows you to flip your focuses into hits or evades.  If you want your stats to be correct, use that feature.
  • Similarly, if your opponent rolls a bunch of hits, and you don’t bother rolling evades (because it destroys your ship, for example), than your stats will be ‘worse’.  So if you want accurate stats, always roll those evade dice.
  • If you run through an asteroid, or do any action that requires rolling a single attack dice, those results will be applied as a positive value to your opponent.
  • Your initiative rolls in the beginning of the log file will be incorporated in, unless you delete them manually from the log.

You’ll then be redirected to the stats for your page:


That’s it!  You can share your game stats by sending the url of the stats to anyone.  Alternatively, if you click on Browse Games, you can browse all the games submitted to date.


“Without precise calculations, we could fly right through a star or bounce too close to a supernova and that’d end your trip real quick, wouldn’t it?” –Han Solo

The basic method for calculating ‘luck’ is mocked up in this google docs spreadsheet.  What follows here is a narrative description of the technique.

Consider an eight-sided red attack dice: it has 3 “hit” faces, 1 “crit” face, 2 “focus” faces, and 2 “blank” faces.  Thus, if you roll a dice, the chances of getting any particular face are: 

  • Probability of Hit = P(hit) = 3  / 8 = .375
  • Probability of Crit = P(crit) =  1 / 8 = .125
  • Probability of Focus = P(focus) =  2 / 8 = .25
  • Probability of Blank = P(blank) =  2 / 8 = .25

When I roll a dice, and get a hit, then the number of hits I’ve made more than expected is 1 hit – P(hit) = 1 – .375 = .625.  On the other hand, I didn’t roll a crit, and so the number of crits I’ve made more than expected is 0 crits – P(crit) = 0 – .125 = -.125.  Similarly, since I didn’t roll a focus or a blank, the number of focuses and blanks I’ve rolled versus expected is -.25 and and -.25.  Intuitively, the .625 “better than expected” is dragged down to zero by the other three values.   This would surprise you  if you didn’t know the relative values of X-Wing dice – that is, that crits are generally better than hits, and hits better than focuses, and focuses better than blanks.

What I’d like to say is that “Here, I’ve rolled a hit, and that is worth something, even when subtracting the expected values of the other rolls I didn’t make.”

To get this you need to assign relative weights to the different dice faces.  After some experimenting Fuamatu and I decided upon:

  • Hit weight = W(hit) = 1
  • Crit weight  W(crit) = 1.25
  • Focus weight = W(focus) = .5
  • Blank weight = W(blank) = 0

Now the calculation of a value of a hit is different.  The number of hits I’ve made more than expected is now W(hit) * (1-P(hit) = 1 * (1-.375) = .625.  That’s the same value as before.  But now the weights of the crit, focus, and blank become smaller; for the crit, its now W(crit) * (0-P(crit)) =1.25 * -.125 = =.15625.  The focus is taken from -.25 to -.125, and the blank is driven to zero.  Thus the new value of the hit becomes:

.625 – .15625 – .125 – 0 = 0.34375

Doing the calculation for the rest of the dice types (cells C17 to F17 in the google spreadsheet) yields the following “luck” values for the different dice types:

  • Hit luck value: .34375
  • Crit luck value: .59375
  • Focus luck value: -0.15625
  • Blank luck value: -.65625

These values let us calculate the value of rolling, say, three hits in a row ( .34375 x 3 == 1.03125 ), or three blanks in a row (-.65625 x 3 == -1.96875 ).  If you were to roll three hits and then three blanks, then your cumulative luck would be 1.03125 – 1.96875 == -0.9375.

The same mechanism applies for the green dice.  With weights of 1 for the evade dice, .75 for focus, and 0 for blanks, the luck values end up being:

  • Evade luck value: .4375
  • Focus luck value: .1875
  • Blank luck value:  -.5625

With the values of both types of dice in place, we can now read the game “tape” and assign cumulative luck stats.  This is done on an attack-set by attack-set basis, where an attack-set is one player attacking and the other player defending.  By netting out the first player’s attack luck against the second player’s defense luck, we can track the relative advantage on a set by set basis.  

From this data multiple interesting views can be derived.  Here’s one:

Chess games use this sort of view (x-axis is moves, y-axis is who is the advantage).  This is from a game between our current National and World Champions, Jake and Paul.  In the first 15 attack sets, Paul (in green) held a small net cumulative advantage, always less then -2.  Then, around move 15, Jake’s luck took over, and never let go, peaking out at +8.

Here’s another:

This graph shows how much un-cancelled damage was dealt out on an attack set by attack set basis.  

And here’s the final two:


This is Jake’s attack versus Paul’s defense.  The net “luck” value is achieved simply by subtracting out defense from attack on a set by set basis.  Here, Paul’s green dice started out pretty decent, but between turns 10 and 14 Jake’s luck got very hot.  Without having gone over the game on Vassal, one could conjecture that these four attack sets were critical to the eventual result.  (Jake won this one.)

The final one:

Same view as before, but from Paul’s perspective.

That’s it!  If you have any feature ideas, bugs, etc please feel free to drop them in the comments section below, or email them over to me at sozinsky at gmail dot com.  I’m calling this a 0.1 beta release in case there are bugs/serious issues, so please pardon the dust a bit in case there are 🙂