package bots.mctsbot.ai.bots.bot.gametree.mcts;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.widgets.Display;
import util.Utils;
import bots.mctsbot.ai.bots.bot.Bot;
import bots.mctsbot.ai.bots.bot.BotFactory;
import bots.mctsbot.ai.bots.bot.gametree.mcts.listeners.MCTSListener;
import bots.mctsbot.ai.bots.bot.gametree.mcts.listeners.SWTTreeListener;
import bots.mctsbot.ai.bots.bot.gametree.mcts.nodes.MCTSBucketShowdownNode;
import bots.mctsbot.ai.bots.bot.gametree.mcts.strategies.backpropagation.SampleWeightedBackPropStrategy;
import bots.mctsbot.ai.bots.bot.gametree.mcts.strategies.selection.MaxValueSelector;
import bots.mctsbot.ai.bots.bot.gametree.mcts.strategies.selection.SamplingSelector;
import bots.mctsbot.ai.bots.bot.gametree.mcts.strategies.selection.SamplingToFunctionSelector;
import bots.mctsbot.ai.bots.bot.gametree.mcts.strategies.selection.UCTSelector;
import bots.mctsbot.ai.bots.bot.gametree.search.expander.sampling.StochasticUniversalSampler;
import bots.mctsbot.ai.opponentmodels.weka.WekaRegressionModelFactory;
import bots.mctsbot.client.common.GameStateContainer;
import bots.mctsbot.client.common.gamestate.DetailedHoldemTableState;
import bots.mctsbot.client.common.gamestate.GameState;
import bots.mctsbot.client.common.gamestate.modifiers.BetState;
import bots.mctsbot.client.common.gamestate.modifiers.BlindState;
import bots.mctsbot.client.common.gamestate.modifiers.CallState;
import bots.mctsbot.client.common.gamestate.modifiers.FoldState;
import bots.mctsbot.client.common.gamestate.modifiers.NewCommunityCardsState;
import bots.mctsbot.client.common.gamestate.modifiers.NewDealState;
import bots.mctsbot.client.common.gamestate.modifiers.NewPocketCardsState;
import bots.mctsbot.client.common.gamestate.modifiers.NewRoundState;
import bots.mctsbot.client.common.gamestate.modifiers.NextPlayerState;
import bots.mctsbot.client.common.gamestate.modifiers.RaiseState;
import bots.mctsbot.client.common.gamestate.modifiers.ShowHandState;
import bots.mctsbot.common.api.lobby.holdemtable.event.BetEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.BlindEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.CallEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.FoldEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.NewCommunityCardsEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.NewDealEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.NewRoundEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.NextPlayerEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.RaiseEvent;
import bots.mctsbot.common.api.lobby.holdemtable.event.ShowHandEvent;
import bots.mctsbot.common.api.lobby.holdemtable.holdemplayer.context.RemoteHoldemPlayerContext;
import bots.mctsbot.common.api.lobby.holdemtable.holdemplayer.event.NewPocketCardsEvent;
import bots.mctsbot.common.elements.chips.Pots;
import bots.mctsbot.common.elements.player.PlayerId;
import bots.mctsbot.common.elements.player.SeatedPlayer;
import bots.mctsbot.common.elements.player.ShowdownPlayer;
import bots.mctsbot.common.elements.table.DetailedHoldemTable;
import bots.mctsbot.common.elements.table.Round;
import bots.mctsbot.common.elements.table.SeatId;
import bots.mctsbot.common.elements.table.TableConfiguration;
import bots.mctsbot.common.elements.table.TableId;
import com.biotools.meerkat.Action;
import com.biotools.meerkat.Card;
import com.biotools.meerkat.GameInfo;
import com.biotools.meerkat.Hand;
import com.biotools.meerkat.Holdem;
import com.biotools.meerkat.Player;
import com.biotools.meerkat.util.Preferences;
public class MCTSMeerkatBot implements Player {
private int ourSeat; // our seat for the current hand
private Card c1, c2; // our hole cards
private GameInfo gi; // general game information
private Preferences prefs; // the configuration options for this bot
private Bot mctsBot;
private BotFactory botFactory;
private GameStateContainer gameStateContainer;
private Map<Integer, PlayerId> playerIdCache = new HashMap<Integer, PlayerId>();
private MyPlayerContext playerContext = new MyPlayerContext();
private static Display myDisplay;
public MCTSMeerkatBot() throws Exception {
gameStateContainer = new GameStateContainer(null);
}
/**
* An event called to tell us our hole cards and seat number
* @param c1 your first hole card
* @param c2 your second hole card
* @param seat your seat number at the table
*/
public void holeCards(Card c1, Card c2, int seat) {
this.c1 = c1;
this.c2 = c2;
this.ourSeat = seat;
if (mctsBot == null) {
seatUpBot(seat);
}
Hand myHand = new Hand();
myHand.addCard(c1);
myHand.addCard(c2);
gameStateContainer.setGameState(new NewPocketCardsState(gameStateContainer.getGameState(), playerIdCache.get(Integer.valueOf(seat)),
new NewPocketCardsEvent(myHand)));
}
private void seatUpBot(int seat) {
final List<MCTSListener.Factory> listeners = new ArrayList<MCTSListener.Factory>();
if (prefs.getBooleanPreference("SHOW_GAMETREE_GUI", false)) {
if (myDisplay == null) {
setupSWTDisplayThread();
}
listeners.add(new SWTTreeListener.Factory(Display.getDefault()));
}
try {
// botFactory = new MCTSBotFactory("MCTSBot", //
// WekaRegressionModelFactory.createForZip("bots/mctsbot/ai/opponentmodels/weka/models/model1.zip"), new SamplingToFunctionSelector(20,
// new UCTSelector(20000)), // decisionSelection
// new MixedSelectionStrategy(new SamplingSelector(), new MinValueSelector(), 0.95),// opponentSelection
// new MaxUnderValueSelector(2),// moveSelection
// new MCTSBucketShowdownNode.Factory(), // showdown evaluation
// new MixtureBackPropStrategy.Factory(new MaxUnderValueSelector(2)), //
// new StochasticUniversalSampler(3), // sample creation
// 2000, // thinking time
// listeners.toArray(new MCTSListener.Factory[0]));
botFactory = new MCTSBotFactory("MCTSBot", //
WekaRegressionModelFactory.createForZip("bots/mctsbot/ai/opponentmodels/weka/models/model1.zip"),//
new SamplingToFunctionSelector(20, new UCTSelector(50000)), // decisionSelection
new SamplingSelector(),// opponentSelection
new MaxValueSelector(),// moveSelection
new MCTSBucketShowdownNode.Factory(), // showdown evaluation
new SampleWeightedBackPropStrategy.Factory(), //
new StochasticUniversalSampler(3), // sample creation
500, // thinking time
listeners.toArray(new MCTSListener.Factory[0]));
// botFactory = new MCTSBotFactory("MCTSBot", //
// WekaRegressionModelFactory.createForZip("bots/mctsbot/ai/opponentmodels/weka/models/model1.zip"), new SamplingToFunctionSelector(20,
// new UCTSelector(20000)), // decisionSelection
// new MixedSelectionStrategy(new SamplingSelector(), new MinValueSelector(), 0.95),// opponentSelection
// new MaxValueSelector(),// moveSelection
// new MCTSShowdownRollOutNode.Factory(), // showdown evaluation
// new MaxDistributionPlusBackPropStrategy.Factory(), //
// new StochasticUniversalSampler(3), // sample creation
// 1500, // thinking time
// listeners.toArray(new MCTSListener.Factory[0]));
} catch (Exception e) {
throw new RuntimeException(e);
}
mctsBot = botFactory.createBot(playerIdCache.get(Integer.valueOf(seat)), gameStateContainer, playerContext);
}
private void setupSWTDisplayThread() {
Thread displayThread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (MCTSMeerkatBot.this) {
myDisplay = new Display();
MCTSMeerkatBot.this.notify();
}
while (!myDisplay.isDisposed()) {
if (!myDisplay.readAndDispatch()) {
myDisplay.sleep();
}
}
}
});
displayThread.start();
synchronized (MCTSMeerkatBot.this) {
if (myDisplay == null) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* Requests an Action from the player
* Called when it is the Player's turn to act.
*/
public Action getAction() {
GameState gameState = gameStateContainer.getGameState();
PlayerId playerId = playerIdCache.get(Integer.valueOf(ourSeat));
gameState = new NextPlayerState(gameState, new NextPlayerEvent(playerId));
gameStateContainer.setGameState(gameState);
mctsBot.doNextAction();
return playerContext.nextAction;
}
/**
* Get the current settings for this bot.
*/
public Preferences getPreferences() {
return prefs;
}
/**
* Load the current settings for this bot.
*/
public void init(Preferences playerPrefs) {
this.prefs = playerPrefs;
}
/**
* A new betting round has started.
*/
public void stageEvent(int stage) {
GameState gameState = gameStateContainer.getGameState();
Hand newCards = new Hand();
Round round = Round.PREFLOP;
switch (stage) {
case Holdem.FLOP:
newCards.addCard(gi.getBoard().getCard(1));
newCards.addCard(gi.getBoard().getCard(2));
newCards.addCard(gi.getBoard().getCard(3));
round = Round.FLOP;
break;
case Holdem.TURN:
newCards.addCard(gi.getBoard().getCard(4));
round = Round.TURN;
break;
case Holdem.RIVER:
newCards.addCard(gi.getBoard().getCard(5));
round = Round.FINAL;
break;
}
gameState = new NewRoundState(gameState, new NewRoundEvent(round, new Pots((int) (gi.getTotalPotSize() * 100))));
if (round != Round.PREFLOP) {
gameState = new NewCommunityCardsState(gameState, new NewCommunityCardsEvent(newCards));
}
gameStateContainer.setGameState(gameState);
}
/**
* A showdown has occurred.
* @param pos the position of the player showing
* @param c1 the first hole card shown
* @param c2 the second hole card shown
*/
public void showdownEvent(int seat, Card c1, Card c2) {
GameState gameState = gameStateContainer.getGameState();
PlayerId playerId = playerIdCache.get(Integer.valueOf(seat));
Hand showDownHand = new Hand();
showDownHand.addCard(c1);
showDownHand.addCard(c2);
ShowdownPlayer showdownPlayer = new ShowdownPlayer(playerId, showDownHand, "");
gameState = new ShowHandState(gameState, new ShowHandEvent(showdownPlayer));
gameStateContainer.setGameState(gameState);
}
/**
* A new game has been started.
* @param gi the game stat information
*/
public void gameStartEvent(GameInfo gInfo) {
this.gi = gInfo;
List<SeatedPlayer> seatedPlayer = new ArrayList<SeatedPlayer>();
playerIdCache = new HashMap<Integer, PlayerId>();
for (int seat = 0; seat < gInfo.getNumSeats(); seat++) {
if (gInfo.getPlayer(seat) != null) {
// we should use a unique player id, otherwise weka-models could get confused
PlayerId player = new PlayerId(gInfo.getPlayerName(seat));
playerIdCache.put(Integer.valueOf(seat), player);
seatedPlayer.add(new SeatedPlayer(player, new SeatId(seat), gInfo.getPlayerName(seat), (int) Utils.roundToCents(gInfo.getBankRoll(seat) * 100),
0, true, true));
}
}
GameState gameState = gameStateContainer.getGameState();
if (gameStateContainer.getGameState() == null) {
TableConfiguration tableConfiguration = new TableConfiguration((int) Utils.roundToCents(gi.getBigBlindSize() * 100));
DetailedHoldemTable table = new DetailedHoldemTable(new TableId(1), "Test", seatedPlayer, true, tableConfiguration);
gameState = new DetailedHoldemTableState(table);
}
gameState = new NewDealState(new NewDealEvent(seatedPlayer, playerIdCache.get(Integer.valueOf(gInfo.getButtonSeat()))), gameState);
gameStateContainer.setGameState(gameState);
}
/**
* An event sent when all players are being dealt their hole cards
*/
public void dealHoleCardsEvent() {
}
/**
* An action has been observed.
*/
public void actionEvent(int pos, Action act) {
GameState gameState = gameStateContainer.getGameState();
PlayerId playerId = playerIdCache.get(Integer.valueOf(pos));
int amount = (int) Utils.roundToCents(act.getAmount() * 100);
int toCall = (int) Utils.roundToCents(act.getToCall() * 100);
if (pos != ourSeat && !(act.getType() == Action.SMALL_BLIND || act.getType() == Action.BIG_BLIND)) {
// no nextPlayerState for
// - ourself (this state was already set in doNextAction)
// - the blinds
gameState = new NextPlayerState(gameState, new NextPlayerEvent(playerId));
}
switch (act.getType()) {
case Action.SMALL_BLIND:
case Action.BIG_BLIND:
gameState = new BlindState(gameState, new BlindEvent(playerId, amount));
break;
case Action.BET:
gameState = new BetState(gameState, new BetEvent(playerId, amount));
break;
case Action.CALL:
gameState = new CallState(gameState, new CallEvent(playerId, toCall));
break;
case Action.RAISE:
gameState = new RaiseState(gameState, new RaiseEvent(playerId, amount, amount + toCall));
break;
case Action.FOLD:
gameState = new FoldState(gameState, new FoldEvent(playerId));
break;
}
gameStateContainer.setGameState(gameState);
}
/**
* The game info state has been updated
* Called after an action event has been fully processed
*/
public void gameStateChanged() {
}
/**
* The hand is now over.
*/
public void gameOverEvent() {
}
/**
* A player at pos has won amount with the hand handName
*/
public void winEvent(int pos, double amount, String handName) {
}
class MyPlayerContext implements RemoteHoldemPlayerContext {
Action nextAction = Action.sitout();
@Override
public void betOrRaise(int amount) {
if (gi.getAmountToCall(ourSeat) > 0) {
nextAction = Action.raiseAction(gi, Utils.roundToCents(amount / 100D));
} else {
nextAction = Action.betAction(Utils.roundToCents(amount / 100D));
}
}
@Override
public void checkOrCall() {
if (gi.getAmountToCall(ourSeat) > 0) {
nextAction = Action.callAction(gi);
} else {
nextAction = Action.checkAction();
}
}
@Override
public void fold() {
nextAction = Action.foldAction(gi);
}
@Override
public String toString() {
return nextAction.toString();
}
}
}