Rock Paper Scissors @manc_js

Manc JS

Last night I attended my first Manc JS. If you know me, you know that I like a good hack and the “Rock Paper Scissors Bots” night lived up to expectations. Props to @martinrue.

The game

Each bot played 50 “hands” against each of the other bots. Unlike traditional Rock Paper Scissors, two new entities were introduced:

  • “dynamite”, which beats paper, scissors and rock but can only be used a maximum of 5 times per game
  • “water (balloon)”, which beats dynamite only and can be used any number of times

Bots were comprised of at least 3 functions, “init”, “play” and “result”, which were called by the server running the bots against each other.

My approach

Even the most complex of strategies would struggle to beat a truly random opponent, although the “dynamite” hand mitigated this slightly. Nevertheless, I didn’t want to write any random code, it’s way less fun and more unpredictable. I decided it would be much better to consistently secure the top positions on the leaderboard as opposed to go for a random boom or bust or hit and hope, we are programmers after all.

Sub-bots

Although we were only allowed to play one hand for each turn, my approach was to create multiple sub-bots that would play silently each time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Example sub-bot that always plays "paper"
bot.paperBot = (function () {
var bot = {};
bot.winners = 0;
bot.lastPlay = "";
bot.nextPlay = function () {
var play = "paper";
bot.lastPlay = play;
return play;
};
return bot;
})();

After the result of each hand was returned by the server, the outcome of whether each sub-bot would have won was recorded. Over the 50 hands, using a greedy approach, the sub-bot that was played was the one with the most amount of winners.

Given more time and more sub-bots this approach would have become slightly more robust.

We’re gonna light it up, like it’s dynamite

I played with a few ideas for dynamite, it appeared quite quickly that most bots played dynamite straight after a draw (as points were carried), so initially I played water straight after a draw then dynamite straight after that. This wasn’t a good strategy, as water is a weak hand.

The approach I rested on was to not ever bother playing water, ever, and to play dynamite (if any was remaining) 2 hands after a draw, as this appeared to be the least likely time another bot would play it :)

Obviously, even when dynamite was played, the outcome of whether each sub-bot would have won was still recorded.

Tests

My code was ugly, but it was linted and had some noddy jasmine tests (using grunt and plugins) for my own sanity and so not to waste too many trial runs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Example of my noddy tests
it("should init and set opponent", function() {
init("test");
expect(bot.opponent).toEqual("test");
});
it("should use paper as the first play", function() {
// First play always paper, as all bots have 0 wins
// NB: Should randomise the first bot :)
expect(play()).toEqual("paper");
});
it("should win the first hand against rock and increment winning tally", function() {
result({ test: "rock", lee: "paper", winner: "lee", draw: false });
expect(bot.paperBot.winners).toEqual(1);
});

Result

Meh, I came 2nd. Not bad out of 17 I guess ;)

I wish we had more time to play. Maybe this could become an ongoing challenge..?