Skip to content

Instantly share code, notes, and snippets.

@rushfrisby
Created June 6, 2014 18:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rushfrisby/a481b1be08cdc036c61c to your computer and use it in GitHub Desktop.
Save rushfrisby/a481b1be08cdc036c61c to your computer and use it in GitHub Desktop.
namespace RockPaperAzure
{
using System;
using System.Collections.Generic;
using System.Linq;
using RockPaperScissorsPro;
public class MyBot : IRockPaperScissorsBot
{
private readonly List<Move> _opponentMoves;
private readonly List<Move> _myMoves;
private readonly List<Move> _doubleTie;
private readonly Random _rnd;
private int _ties;
private int _dynamiteIveUsed;
public MyBot()
{
_myMoves = new List<Move>();
_opponentMoves = new List<Move>();
_doubleTie = new List<Move>();
_rnd = new Random();
_ties = 0;
_dynamiteIveUsed = 0;
}
public Move MakeMove(IPlayer you, IPlayer opponent, GameRules rules)
{
//record all moves
if (opponent.LastMove != null)
{
_opponentMoves.Add(opponent.LastMove);
_myMoves.Add(you.LastMove);
}
//track moves made after 2 consecutive ties so we can try to figure out opponents pattern
if (_ties == 2)
{
_doubleTie.Add(opponent.LastMove);
}
//track the number of dynamites ive used
if (you.LastMove == Moves.Dynamite)
{
_dynamiteIveUsed = _dynamiteIveUsed + 1;
}
//track the number of consecutive ties
var moveCount = _opponentMoves.Count;
_ties = you.LastMove == opponent.LastMove ? _ties = _ties + 1 : 0;
if (moveCount > 1 && opponent.HasDynamite && _opponentMoves.Count == _opponentMoves.Count(x => x == Moves.Dynamite))
{
//protects us from players opening with a barrade of dynamite
return Moves.WaterBalloon;
}
if(_ties >= 2)
{
if (_doubleTie.Count >= 3)
{
//if we've had 2 or more consecutive ties 3 or more times try to guess the opponents pattern
if (_doubleTie.Skip(_doubleTie.Count - 3).Take(3).Distinct().Count() == 1)
{
//the last 3 moves were the same so lets beat what they are playing
var move = _doubleTie.Skip(_doubleTie.Count - 1).Take(1).First();
if (move == Moves.Dynamite && opponent.HasDynamite)
return Moves.WaterBalloon;
return Beat(move);
}
}
if (opponent.HasDynamite && you.LastMove == Moves.WaterBalloon)
return GetRandomMoveAll(opponent); //random move seems to work better than being predictable here - maybe other players have the same pattern guess logic?
//return Moves.WaterBalloon; //try to win 4+ points (previous moves: random tie, dynamite, water balloon) by assuming opponent has dynamite/water balloon rotation for tie breaker - we break their rotation
if (opponent.HasDynamite && opponent.LastMove == Moves.Dynamite)
return GetRandomMoveAll(opponent); //random move seems to work better than being predictable here - maybe other players have the same pattern guess logic?
//return Moves.WaterBalloon; //try to win 4 points (previous moves: random tie, dynamite) by assuming opponent has dynamite tie breaker
if (you.HasDynamite && you.LastMove != Moves.Dynamite)
return GetDynamite(); //try to win 3 points (previous moves: 2 random ties)
}
#region anti cycle
/*
if the opponent is cycling through all of the moves this will crush them
do this 3 times for RPS/RPSD/RPSDW cycles
*/
if (moveCount > 5)
{
var ac = AntiCycle(opponent, 3);
if (ac != null) return ac;
}
if (moveCount > 7)
{
var ac = AntiCycle(opponent, 4);
if (ac != null) return ac;
}
if (moveCount > 9)
{
var ac = AntiCycle(opponent, 5);
if (ac != null) return ac;
}
#endregion
if (opponent.DynamiteRemaining >= rules.PointsToWin - opponent.Points)
{
if (opponent.LastMove == Moves.Dynamite)
{
//opponent has dynamite they are trying to finish with
return Moves.WaterBalloon;
}
}
if (you.DynamiteRemaining >= rules.PointsToWin - opponent.Points)
{
//you have dynamite and your opponent is about to win / try to catch up
return GetDynamite();
}
if (you.DynamiteRemaining >= rules.PointsToWin - you.Points)
{
//you have dynamite and you are about to win / FINISH HIM!
return GetDynamite();
}
//if no logical move has been made then pick something at random
return GetRandomMove();
}
public Move AntiCycle(IPlayer opponent, int cycleLength)
{
var moveCount = _opponentMoves.Count;
for (var i = 1; i <= cycleLength; i++)
{
var move1 = _opponentMoves[moveCount - i];
var move2 = _opponentMoves[moveCount - i - cycleLength];
if (move1 != move2)
return null;
}
var nextMove = _opponentMoves[moveCount - cycleLength];
return Beat(nextMove);
}
private Move Beat(Move opponentsMove)
{
if (opponentsMove == Moves.Rock)
{
return Moves.Paper;
}
if (opponentsMove == Moves.Paper)
{
return Moves.Scissors;
}
if (opponentsMove == Moves.Scissors)
{
return Moves.Rock;
}
//don't try to beat dynamite, it's risky
//RPS beats water ballon, so we can get a random move and not use dynamite
return GetRandomMove();
}
private Move GetRandomMove()
{
switch (_rnd.Next(1, 100) % 3)
{
case 0:
return Moves.Rock;
case 1:
return Moves.Paper;
}
return Moves.Scissors;
}
private Move GetRandomMoveAll(IPlayer opponent)
{
switch (_rnd.Next(1, 100) % (opponent.HasDynamite ? 5 : 4))
{
case 0:
return Moves.Rock;
case 1:
return Moves.Paper;
case 2:
return Moves.Scissors;
case 3:
return GetDynamite();
}
//only returns water balloon if the opponent has dynamite
return Moves.WaterBalloon;
}
private Move GetDynamite()
{
//use all but 1 dynamite so that the other player is more likely to play water balloons up until the end
return _dynamiteIveUsed < 99 ? Moves.Dynamite : GetRandomMove();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment