/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.cspoker.ai.bots.bot.rule;
import java.rmi.RemoteException;
import java.util.EnumSet;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import org.apache.log4j.Logger;
import org.cspoker.ai.bots.bot.AbstractBot;
import org.cspoker.ai.bots.listener.BotListener;
import org.cspoker.client.common.SmartLobbyContext;
import org.cspoker.client.common.gamestate.GameState;
import org.cspoker.client.common.playerstate.PlayerState;
import org.cspoker.common.api.shared.exception.IllegalActionException;
import org.cspoker.common.elements.cards.Card;
import org.cspoker.common.elements.player.PlayerId;
import org.cspoker.common.elements.table.Round;
import org.cspoker.common.elements.table.TableId;
import org.cspoker.common.handeval.stevebrecher.HandEval;
public class AlternatingBot extends AbstractBot {
private final static Logger logger = Logger.getLogger(CallBot.class);
private long threshold;
AlternatingBot(PlayerId playerId, TableId tableId, SmartLobbyContext lobby,
int buyIn, ExecutorService executor, long threshold, BotListener... botListeners) {
super(playerId, tableId, lobby, buyIn, executor, botListeners);
this.threshold = threshold;
}
private boolean printed = false;
private long nrActions = 0;
@Override
public void doNextAction() {
nrActions++;
if (nrActions < threshold) {
try {
playerContext.checkOrCall();
} catch (IllegalActionException e) {
logger.error(e);
throw new IllegalStateException("Call was not allowed.", e);
} catch (RemoteException e) {
logger.error(e);
throw new IllegalStateException("Call failed.", e);
}
} else {
if (nrActions > threshold && !printed) {
System.out.println("=================\nNEW BOT\n====================");
printed = true;
}
GameState gameState = playerContext.getGameState();
int deficit = gameState.getDeficit(getId());
int nbPlayers = 0;
for(PlayerState p: gameState.getAllSeatedPlayers()){
if(p.isActivelyPlaying()) nbPlayers++;
}
double winPercentage = getWinPercentage(gameState
.getCommunityCards(), playerContext.getPocketCards(),
100, nbPlayers - 1);
if (logger.isDebugEnabled()) {
logger.debug("Win percentage is " + winPercentage
+ " with " + playerContext.getPocketCards()
+ " and " + gameState.getCommunityCards() + " and "
+ (nbPlayers - 1) + " opponents.");
}
double EV = winPercentage
* (gameState.getGamePotSize() + deficit);
if (gameState.getRound().equals(Round.PREFLOP)) {
// be more aggressive on the flop
EV += EV;
}
try {
if (gameState
.getNextActivePlayerAfter(getId()) == null) {
// can only call or fold
if (deficit <= Math.round(EV)) {
playerContext.checkOrCall();
} else {
playerContext.fold();
}
} else {
playerContext.raiseMaxBetWith((int) Math
.round(EV * 0.5), (int) Math.round(EV));
}
} catch (IllegalActionException e) {
logger.warn("Raise bounds: "
+ tableContext.getGameState().getLowerRaiseBound(
getId())
+ " to "
+ tableContext.getGameState().getUpperRaiseBound(
getId()));
logger.error(e);
throw new IllegalStateException("Action was not allowed.",
e);
} catch (RemoteException e) {
logger.error(e);
throw new IllegalStateException("Action failed.", e);
}
}
}
// Card roll out code
public double getWinPercentage(EnumSet<Card> fixedCommunityCards,
EnumSet<Card> botCards, int nbSamples, int nbOpponents) {
EnumSet<Card> usedFixedCards = EnumSet.copyOf(fixedCommunityCards);
usedFixedCards.addAll(botCards);
int nbFixedCards = fixedCommunityCards.size();
int nbMissingCommunityCards = 5 - nbFixedCards;
int nbCommunitySamples, nbOpponentSamples;
if (nbMissingCommunityCards == 0) {
nbCommunitySamples = 1;
nbOpponentSamples = nbSamples;
} else {
double root = Math.sqrt(nbSamples);
nbCommunitySamples = (int) (root * nbMissingCommunityCards / 2);
nbOpponentSamples = (int) (root * 2 / nbMissingCommunityCards);
}
int nbWins = 0;
for (int i = 0; i < nbCommunitySamples; i++) {
EnumSet<Card> fixedAndCommunityCards = EnumSet
.copyOf(usedFixedCards);
EnumSet<Card> communityCards = EnumSet.copyOf(fixedCommunityCards);
for (int j = 0; j < nbMissingCommunityCards; j++) {
Card communityCard;
do {
communityCard = getRandomCard();
} while (fixedAndCommunityCards.contains(communityCard));
fixedAndCommunityCards.add(communityCard);
communityCards.add(communityCard);
}
for (int j = 0; j < nbOpponentSamples; j++) {
EnumSet<Card> fixedAndCommunityAndOpponentCards = EnumSet
.copyOf(fixedAndCommunityCards);
// fixedAndCommunityCards coincidentally contains the bot's
// hand!
int botRank = getRank(fixedAndCommunityCards);
boolean botWins = true;
for (int k = 0; k < nbOpponents; k++) {
Card opponentFirst;
do {
opponentFirst = getRandomCard();
} while (fixedAndCommunityAndOpponentCards
.contains(opponentFirst));
fixedAndCommunityAndOpponentCards.add(opponentFirst);
Card opponentSecond;
do {
opponentSecond = getRandomCard();
} while (fixedAndCommunityAndOpponentCards
.contains(opponentSecond));
fixedAndCommunityAndOpponentCards.add(opponentSecond);
EnumSet<Card> particularOpponentHand = EnumSet.of(
opponentFirst, opponentSecond);
particularOpponentHand.addAll(communityCards);
int opponentRank = getRank(particularOpponentHand);
if (opponentRank >= botRank) {
botWins = false;
}// TODO fix for split pot
}
if (botWins) {
++nbWins;
}
}
}
return (double) nbWins / (nbOpponentSamples * nbCommunitySamples);
}
private int getRank(EnumSet<Card> cards) {
return HandEval.getRank(cards);
}
private static Random random = new Random();
private static Card[] cards = Card.values();
protected static Card getRandomCard() {
return cards[random.nextInt(cards.length)];
}
}