James Stanley


A Rock-Paper-Scissors AI that is too good

Fri 25 May 2018
Tagged: software, ai

Yesterday I had an idea for a simple Rock-Paper-Scissors AI based on Markov chains. The "state" would be what the opponent played in the last N rounds, and would be used to predict the probability that the opponent would play Rock, Paper, or Scissors next. The AI would choose what to play, to beat what the opponent is expected to play, weighted by the expected probability for each possible action.

I quickly wrote it, and went looking online for an AI to test it against. The top result was from "Afiniti":

You can play against it on their website. You'll lose.

(Update 2018-07-02: Afiniti have now updated their AI, and it now appears totally legit. Good work, Afiniti! To check whether the version you're playing against is legit, you'll have to a.) check in the "Network" tab in developer tools that it's making reasonable-looking requests to smartplay.afiniti.com, that don't include the move the player has played, and b.) make sure that it is actually playing the move that smartplay.afiniti.com tells it to, and c.) for good measure, play 100 random moves against it and verify that the outcome looks believable. The rest of this article is unchanged.)

The Afiniti AI did extremely well against my Markov model. Suspiciously well. The more rounds I played, the better it got. These Afiniti guys obviously have some pretty powerful artificial intelligence!

To get an idea of what I was up against, I tried having my program generate completely random moves. Regardless of how smart your AI is, (and unless you're detecting patterns in the RNG), you can not do better than random chance against an opponent who is picking moves at random.

To my surprise, the Afiniti AI did very well again. Just as well as last time. The more rounds I played, the better the Afiniti AI got at predicting the "random" moves that I would choose.

So at this point I wanted to know exactly what they were up to. The text on the page all looks legit, there's nothing to suggest that the game is a joke or otherwise designed to trick the user, it's presented as a legitimate exercise in artificial intelligence.

I opened the "Network" tab in the Firefox Developer Tools to see if I could observe the web page communicating remotely with their AI. There was no network traffic whatsoever, which implies the AI is all javascript. That's good news, because that means I can find out what it's doing.

Next I downloaded their 1.3 megabytes (!) of minified (!) javascript, and ran it through jsbeautifier, to get 41,650 lines of code to look through. This is too much. (It's the code for their entire website, bundled into one file, not just the Rock-Paper-Scissors AI). I used Firefox Developer Tools again to "inspect element" on the game's input buttons and found that when you click the scissors button, it calls checkWinner("Scissors"). The checkWinner function begins as follows:

this.checkWinner = function(t) {
    e.setState({
        computerImage: "handNone",
        inProgress: !0,
        playerImage: "handNone"
    }), setTimeout(function() {
        var n = null,
            o = null,
            r = e.state.gameResult,
            s = ["Rock", "Paper", "Scissors"],
            l = [2, 2, 2];
        l[s.indexOf(t)] = l[s.indexOf(t)] * (Math.floor(e.state.round / 10) + 1);
        var a = e.getCompGuess(s, l);

It updates the animation states, and then registers a hook to run after a timeout, in which the computer makes its move. Without even understanding anything about how the AI works, the parts I've bolded show that:

1.) the array l is modified based on the value of t (i.e. the player's move).

2.) the array l (which may now contain information about the player's move) is passed to getCompGuess.

Wat.

At this point there's still a chance that the Afiniti AI is legit. The getCompGuess function might completely ignore its second argument, for example. So let's look at getCompGuess:

this.getCompGuess = function(e, l) {
    for (var n = 0, o = 0, r = [], s = 0; s < l.length; s++) o += l[s];
    for (; n < e.length;) {
        for (var l = 0; l < l[n]; l++) r[r.length] = e[n];
        n++
    }
    return r[Math.floor(Math.random() * o)]
}

Seems... short?

You'll notice that no global state seems to be involved in calculating the AI's guess, which is odd, given that it is supposed to learn from what the player does. What actually happens is the array l contains a 2 for the actions the human didn't take (e.g. if you played "Rock", l contains a 2 for each of "Paper" and "Scissors") and contains some value >= 2 for the action the human did take, with the value increasing as the round number increases. getCompGuess then picks from Rock, Paper, and Scissors at random, weighted by the values in the l array.

This means that as the game goes on, the computer correctly "guesses" what action you took (past tense!) this round with increasing probability. Because it already knows what action you took. Because the action you took is the only actual input to the function that is supposed to guess what action you took.

Scam scam scam.

The text on the web page makes this even sneakier: "I correctly predicted you would throw Paper", "SEE HOW THE COMPUTER'S THINKING".

(Incidentally, the text from "SEE HOW THE COMPUTER'S THINKING" is not even consistent with how the AI behaves and is just generated at random. It might say it's planning to throw Rock, but then it throws something else. This is because it has no way to know what it is "planning" to throw until after you have taken your action).

Afiniti's description of their AI is:

This rock-paper-scissors game illustrates the basic principles of an adaptive artificial intelligence technology. Like Afiniti, the system learns to identify patterns of a person’s behavior by analysing their decision strategies in order to predict future behavior.
While a computer won’t win all rounds, over time it can learn a person’s behavioral characteristics to perform better than chance. In a similar way, Afiniti improves on the traditional random process of pairing callers with agents by using behaviour to make better pairings.

(I've bolded all of the outright lies).

And I hope that the Rock Paper Scissors AI is not really an accurate mirror of the way Afiniti's actual product "learns to identify patterns".

There is also a "Leaderboard" on the Afiniti rock-paper-scissors page, but I didn't find any code in their javascript that ever sends user scores off to the server (although I admittedly didn't search all 41,650 lines of it). This along with the handwritten-sounding CSS style names used to import the player pictures implies that the leaderboard is at best maintained manually, and at worst completely fake.

I contacted Afiniti to ask how their AI works about 24 hours ago, but they've not replied. (Update: they have replied; the Chief Marketing Officer claimed that the AI is using legitimate artificial intelligence, and stopped replying when I linked him to the evidence in this post, but their AI has now been removed!).

While researching other Rock Paper Scissors AIs, I came across one at essentially.net. Their AI is 100% server-side, so it's impossible to know whether or not they are cheating. I wrote a script to play 6000 games against their AI, picking moves at random, and it came out about 1/3 win, 1/3 tie, 1/3 lose, so I am happy to believe that they are above board. Good work, essentially.net.

I also came across rpscontest.com, which allows anybody to write an AI in python and upload it to be entered into the tournament. I found a link to an article on Rock Paper Scissors algorithms by Daniel Lawrence in one of the AIs I looked at. He goes into much more detail about how Rock-Paper-Scissors strategies can work than anything I had come up with. (There's lots of other interesting stuff on his site as well, it's definitely worth having a look through his Programming projects).

I did enter my Markov Model AI into rpscontest, but it did quite badly. So far it's played 95 matches, won 47, and is ranked 1300th of 2621. Almost exactly as good as you'd expect if it were simply picking moves at random...



If you like my blog, please consider subscribing to the RSS feed or the mailing list: