Let’s start with a simple example, flipping a coin. When a fair coin is tossed, it can be assumed that the probability of getting heads or tails is equal (in reality, it isn’t, because it depends on the way you flip it, the gravitational field strength, how far it falls before landing etc). A way of simulating this in DM would be to just say
var/flip = rand(0,1) //you can treat 1 as heads and 0 as tails
But what if we want to cheat our player? What if we want heads to be more likely than tails? Let me introduce my good friend, the prob() function. Let’s start by making the probability of getting heads 60% and, therefore, the probability of getting tails 40%. Well, this is just a simple matter of understanding the syntax and requires hardly any explanation:
var/flip = pick (
prob(60)
1,
prob(40)
0,
)
Not exactly riveting stuff, is it? Let’s move on to more complicated situations. Suppose we’re playing a board game where we roll a 6-sided cubical die to decide how many spaces we can move. So if I roll a 3, I can move 3 spaces. Let’s say there’s computer player (that’s us) and one other player (unaware what he’s got himself into). As programmers, we want our computer player to win because we’re evil. But how do we rig our die so that it’s more likely we’ll win? Well, the obvious solution would be
var/diceroll = 6
This would ensure we always roll a 6 as there’s no pick()ing involved! But this is too obvious and players would see through it. So how should we assign our probabilities so it’s not too obvious but we still give ourselves a nice advantage? The answer is to adjust the expectation of our random variable (the random variable being the number shown when the die is rolled). Readers who are already familiar with expectations need not read this, but for those who aren’t, it will only take a few minutes to familiarise yourself with it. The expectation of a random variable means what, on average, we expect it to return. If we were to roll a fair die over and over again and then took an average of all our results, we would expect this to be 3.5. Here’s a quick way for you to check that:
mob/verb/Stats()
var/diceroll
var/total
for(var/i=1, i<= 1000000, i++)
diceroll = rand(1,6)
total += diceroll
usr << "[total/1000000]"
That “rolls” a die 1,000,000 times and then works out the average of each roll. It should come out to be roughly 3.5. This is known as the expectation and written as E(X) where X = the number rolled by a fair die. The way of working out the expectation of discrete random variables (that is, random variables which can only take a certain value – for example, you can’t roll a 2.7 on a cubical die, can you?) is to write out what’s known as the probability density function (PDF). For a fair die, it would look like this:
For a fair coin, it would look like this
For the biased coin generated above, it would look this
An important thing to note about the PDF is that every outcome must be defined and, therefore, the probabilities have to add up to 1.
To work out the expectation, we just multiply the x value with its associated P(X=x) value. So for the die, it would be:
E(X) = 1 * 1/6 + 2 * 1/6 + 3 * 1/6 + 4 * 1/6 + 5 * 1/6 + 6 * 1/6 = 3.5 as demonstrated by the code above.
For this PDF
E(X) = 2*0.1 + 6*0.57 + 10 * 0.33 = 6.92
Let’s recall what we mean by this. If we had a random process which selected the number 2 with probability 0.1, the number 6 with probability 0.57 and the number 10 with probability 0.33, then over a large number of trials, on average, we’d expect to get 6.92. Don’t believe me? Try it:
mob/verb/Stats()
var/randvariable
var/total
for(var/i=1, i<= 1000000, i++)
randvariable = pick(
prob(10)
2,
prob(57)
6,
prob(33)
10,
)
total += randvariable
usr << "[total/1000000]"
That should give you 6.92.
Right then, so back to our problem. We want the computer player to win the board game. If we were to roll a fair die, as demonstrated above, its expectation would be 3.5. In other words, we’d expect to move 3.5 places on our board on average. Let’s try to up its expectation to 4.5. Recall that
E(X) = 1*P(X=1) + 2*P(X=2) + 3*P(X=3) + 4*P(X=4) + 5*P(X=5) + 6*P(X=6).
We’ve chosen this to be 4.5. So we have
4.5 = 1*P(X=1) + 2*P(X=2) + 3*P(X=3) + 4*P(X=4) + 5*P(X=5) + 6*P(X=6).
4.5 = P(X=1) + 2P(X=2) + 3P(X=3) + 4P(X=4) + 5P(X=5) + 6P(X=6)
Now then, we need to decide on its PDF. We can essentially just choose whatever numbers we want, so long as the probabilities add up to 1. To make the player think we’re not cheating, we’ll make it somewhat likely that we can get a 1. This means we should roll a fair amount of 1s, but as our expectation is high, in the end, we will end up winning. We’ll also make the probability of getting a 6 not too high so it’s not obvious that it’s rigged. Let’s say P(X=1) = 0.1, P(X=2) = 0.05 and P(X=6) = 0.25. Now we have
4.5 = P(X=1) + 2P(X=2) + 3P(X=3) + 4P(X=4) + 5P(X=5) + 6P(X=6)
4.5 = 0.1 + 2*0.05 + 3P(X=3) + 4P(X=4) + 5P(X=5) + 6*0.25
4.5 = 0.1 + 0.1 + 3P(X=3) + 4P(X=4) + 5P(X=5) + 1.5
2.8 = 3P(X=3) + 4P(X=4) + 5P(X=5)
Remembering that the probabilities have to add up to 1, we note that P(X=3) + P(X=4) + P(X=5) must now equal 0.6. Next we have to make sure we actually cover all of the expectation. This will means making P(X=5) and P(X=4) quite high. Let’s try P(X=5) = 0.425 and P(X=4) = 0.15.
2.8 = 3P(X=3) + 4*0.15 + 5*0.425
2.8 = 3P(X=3) + 0.6 + 2.125
0.075 = 3P(X=3)
0.025= P(X=3)
And so we’re done. We can write the PDF as:
You can run a test on DM to check the expectation is working:
mob/verb/Stats()
var/dieroll
var/total
for(var/i=1, i<= 1000000, i++)
dieroll = pick(
prob(10)
1,
prob(5)
2,
prob(2.5)
3,
prob(15)
4,
prob(42.5)
5,
prob(25)
6,
)
total += dieroll
usr << "[total/1000000]"
This will make it more likely that our computer player will win than if he was using a fair die. The good thing about this is that we can adjust the expectation to whatever we like quite simply. 4.5 was just an example. We could even disadvantage someone by reducing the expectation to, say, 3.
Another method of giving the computer and advantage is to guess what the player will do. Let’s suppose we’re playing a game in which both the player and the computer player guess a number; either 1, 2 or 3. If the numbers differ by 2, the person who picked the largest number wins £2 from their opponent. If the numbers differ by 1, the person who picked the smallest number wins £1 from their opponent. If the numbers are equal, nothing happens. We want to try to maximise our winnings. Let’s predict that the player will pick his number randomly (in other words, we’ll assume it’s equally likely for the player to pick 1, 2 or 3). This means that the probability the players picks any number is 1/3. We now need to come up with a process to pick our number given this information. So, for example, we could just mimic the player and do this:
mob/proc/Guess()
var/mynum = rand(1,3)
But how can we create a PDF that maximises our winnings? Let’s start by defining it:
We’re trying to work out values for a, b and c so that we maximise our winnings.
Let’s say the player had picked 1:
If we picked 1, we’d make £0.
If we picked 2, we’d make -£1
If we picked 3, we’d make £2
The probability of the computer picking 1 is a, picking 2 is b and picking 3 is c. So we can say that, if the player picks 1, the expectation is
E(winnings _when_player_picks_1) = 0a – 1b + 2c = 2c – b
Likewise, we find that
Let’s say the player had picked 2:
If we picked 1, we’d make £1.
If we picked 2, we’d make £0.
If we picked 3, we’d make -£1.
Then E(winnings _when_player_picks_2) = 1a + 0b – 2c = a – c
Let’s say the player had picked 3:
If we picked 1, we’d make £-2.
If we picked 2, we’d make £1.
If we picked 3, we’d make £0
Then E(winnings _when_player_picks_2) = -2a + 1b + 0c = b – 2a
We’re almost there now. We have to also take into account that there’s only a chance that the player will pick, say, 2. The probability of the player picking 2 is 1/3, so we multiply E(winnings _when_player_picks_2) by 1/3 to give E(winnings _when_player_picks_2)/3. Now we can work out our expected winnings:
E(winnings) = E(winnings_when_player_picks_1)/3 + E(winnings _when_player_picks_2)/3 + E(winnings _when_player_picks_3)/3
E(winnings) = (E(winnings_when_player_picks_1) + E(winnings _when_player_picks_2) + E(winnings _when_player_picks_3))/3
E(winnings) = (2c – b + a – c + b – 2a)/3
E(winnings) = (c – a)/3
And so we find that our expected winnings actually have nothing to do with the probability that we pick 2. To maximise the above expression, we can just have c = 1 and a = 0 (which in turn means b = 0 because a + b + c = 1 for a PDF). This results in the following PDF
After a large number of games, if we chose our probabilities like that, we’d expect to have won £0.33. It is left as a task to the reader to construct a program to test this out.