/// <summary> /// Validates the AI action. /// Prevents raises when only calls are possible. /// Prevents raising more than allowed. /// Prevent raising less than allowed. /// </summary> /// <param name="aiAction">The AI action to be validated.</param> /// <returns>The validated decision.</returns> public static Play ValidatePlayerDecision(Play oldAiAction, databaseCache cache) { //PlayerId passed through because "cache.getCurrentActiveTablePosition()" is a slow method. //long playerId = cache.getPlayerId(cache.getCurrentActiveTablePosition()); long playerId = oldAiAction.PlayerId; decimal minCallAmount = cache.getMinimumPlayAmount(); decimal currentRoundBetAmount = cache.getPlayerCurrentRoundBetAmount(playerId); decimal playerRemaningStack = cache.getPlayerStack(playerId); //if (oldAiAction.PlayerId != playerId) // throw new Exception("Current active table position does not correspond with bot player position."); //Copy the action here so that we can still see the old one for debugging reasons. //Once we know this is stable we can get rid of this line. Play aiAction = new Play(oldAiAction.Serialise()); //We need to make sure a raise is possible byte[] activePositions = cache.getActivePositions(); byte numPlayersAllIn = (byte)cache.getAllInPositions().Length; //We need to ensure the bot is not calling dead, i.e. if the winPercentage is less than 5% we should not be calling after the river if (aiAction.Action == PokerAction.Fold) { //Check for the free check if (minCallAmount - currentRoundBetAmount == 0) { aiAction.Action = PokerAction.Check; } } else if (aiAction.Action == PokerAction.Call) { //We must call atleast the minimum amount if (minCallAmount - currentRoundBetAmount > aiAction.Amount) { aiAction.Amount = minCallAmount; } //We cannot call 0, it should be a check if (minCallAmount - currentRoundBetAmount == 0) { aiAction.Action = PokerAction.Check; aiAction.Amount = 0; } else if (minCallAmount - currentRoundBetAmount > playerRemaningStack) { //We cannot call more than the stack amount aiAction.Amount = playerRemaningStack; } //Never call a 0 amount!!! if (aiAction.Amount == 0 && aiAction.Action == PokerAction.Call) { aiAction.Action = PokerAction.Check; } } else if (aiAction.Action == PokerAction.Raise) { //If the raiseToAmount is less than the minimum allowable raise then raise the minimum. decimal lastAdditionalRaiseAmount = cache.getCurrentRoundLastRaiseAmount(); decimal minimumRaiseToAmount = (minCallAmount - lastAdditionalRaiseAmount) + (lastAdditionalRaiseAmount * 2); if (aiAction.Amount < minimumRaiseToAmount) { aiAction.Amount = minimumRaiseToAmount; } //if (aiAction.Amount > (cache.getCurrentHandDetails().potValue * 2)) // aiAction.Amount = cache.getCurrentHandDetails().potValue * 2; //If the raiseToAmount is more than we are able to raise then raise the maximum amount possible. if (aiAction.Amount > currentRoundBetAmount + playerRemaningStack) { aiAction.Amount = currentRoundBetAmount + playerRemaningStack; } if (minCallAmount - currentRoundBetAmount >= playerRemaningStack) { //If we are trying to raise but we cannot even meet the minimum call then call stack aiAction.Action = PokerAction.Call; aiAction.Amount = playerRemaningStack; } else if (numPlayersAllIn + 1 == activePositions.Length) { //If everyone else is all in then calling is our only option aiAction.Action = PokerAction.Call; aiAction.Amount = minCallAmount - currentRoundBetAmount; //If we already have the right amount in the pot we can only check if (aiAction.Amount == 0) { aiAction.Action = PokerAction.Check; } } } //If we are still trying to call a 0 amounts somthing else has gone very wrong if (aiAction.Amount == 0 && aiAction.Action == PokerAction.Call) { throw new Exception("Critical validation error - trying to call 0 amount."); } /* * if (oldAiAction.Amount != aiAction.Amount || oldAiAction.Action != aiAction.Action) * throw new Exception("Validation Error"); */ return(aiAction); }
public void GetRaiseCallStealAmounts(databaseCache cache, long playerId, out decimal call, out decimal steel, out double probStealSuccess, out double probCallSuccess) { var playersInHand = cache.getActivePlayerIds(); call = decimal.MinValue; steel = decimal.MinValue; decimal callTemp, stealTemp; double stealProbTemp, callProbTemp; probStealSuccess = 0; probCallSuccess = 0; HandState stage; var details = cache.getCurrentHandDetails(); if (details.tableCard1 == 0) { stage = HandState.PreFlop; } else if (details.tableCard4 == 0) { stage = HandState.Flop; } else if (details.tableCard5 == 0) { stage = HandState.Turn; } else { stage = HandState.River; } var playerCards = cache.getPlayerHoleCards(playerId); decimal minExtraRaise = (cache.getCurrentRoundLastRaiseAmount() == 0 ? cache.BigBlind : cache.getCurrentRoundLastRaiseAmount()); decimal maxExtraRaise = cache.getPlayerStack(playerId) - cache.getMinimumPlayAmount() + cache.getPlayerCurrentRoundBetAmount(playerId); double numberOpponents = cache.getActivePositions().Length - cache.getAllInPositions().Length - 1; if (playersInHand.Length > cache.getAllInPositions().Length + 1 && maxExtraRaise > 0) { foreach (var player in playersNew) { if (player.Key != playerId && playersInHand.Contains(player.Key)) { if (cache.getAllInPositions().Contains(cache.getPlayerPosition(player.Key))) { continue; } var dd = (cache.getActivePlayerDistanceToDealer(player.Key) - 1.0) / (cache.getActivePositions().Length - 1.0); player.Value.GetRaiseCallSteal(stage, (Card)playerCards.holeCard1, (Card)playerCards.holeCard2, dd < 0.5, details.potValue, minExtraRaise, maxExtraRaise, Math.Pow(0.5, 1.0 / numberOpponents), Math.Pow(0.75, 1.0 / numberOpponents), out callTemp, out stealTemp, out stealProbTemp, out callProbTemp); if (callTemp > call) { call = callTemp; probCallSuccess = callProbTemp; } if (stealTemp > steel) { steel = stealTemp; probStealSuccess = stealProbTemp; } } } probStealSuccess = Math.Pow(probStealSuccess, numberOpponents); probCallSuccess = Math.Pow(probCallSuccess, numberOpponents); } else { call = minExtraRaise; steel = minExtraRaise; probStealSuccess = 0; probCallSuccess = 0; } decimal currentMinPlayAmount = cache.getMinimumPlayAmount(); decimal minRaise = minExtraRaise + currentMinPlayAmount; decimal maxRaise = maxExtraRaise + currentMinPlayAmount; call += currentMinPlayAmount; steel += currentMinPlayAmount; decimal callChange = call * 0.2m * (decimal)(wrProv.randomGen.NextDouble() - 0.5); decimal stealChange = steel * 0.2m * (decimal)(wrProv.randomGen.NextDouble() - 0.5); if (Math.Abs(callChange) < cache.LittleBlind) { callChange = cache.LittleBlind * callChange / Math.Abs(callChange); } if (Math.Abs(stealChange) < cache.LittleBlind) { stealChange = cache.LittleBlind * stealChange / Math.Abs(stealChange); } call += callChange; call = Math.Round(call / cache.LittleBlind, 0, MidpointRounding.AwayFromZero) * cache.LittleBlind; if (call < minRaise) { call = minRaise; } if (call > maxRaise) { call = maxRaise; } steel += stealChange; steel = Math.Round(steel / cache.LittleBlind, 0, MidpointRounding.AwayFromZero) * cache.LittleBlind; if (steel < minRaise) { steel = minRaise; } if (steel > maxRaise) { steel = maxRaise; } }