Created
June 6, 2014 18:06
-
-
Save rushfrisby/a481b1be08cdc036c61c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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