Sunday, September 30, 2012

Pick a Number, Any Number... So Long As It's Four

Just for the fun of it, today I went back and played Tepiiku, a dice game I reviewed way back when for JIG. It's an interesting game for three players (you and two AI) where the player with the lowest-scoring hand has the obligation to either pay the highest-scoring player a forfeit, or attempt to increase their hand's value at the risk of paying double the forfeit for failing. There's a major flaw in how the wagering plays out, since the game will not allow you to attempt to reroll if you can't pay the potential double forfeit; sometimes your opponents will boost the stake between them, and by the time it passes to you, you're forced to pay up without ever getting to play. Sure, it could result in interesting tactics if you could find a way to work it to your advantage, but more often than not, it's just frustrating.

That was a flaw I pointed out in my review, and it was echoed by many of the commenters. Later on, the creator of the game stopped by to acknowledge the flaw and explain his reasoning for it. Which is awesome. I love it when the developer stops by to discuss the development of their game.

But that's not what this post is about. This post was sparked by what was the very first comment left to the game, by an ever-so-popular visitor named Anonymous.

"Mmm-hmmm. Just like I thought. Just like every other dice and card game in existence, apparently, once you get out of the practice round, it's absolutely nothing but consistent crap for you, and perfect hands/rolls for all AI players, every single hand. No thanks. How about at least faking some random fairness for once, developers? Maybe?"

The developer addressed this comment by assuring Mr. 'mous that the dice rolls are "fair and random" and that there were no shenanigans at hand. Streaks of bad luck, perhaps, but no foul play. That didn't stop 'mous from insisting that the game was clearly still rigged, and even going so far as to accuse the developer of ignoring him and the "evidence" that the game is rigged.

This, for reasons I don't understand, is a trend in a lot of dice games I review or see reviewed; the game is cuh-learly rigged because the player doesn't win once or twice. It happened with Tepiiku. It happened with Pigalator 2k5; the developer also addressed complaints about randomness there (but they still continued). It happened with Zilch, and the developer not only addressed the griefers but also released the code for the random number generator. Is it so hard to accept that streaks of bad luck happen, and that hopefully you'll hit a good streak if you keep playing? Or, heaven forbid, could you just not be good at the game?

-----

Bear with me on this sudden change of topic: Last week, I started working my way through Codecademy, a website that takes you through the basics of coding in HTML, JavaScript, Python, and other programming languages. I'm obviously not a coding genius, but if my experiences with this website and SpaceChem are anything to go by, I might be a bit of a programming addict. I'm about a third of the way through the JavaScript Fundamentals track, which, if I am to take the authors' interpretations of "next week we'll talk about..." literally, is supposed to be a 26-week course... Whoops.

I'm obviously not a coding genius, and I don't know how much JavaScript goes into programming Flash games, but I've picked up a few things from the course. In the lessons I've had so far, I've learned the basics for simple random events, such as rolling a die or picking a card. In fact, here's what rolling a die looks like:

var roll = Math.floor(Math.random()*6+1);

To break that down:
  • Starting inside the parentheses, Math.random() generates a random number between 0 and 1. I don't know how specific the number is, but in the printouts I've tried, it's generally extended out to something like twelve decimal places, like 0.728027699732 (and yes, I did pull that number from random.org).
  • Since there are six sides on our die, we multiply that random value by six. That will give us something between 0.000000000006 and 5.999999999994. Our random value is now 4.368166198392.
  • Dice are usually labelled with the numbers 1-6, not 0-5, so we add one to our result. Now we've got 5.368166198392.
  • But what sort of die gives us a decimal as a roll? Math.floor() rounds down the value to the highest whole number below our value, which is 5. So there you have it, I just rolled a 5.

It's a long explanation, but that one line of code is all it takes to roll a six-sided die. (Well, you'd also add console.log(roll); to print out the number.) But can you imagine the coding it would take to rig a die so it rolls one number more frequently than another, for example, more low numbers than high? Walk with me through this idea.

  • In theory, while you'd be visually displaying a die with six sides, you'd probably program it as something with more than six sides, with extra sides representing more instances of the numbers you want to occur more frequently. We'll keep it simple by secretly rolling a 10-sided die, where the numbers 7-10 give more weight to outcomes of 1 or 2.
  • We could start out with a similar line of code to the one above and substitute one number: var rollStack = Math.floor(Math.random()*10+1);
  • On a regular roll of the dice, we would just return the value of roll as a result. But since we're stacking the deck, we need to add more coding. We need to add an if/else function statement that checks if our result is 7-10 and tweak it so it gives us a 1 or 2. (This was implemented in an exercise involving making a blackjack game, such that jacks, queens, and kings, with the values 11-13, still return a value of 10.) In the end, you'll end up with something like this:

var rollStack = function() {
var die = Math.floor(Math.random()*10+1);
return die;};
var die = rollStack();
var getValue = function(die) {
if(die===7 || die===8 || die===9)
{return 1;}
else if(die===10)
{return 2;}
else
{return die;}};


Slap a console.log(getValue(die)); on the end, and you've got yourself a stacked die that gives 1s and 2s over half of the time. I've got to confess to you though, the above coding took me about two hours to figure out, and I ended up having to go back to the blackjack exercise and lift a decent portion of the code from there. In the end, by my writing (I'm sure someone could probably reduce what I've done down by a couple lines), it takes you about a dozen lines of code to rig a die, and a slender single line to play with a fair die.

-----

I went through all that coding hell to illustrate this point: Coding a fair die is so much easier than coding a stacked die. To think that someone would intentionally suffer through coding a stacked die to make a game unfair, rather than using a simple fair die, is just stupid. If anything, I would hope that if someone wrote a game with a stacked die, they would wear it as a badge to show you how much work they went through (like something by RRRRThats5Rs, or The Binding of Isaac). Plus, people would flock to it in greater numbers knowing that it's a deliberately unfair game against them.

Coding a cheating game isn't logical. Next time you play a game that "seems to hate you," just accept it: You suck. But you can change that! Play some more. Develop new strategies. Find out how other people beat the game. It's entirely possible that you might've had bad luck once or twice or twenty times, but automatically blaming that on the game being rigged is narcissistic lunacy. After all, if you won every time, it wouldn't be much of a game, would it?

-----

Edit: Well, that didn't take long. As zxo points out in the comments, all it would take to change the fair die roll to a stacked die roll is to square the value of the random number, giving much greater weight to lower numbers. He suggests it could be done by adding two characters (I'd imagine ^2), but the shortest addition I can find is Math.pow(). So all of a sudden, my brick of stacked code looks like this:

var roll = Math.floor(Math.pow(Math.random(),2)*6+1);

And my entire argument has already been obliterated. Not that it was even a strong argument to begin with, I confess. But can we at least agree that people who complain about random number generators need to shut up?

2 comments:

zxo said...

Yeah, the people who make those complaints don't really understand statistics. But at the same time, I don't think the argument that it's harder to code weighted dice really carries much weight. In order to make even a barely competent AI, a programmer would need to write decision-making code which is much more complicated than the weighted dice, mathematically anyway.

Actually, your fair die code can become a stacked die code in as little as 2 additional characters. Just square the random number before you multiply it. Or square root, or cube or take it to any power -- higher powers favor higher rolls more strongly and lower ones favor low numbers.

Also, I'm glad someone else remembers RRRRthats5Rs.

Stephen Lewis said...

CURSE YOU AND YOUR DOCTORISHNESS! I've added your suggestion about squaring the die and altogether admitted defeat in the edit.