Creating a Bot for the Rock Paper Azure Contest
Posted by: Steven Smith,
on 18 Apr 2011 |
View original | Bookmarked: 0 time(s)
The Windows Azure team is holding a contest, and The Code Project is helping to support it. As part of this contest, you can get free Azure compute time by signing up with the code CP001 here. The contest is simple Rock, Paper, Scissors, with bots, and with 2 new moves: Dynamite, which beats Rock, Paper, or Scissors, and Water Balloon, which beats Dynamite. The catch: you only have a limited supply of dynamite.
Get Started Here:

My bot placed 4th out of about 50 in last weeks contest (look for ardalis, the same as my twitter handle). Im hoping to be in the top 3 this week. I thought Id share some tips to help you get started.
Countering an Expected Move
If you think you can predict what the other bot is going to do, then its a relatively simple matter to counter that move. Calculating the move to use to counter that move is fairly straightforward. In my initial design, I had a simple method that achieved this, but as my basic bot class grew in size, I refactored this out into its own simple class that follows the Single Responsibility Principle:
using
RockPaperScissorsPro;
namespace RockPaperAzure
{
public class CounterMove
{
private readonly Move _moveToCounter;
public CounterMove(Move moveToCounter)
{
_moveToCounter = moveToCounter;
}
public Move Result()
{
if (_moveToCounter == Moves.Dynamite)
{
return Moves.WaterBalloon;
}
if (_moveToCounter == Moves.Rock)
{
return Moves.Paper;
}
if (_moveToCounter == Moves.Paper)
{
return Moves.Scissors;
}
if (_moveToCounter == Moves.Scissors)
{
return Moves.Rock;
}
if (_moveToCounter == Moves.WaterBalloon)
{
return Moves.GetRandomMove();
}
return Moves.GetRandomMove();
}
}
}
Note that this will never return Dynamite as a counter move Im assuming that youll have your own special logic for determining when youd like to use your Dynamite.
Tracking Game State
The MakeMove method accepts two IPlayer instances and a GameRules instance that pretty much just includes constants (like how much Dynamite each player has). The player instances will let you know their last move, points, whether they have Dynamite remaining, etc., but notably will not provide you with a complete history of the game, except as a text log. Thus, if youre interested in knowing the current state of the game, or how your opponent reacted when faced with similar game state in the past, youll need to track this yourself. The simplest thing you can do is create a couple of arrays or lists of moves for yourself and your opponent, but eventually you have enough game state tracking logic that it makes sense to move it to its own class as well. Thus, you might find something like the GameState class useful. This class lets me inspect my own move history, my opponents move history, how many of a given move my opponent has made, and also what my opponents response was to a given Tie scenario, such as tied once, tied twice, tied three times, etc.
using System.Collections.Generic;
using RockPaperScissorsPro;
using System.Linq;
namespace RockPaperAzure
{
public class GameState
{
public List<Move> MyMoves = new List<Move>();
public List<Move> OpponentMoves = new List<Move>();
public Dictionary<int, List<Move>> OpponentTieResponse = new Dictionary<int, List<Move>>();
public int CurrentTieCount = 0;
public void Update(IPlayer you, IPlayer opponent)
{
MyMoves.Add(you.LastMove);
OpponentMoves.Add(opponent.LastMove);
if (CurrentTieCount > 0)
{
if (!OpponentTieResponse.ContainsKey(CurrentTieCount))
{
OpponentTieResponse[CurrentTieCount] = new List<Move>();
}
OpponentTieResponse[CurrentTieCount].Add(opponent.LastMove);
}
if (you.LastMove == opponent.LastMove) // tie
{
CurrentTieCount++;
}
else
{
CurrentTieCount = 0;
}
}
public int MovesMadeByOpponent(Move move)
{
return OpponentMoves.Count(m => m == move);
}
}
}
Countering a Particular Bot
If you find that one particular bot is foiling your primary strategy, you can target that bot specifically. For instance, at one point early in the contest last week I reviewed the log of a match Id lost to a player named mitchmilam. After looking at the log, I noticed that his bot simply countered whatever move I made previously. Knowing this, I thought Id test out a bot that countered the counter of whatever move it had just made the CounterCounterBot was born. However, I didnt want to run this bot against everybody only against mitchmilam (and perhaps later against any bot I determined to be a CounterBot). To make this work, I simply added this case to my MakeMove() method:
if (opponent.TeamName == "mitchmilam")
{
return CounterMitchMilam(you, opponent);
}
and then wrote this method:
private Move CounterMitchMilam(IPlayer you, IPlayer opponent)
{
if (_gameState.CurrentTieCount == 1 &&
opponent.HasDynamite)
{
return Moves.WaterBalloon;
}
if (ShouldThrowDynamiteIncreasingChanceWithTies(you))
{
return Moves.Dynamite;
}
if (Moves.GetRandomNumber(10) < 8)
{
return new CounterMove(new CounterMove(you.LastMove).Result()).Result(); }
return Moves.GetRandomMove();
}
The result? In the next matchup between our bots, my bot won 1000 to 2. Later, this bot updated its strategy a bit
Summary
If youre interested in getting started with Windows Azure or just want to try your skill at creating a simple AI bot to play a very simple game, give the contest a go. There are some prizes if youre in the top 3 in a given week (only for US residents, Im afraid), but its also a fun test of skill and if you get your friends to play you can earn some bragging rights. Good luck!

