I recently wrote a post on probabilities in the game “XCOM: Enemy Unknown”, in particular for the rapid fire ability. Lots of the discussion around the article (and XCOM in general) was about randomness, and pseudo-randomness. There’s an interesting post to be written about subjective observation of probability, but I’m interested in a practice known as save-scumming: repeatedly reloading the same saved game until you achieve your desired result (especially in a luck-based game). To do that, I first need to explain a bit about how randomness in games work.
I’m Soooo Random!
To implement a game like XCOM with a chance of a shot hitting, you can use a random number generator. If the shot has a 63% chance to hit, you generate a random number from 1-100, and if it’s less than or equal to 63, it’s a hit (this way, 63 of the 100 numbers will correspond to a hit). In the real world, you might roll a 100-sided die. But computers can’t roll actual dice, so they either need to use a hardware random number generator, e.g. measuring electrical noise inside the machine, or use a pseudo random-generator (PRNG). A PRNG has a current state from which it can generate the next “random” number. If you know the state, then by definition you can know the next number, but otherwise it appears random to you. For the purposes of games, a PRNG with unknown state is observably just as random as a hardware generator.
So then: implementing random shots is easy. Initialise your PRNG’s state using the time when the game starts, then when the user chooses to fire a shot, generate the next random number and decide whether the shot hits. But maybe the shot misses: the chance was 85% but you generated 93, meaning the shot misses. The player is annoyed: they wanted the shot to hit! So they have an idea: reload the game and try again. The PRNG state was altered by generating the number, so the next one is different: 41. A hit! The player is happy this time, and plays on. If they save before each shot, and are perseverant enough, they can now make any shot in the game a hit. It’s cheesy, but as players we’ve probably all done similar at some point. This technique is known as save-scumming.
So what can the designer do? I think the only perfect solution is to disbar reloading: the Ironman mode in XCOM has one save file per game, which is overwritten every time anything happens in the game. That’s probably the best solution, but it has downsides: accidental clicks in the game (I’m looking at you, XCOM’s wonky camera control) are permanent, corrupted save files are now hellish, and some players find Ironman too restrictive. In the rest of the post, I’ll explain alternate solutions.
Remember that the next random number is dependent solely on the PRNG state. The reload “exploit” works because the PRNG state is different every time the player reloads, so the shot result differs every time you reload. But it doesn’t have to: what if you save the PRNG state in the save file? That way, every time you reload and take the shot, the same next “random” number will be generated, meaning that reloading is futile. This is using the property of the PRNG in our favour: by taking advantage of the hidden determinism of the PRNG, we can make sure that a reload doesn’t affect the result of the shot. So that seems like a solution to save-scumming: save the PRNG state.
XCOM definitely saves the PRNG state. I have a save file in the first mission on impossible, with one soldier able to take a 45% shot — every time I reload it and fire that shot, it hits:
Unfortunately, our solution isn’t as sound as we would like. The PRNG state is saved in the save game file, and next time a random number is generated, it will be,say, 93, and after that will come 32. The player doesn’t know this explicitly, but they do have the ability to reload. Inevitably, the player notices that every time they load that same game, their 85% shot misses. So they reload the game, and try a shot with a different soldier, who has a 65% chance. The 93 is secretly generated: still a miss. Frustrated, the player tries the original 85% soldier again: this time, the 32 is generated — a hit! So the player gets the idea that the first shot they make seems destined to miss, but the second will hit. So they reload the game, take the first shot with a spare soldier who’s far away, then take the second shot with their most powerful soldier. So our supposed solution, saving the PRNG state, won’t completely protect against reloading.
XCOM has this problem. In my save game, I have two soldiers with 45% shots that always hit if I take that shot first. With my other two soldiers, I can move to take a 42%, a 55% or 60% shot that will also always hit. However, several 25% shots, a 32%, a 38% and a 40% shot always miss when taken as the first shot. Therefore, my next random number from the save game generator is either 41 or 42. Scientific save-scumming:
What can be done about the reload-and-try-someone-else problem? One solution that comes to mind is to individualise the odds. To avoid letting the order of attempted shots matter, at the start of each turn, you could secretly assign a random number to each soldier (for each type of shot). So the 85% soldier gets assigned 93 in secret, and no matter which order you make your moves, that soldier will always miss on an 85% chance. So the player loads up the game, tries the shot… and misses. They reload, miss, reload, miss, and pretty quickly they realise that this shot is literally destined to miss. So they don’t make that shot: they move the soldier into hiding and try a different shot with a different soldier. With a bit of patience, the player can know exactly which shots will hit and which won’t. This effectively removes all randomness from the game, and replaces it with a sort of deductive meta-game where the player has to figure out which shots hit, which miss, and plan accordingly. It might be sort of fun, but it’s not the game that the designer intended!
XCOM does not do this. If I shoot once or twice with other soldiers and then try my 45% shot (which always hits as the first shot), it misses. Also, it matters whether the first shot hits or misses: a hit presumably consumes two random numbers from the generator (one for hitting, one to decide whether it’s a critical hit), whereas a miss consumes one number (for hitting). If I make a hit with the first shot, the result of a given second shot is always the same — but it is different if I miss with the first shot.
Mitigating The Problem
I’m not sure the reload-and-try-someone-else problem can be completely solved (except by banning reloading) without another exploit popping up. Two mitigations spring to mind. One is to make reloading annoying enough that the player won’t do it so much (more splash screens! annoying voiceover every time!). Hmmm, not ideal.
The other mitigation is to individualise the random chance. Remember that our original algorithm was to generate a number and see if it’s less than or equal to our percentage probability. If you generate a 97 as the first number, then pretty much every shot will miss, and the player can easily detect this.
Instead, for each shot, you can decide a set of numbers (for the programmers: e.g. by hashing the player and turn number) that will count as a success. Let’s make this simpler and say each shot has a chance out of 10. So one shot might have a 4/10 chance. Instead of generating a number from 1-10 and seeing if it’s less than or equal to 4, pick 4 numbers out of 10 for that shot: say, 1, 6, 8, 9. If any of those come up, it’s a hit. Meanwhile, another shot with a 7/10 chance, will be a hit on 1, 2, 4, 5, 6, 9, 10. If the next number is an 8, the 4/10 shot will hit, but the 7/10 will miss. Thus from the player’s point of view, the order of shots can still affect which ones miss and which ones hit, but not quite as simply as before (i.e. the first shot can’t always be destined to hit or to miss). This would make save-scumming harder.
I’m fairly certain XCOM doesn’t do this, based on the analysis above. I have five shots above 42% which all hit when taken as the first shot, and about seven below 40% which always miss as the first shot — the chances of this being the case with the above mitigation would be incredibly slim.
So, save-scumming is a hard problem to fix, without Ironman. Even without reverse engineering the code or the save game file, we were able to work out what must be happening under the hood, and with unlimited reload, it’s possible to exploit this. But ultimately, players can do what they like with their single-player game — I grew up playing the original XCOM (well, TFTD) by borrowing someone else’s hack of ASCII-editing the save game file to give myself piles of money. Those were the days…