David (dblume) wrote,
David
dblume

"Random" Notes

I carpool with a friend in a nearby building to the climbing gym. To decide who drives, we play email roshambo. The HTML code for the Rock/Paper/Scissors choice is presented below:

<SELECT name="p_throw">
<OPTION value="r">Rock</OPTION>
<OPTION value="p">Paper</OPTION>
<OPTION value="s">Scissors</OPTION>
</SELECT>

We've done this for years, and we each end up driving about 50% of the time, with a few short streaks breaking up the routine. I decided it was too much effort to have to actually choose rock, paper or scissors if I didn't want to. It'd be nice for the website to randomly suggest one for me.

So I added the following PHP code:

$suggest = rand(0, 2);
<SELECT name="p_throw">
<OPTION <? if ($suggest == 0) echo "SELECTED "; ?>value="r">Rock</OPTION>
<OPTION <? if ($suggest == 1) echo "SELECTED "; ?>value="p">Paper</OPTION>
<OPTION <? if ($suggest == 2) echo "SELECTED "; ?>value="s">Scissors</OPTION>
</SELECT>

There, now the web page will suggest a random throw when it loads. How convenient!

Except that my friend started destroying me in our challenges. From December 2009 through March 2010, he began winning over 75% of our challenges, and I had to keep driving the carpool. This was costing me money!

It turns out that while I was using the suggested random throw, he had a different strategy. He considered his suggested throw, and made the throw that would beat it.

So there was a correlation between the random throw suggested for me, and the one suggested for him! Even though our suggestions are generated from different pages (mine from index.php because I start the challenge, and his from throw.php because he responds to a challenge), the random number generator runs on the same server.

It's just not random enough, and it exposed an exploit that was costing me money!

There's a simple fix for this, use mt_rand() instead of rand(). So I made the following change:

$suggest = mt_rand(0, 2);
Although I made the change to mt_rand(), the fact that the pseudo-random numbers were being made by the same physical generator bothered me. I decided that instead of generating the suggested throw on the server, it'd be best to generate the suggestion at the client computer, in Javascript. So I wrote the following code and deployed that:

function Set_suggested_throw()
{
var randomnumber = Math.floor( Math.random() * 3 )
document.rpsform.p_throw.selectedIndex=randomnumber
}
</head>
<body onLoad="Set_suggested_throw()">
That's better. Now his random suggestion is generated on an entirely different machine than mine...

Except it bothered me that both random numbers were seeded in a similar fashion, and there'd often be a constant offset between the localtime and uptimes of both machines. This wouldn't do. I needed better randomness. Luckly, there's a site for that.

Great! I'll have my Javascript make a quick call to random.org to make the suggestion.

function Set_suggested_throw()
{
var xmlhttp = null;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
if ( typeof xmlhttp.overrideMimeType != 'undefined') {
xmlhttp.overrideMimeType('text/xml');
}
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// Only if quota not exceeded. See http://www.random.org/clients/
xmlhttp.open( 'GET',
'/rnd_org/integers/?num=1&min=0&max=2&col=1&base=10&format=plain&rnd=new',
true );
xmlhttp.send( null );
xmlhttp.onreadystatechange = function() {
if ( this.readyState == 4 && this.status == 200 ) {
document.contactform.p_throw.selectedIndex=this.responseText
}
}

}
Making a call to random.org from a page generated at rps.dlma.com runs afoul of the Same Origin Policy. My server is a Linux server, so all I have to do is add a ProxyPass to my httpd.conf and restart it.

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

ProxyPass /rnd_org http://www.random.org

A downside is that the proxy content can be cached, so I'd have to be sure to disable that.

Even worse is that my server is a shared server running at DreamHost, and I don't have access to httpd.conf. And one can't specify a ProxyPass in a .htaccess file either.

So the work-around is to have the PHP code make the call to random.org. Great! Just code that sucker up, ensure that we don't spam random.org by checking our quotas, and falling back on the Javascript implementation if we do go over quota. I won't show you all that, just the PHP snippet.

$ctx = stream_context_create( array( 'http' => array( 'timeout' => 5 ) ) );
$url = "http://www.random.org/integers/?num=1&min=0&max=2&col=1&base=10&format=plain";
$result=file_get_contents( $url, 0, $ctx );
if ( !is_bool( $result ) || $result != false ) {
// set $suggest }
Great! The next time we played Email Roshambo, I won! Phew! As he drove me to the gym, I asked him how he'd been since I last saw him.

He said, "Oh, I've had better Fridays. I was RIFfed. We won't be carpooling anymore."
Tags: code, geek, programming
Subscribe

  • Progress on my Google+ and LJ backups

    Since Google is going to shut down Google+, I decided it was time to really make a home for my LiveJournal backup and my Google+ backup. Working…

  • Getting Shit Done

    I came across an old LifeHacker article Get Shit Done Blocks Distracting Web Sites So You Can Do As the Name Instructs, that mentions a productivity…

  • Fifth Grade Homework - Nine digit pandigital prime number

    Yesterday my daughter in the fifth grade got the following homework assignment "arrange the digits one through nine into a nine-digit prime number."…

  • Post a new comment

    Error

    Comments allowed for friends only

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 2 comments