package bots.smarterbot;
import java.util.LinkedList;
import com.biotools.meerkat.Action;
import com.biotools.meerkat.Card;
import com.biotools.meerkat.GameInfo;
import com.biotools.meerkat.HandEvaluator;
import com.biotools.meerkat.Holdem;
import com.biotools.meerkat.Player;
import com.biotools.meerkat.PlayerInfo;
import com.biotools.meerkat.util.Preferences;
/**
* A model based/learning agent. Smarter Bot keeps track of data for each player. It uses this data to determine
* the best course of action for the players that are currently in the hand. The main factor for determining if
* Smarter Bot should bet or check is by calculating the pot odds. Another main statistic that Smarter Bot will track
* is each player's VPIP (Voluntarily put money in pot.) This will help Smarter Bot determine the aggressiveness of
* opponents and allow it to shift its play style accordingly.<p>
* <p/>
* <strong>Pre-Flop</strong> - Smarter Bot divides all the possible pre-flop hand combinations into 6 different
* rankings. It determines which ranking to use for its particular hand via a dynamic look-up table. The hand
* combinations are assigned rankings via a learning function. For each hand, the bot determines how well it played
* that hand via a fitness function and updates the table accordingly. Smarter Bot actually has multiple independent
* look-up tables depending on what play style it is using: aggressive, passive, and neutral. Each table learns
* independently from the other ones and has its own associated fitness function.<p>
* <p/>
* <strong>Post-Flop</strong> - Smarter Bot uses the Hand Evaluator to determine the strength of its hand.
*
* @see <a href="https://wiki.csc.calpoly.edu/CPE-480-F10-13/wiki">Poker Face Project</a>
*/
public class SmarterBot 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 boolean didRaise; // did we raise preflop?
private bots.smarterbot.Action preflopLevel; // what strategy will we be using for this hand
private LinkedList<PlayerStats> stats; //list of all players histories
private History history;
private double prePotSize; // size of the pot when we limped or called (counting amount we put in)
private boolean limped; // did we successfully limp preflop
private boolean called; // did we successfully call preflop
private int timesLimped; // times successfully limped preflop with a limp category hand
private int timesCalled; // times successfully called preflop with a call category hand
private double profitLimp, profitCall, stackSize; // total percent of preflop pot won when limped/called successfully
private int lastRaiser;
private int cBetter;
private int[][] bestHands;
private enum stance {
DEFENSIVE, AGRESSIVE, NEUTRAL
}
PlayMatrix neutralTable;
PlayMatrix curTable;
public SmarterBot() {
neutralTable = new PlayMatrix();
curTable = neutralTable;
didRaise = false;
limped = called = false;
timesLimped = timesCalled = 0;
prePotSize = stackSize = 0;
lastRaiser = -1;
cBetter = -1;
preflopLevel = bots.smarterbot.Action.INVALID;
stats = new LinkedList<PlayerStats>();
history = new History();
bestHands = new int[25][2];
//66, A6, K8, Q8, J8, T8
bestHands[0][0] = 12;
bestHands[0][1] = 12;//AA
bestHands[1][0] = 11;
bestHands[1][1] = 11;//KK
bestHands[2][0] = 12;
bestHands[2][1] = 11;//AK
bestHands[3][0] = 10;
bestHands[3][1] = 10;//QQ
bestHands[4][0] = 9;
bestHands[4][1] = 9;//JJ
bestHands[5][0] = 12;
bestHands[5][1] = 10;//AQ
bestHands[6][0] = 8;
bestHands[6][1] = 8;//TT
bestHands[7][0] = 12;
bestHands[7][1] = 9;//AJ
bestHands[8][0] = 7;
bestHands[8][1] = 7;//99
bestHands[9][0] = 11;
bestHands[9][1] = 10;//KQ
bestHands[10][0] = 6;
bestHands[10][1] = 6;//88
bestHands[11][0] = 12;
bestHands[11][1] = 8;//AT
bestHands[12][0] = 11;
bestHands[12][1] = 9;//KJ
bestHands[13][0] = 10;
bestHands[13][1] = 9;//QJ
bestHands[14][0] = 11;
bestHands[14][1] = 8;//KT
bestHands[15][0] = 12;
bestHands[15][1] = 7;//A9
bestHands[16][0] = 9;
bestHands[16][1] = 8;//JT
bestHands[17][0] = 10;
bestHands[17][1] = 8;//QT
bestHands[18][0] = 8;
bestHands[18][1] = 7;//T9
bestHands[19][0] = 11;
bestHands[19][1] = 7;//K9
bestHands[20][0] = 12;
bestHands[20][1] = 6;//A8
bestHands[21][0] = 5;
bestHands[21][1] = 5;//77
bestHands[22][0] = 10;
bestHands[22][1] = 7;//Q9
bestHands[23][0] = 9;
bestHands[23][1] = 7;//J9
bestHands[24][0] = 12;
bestHands[24][1] = 5;//A7
}
/**
* 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;
preflopLevel = curTable.getFlopAction(c1, c2);
this.didRaise = false;
this.limped = this.called = false;
this.prePotSize = this.stackSize = 0;
this.lastRaiser = -1;
this.cBetter = -1;
}
/**
* Requests an Action from the player
* Called when it is the Player's turn to act.
*/
public Action getAction() {
if (gi.isPreFlop()) {
return preFlopAction();
} else {
return postFlopAction();
}
}
/**
* 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;
}
/**
* @return true if debug mode is on.
*/
public boolean getDebug() {
return prefs.getBooleanPreference("DEBUG", false);
}
/**
* print a debug statement.
*/
public void debug(String str) {
if (getDebug()) {
System.out.println(str);
}
}
/**
* print a debug statement with no end of line character
*/
public void debugb(String str) {
if (getDebug()) {
System.out.print(str);
}
}
/**
* A new betting round has started.
*/
public void stageEvent(int stage) {
}
/**
* 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) {
}
/**
* A new game has been started.
*
* @param gi the game stat information
*/
public void gameStartEvent(GameInfo gInfo) {
this.gi = gInfo;
if (gi.getNumPlayers() < 5) {
neutralTable.setDefaultMatrix();
} else {
neutralTable.setNewAggressiveMatrix();
}
}
/**
* An event sent when all players are being dealt their hole cards
*/
public void dealHoleCardsEvent() {
}
/**
* An action has been observed.
* In this method, we mine for data about players.
* A call bet or raise increases vpmip (voluntarily put money in the pot)
* A bet or raise also increases af (aggression factor)
* I AM ASSUMING THAT THE ACTTION HAS NOT PROCESSED YET AND THAT BB COUNTS AS A BET. CHECK THIS
* IF SO THEN I NEEDTO CHANGE TO ASSUME THE ACTION DIDNT PROCESS YET
*/
public void actionEvent(int pos, Action act) {
PlayerStats player = null;
int i;
boolean preflop = gi.isPreFlop();
if (act.getType() < 0 || act.getType() > 4) {
return; // not a type we care about, so dont track it
}
for (i = 0; i < stats.size(); i++) {
if (stats.get(i).seat == pos) {
player = stats.get(i);
break;
}
}
lastRaiser = pos;
if (player == null || !player.name.equals(gi.getPlayerName(pos))) { // if new name, then new player
if (player != null) {
stats.remove(i);
}
player = new PlayerStats(pos, gi.getPlayerName(pos));
stats.add(player);
}
if (act.isBetOrRaise()) {
player.preRaise++;
}
if (!act.isFold()) {
player.prePutMoney++;
}
player.preHands++;
if (preflop) {
if (gi.getNumRaises() == 0 && act.getToCall() > 0) {
player.preLimpPos++;
if (act.isCall()) {
player.preLimp++;
}
}
if (gi.getNumRaises() == 1) {
player.preRespondTo2++;
}
if (gi.getNumRaises() == 2) {
player.preRespondTo3++;
}
if (gi.getNumRaises() == 3) {
player.preRespondTo4++;
}
if (gi.getNumRaises() == 1 && act.isBetOrRaise()) {
player.pre3Bet++;
}
if (gi.getNumRaises() == 2 && act.isBetOrRaise()) {
player.pre4Bet++;
}
if (gi.getNumRaises() == 2 && act.isFold()) {
player.preFoldTo3++;
}
if (gi.getNumRaises() == 3 && act.isFold()) {
player.preFoldTo4++;
}
if (gi.getNumRaises() >= 1 && act.getToCall() == gi.getCurrentBetSize()) {
player.preColdCallPos++;
if (act.isCall()) {
player.preColdCall++;
}
}
if (gi.getNumRaises() == 3 && act.isCall()) {
player.preCall4++;
}
} else { // for post flop stats (none right now)
if (gi.getStage() == Holdem.FLOP) { // is it the flop?
if (pos == lastRaiser && gi.getNumRaises() == 0) { // no bets yet and the action is on last raiser
player.cBetPos++;
if (act.isBet()) {
player.cBet++;
this.cBetter = pos;
}
} else if (pos == cBetter) { // else if cBetter was raised
player.cBetRaised++;
if (act.isFold()) {
player.foldToCBetRaise++;
}
}
}
}
}
/**
* 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() {
double profit = gi.getBankRoll(ourSeat) - gi.getPlayer(ourSeat).getBankRollAtStartOfHand();
bots.smarterbot.Action action = history.update(c1, c2, profit, preflopLevel.getValue(), curTable.getFlopAction(c1, c2).getValue());
if (action.getValue() != Action.INVALID) {
curTable.setFlopAction(c1, c2, action);
}
// now update limp / call % winnings
if (this.called) {
this.timesCalled++;
this.profitCall += ((gi.getBankRoll(ourSeat) - this.stackSize) / this.prePotSize);
}
if (this.limped) {
this.timesLimped++;
this.profitLimp += ((gi.getBankRoll(ourSeat) - this.stackSize) / this.prePotSize);
}
}
/**
* A player at pos has won amount with the hand handName
*/
public void winEvent(int pos, double amount, String handName) {
}
/**
* Uses table to look up appropriate course of action for hole cards.
*/
private Action preFlopAction() {
double toCall = gi.getAmountToCall(ourSeat);
//note the following action is our Action.class, not theirs
bots.smarterbot.Action action = preflopLevel;
if (action == bots.smarterbot.Action.FOLD) {
if (toCall == 0) {
return this.call(toCall);
} else {
return Action.foldAction(toCall);
}
} else if (action == bots.smarterbot.Action.LIMP) {
if (toCall == 0) {
this.limped = false;
return this.call(toCall);
} else if (gi.getNumRaises() != 0) { //does the BB count as a bet?
this.limped = false;
return Action.foldAction(toCall);
} else { //no raises yet
LinkedList<PlayerInfo> players = this.getPlayers();
double noRaiseProb = 1; //probability of there being no raise if we call
int player = -1; // variable representing seat of player in question
PlayerStats stat; // our data on a certain player
for (int i = 0; i < players.size(); i++) {
player = players.get(i).getSeat();
stat = this.find(stats, player);
if (stat.preHands > 0) {
noRaiseProb *= (1 - stat.af());
}
}
double percentExpected = 0.85; // how much of the present pot we expect to win if we limp
if (this.timesLimped >= 5) {
percentExpected = this.profitLimp / (double) this.timesLimped;
}
if (noRaiseProb * percentExpected * (gi.getTotalPotSize() + toCall) >= toCall) {
Action act = this.call(toCall);
if (act == Action.callAction(toCall)) {
this.limped = true;
this.prePotSize = gi.getTotalPotSize() + toCall;
this.stackSize = gi.getPlayer(ourSeat).getBankRoll() - toCall;
}
return act;
} else {
this.limped = false;
return Action.foldAction(toCall);
}
}
} else if (action == bots.smarterbot.Action.CALL) {
if (toCall == 0) { // we are BB, and no raise
this.called = false;
return Action.callAction(toCall);
} else if (gi.getNumRaises() > 1) { // More than one raise, so fold. does the BB count as a bet?
this.called = false;
return Action.foldAction(toCall);
} else if (gi.getNumRaises() == 0) { // no raises, so call
this.called = false;
return this.call(toCall);
} else { //there is just one raise
if (toCall <= gi.getBigBlindSize() * 4) { // will call a raise <= 4x BB if its likely noone else will raise
LinkedList<PlayerInfo> players = this.getPlayers();
double noRaiseProb = 1; //probability of there being no raise if we call
double foldTo3BetProb = 1; // probability of everyone folding if we 3bet
int player = -1; // variable representing seat of player in question
PlayerStats stat; // our data on a certain player
for (int i = 0; i < players.size(); i++) {
player = players.get(i).getSeat();
stat = this.find(stats, player);
if (stat.preRespondTo2 > 0) {
noRaiseProb *= (1 - stat.percent3Bet());
}
if (stat.preRespondTo3 > 0) {
foldTo3BetProb *= stat.foldTo3Bet();
} else if (stat.preHands > 0) {
foldTo3BetProb *= 1 - (stat.vpmip());
}
}
double percentExpected = 0.85;
if (this.timesCalled >= 5) {
percentExpected = this.profitCall / (double) this.timesCalled;
}
double amountGainedRaising = foldTo3BetProb * gi.getTotalPotSize() - (1 - foldTo3BetProb) * toCall;
double amountGainedCalling = ((gi.getTotalPotSize() + toCall) * percentExpected * noRaiseProb) - toCall;
if (amountGainedRaising > 0 && amountGainedRaising > amountGainedCalling) {
this.called = false;
return this.raise(toCall);
} else if (amountGainedCalling > 0) {
Action act = this.call(toCall);
if (act == Action.callAction(toCall)) {
this.called = true;
this.prePotSize = gi.getTotalPotSize();
}
return act;
}
this.called = false;
return Action.foldAction(toCall);
} else { // fold if the raise is too big (like to 9$ for a 2$ BB
this.called = false;
return Action.foldAction(toCall);
}
}
} else if (action == bots.smarterbot.Action.RAISE) {
if (gi.getNumRaises() == 0) { //then raise!
return this.raise(toCall);
} else if (gi.getNumRaises() == 1) { //then call a raise
if (toCall <= gi.getBigBlindSize() * 5) {
return this.call(toCall);
} else {
return Action.foldAction(toCall);
}
} else if (gi.getNumRaises() >= 3) { // fold to 4 bets
return Action.checkOrFoldAction(toCall);
} else if (gi.getNumRaises() == 2 && this.didRaise) { // someone 3 bet our raise
double handRange = 0;
int i = 0;
for (i = 0; i < 25 && handRange < find(this.stats, lastRaiser).percent3Bet(); i++) {
if (this.bestHands[i][0] == this.bestHands[i][1]) { //pocket pairs are 1 / 221
handRange += (1.0 / 221.0);
} else { // not a pocket pair is
handRange += (8.0 / 663.0);
}
}
double winChance = 0;
for (int j = 0; j < i; j++) {
if (this.bestHands[j][0] == this.bestHands[j][1]) { //pocket pair is less likely
winChance += ((1.0 / 221.0) / handRange) * this.handMatcher(c1, c2, j);
} else { // not pair is more likely
winChance += ((8.0 / 663.0) / handRange) * this.handMatcher(c1, c2, j);
}
}
if (handRange < find(this.stats, lastRaiser).percent3Bet()) {
winChance += (find(this.stats, lastRaiser).percent3Bet() - handRange) * 0.75;
}
if (toCall <= (gi.getTotalPotSize() + toCall) * winChance) {
return this.call(toCall);
}
} else {
return Action.checkOrFoldAction(toCall);
}
} else if (action == bots.smarterbot.Action.RERAISE) {
if (gi.getNumRaises() <= 1) { //no raise or one raise so far
this.didRaise = true;
return this.raise(toCall);
} else if (gi.getNumRaises() == 2) { // there is a 3 bet
// code copied from raise for considering the call
double handRange = 0;
int i = 0;
for (i = 0; i < 25 && handRange < find(this.stats, lastRaiser).percent3Bet(); i++) {
if (this.bestHands[i][0] == this.bestHands[i][1]) { //pocket pairs are 1 / 221
handRange += (1.0 / 221.0);
} else { // not a pocket pair is
handRange += (8.0 / 663.0);
}
}
double winChance = 0;
for (int j = 0; j < i; j++) {
if (this.bestHands[j][0] == this.bestHands[j][1]) { //pocket pair is less likely
winChance += ((1.0 / 221.0) / handRange) * this.handMatcher(c1, c2, j);
} else { // not pair is more likely
winChance += ((8.0 / 663.0) / handRange) * this.handMatcher(c1, c2, j);
}
}
if (handRange < find(this.stats, lastRaiser).percent3Bet()) {
winChance += (find(this.stats, lastRaiser).percent3Bet() - handRange) * 0.85;
}
double profitCall = ((gi.getTotalPotSize() + toCall) * winChance) - toCall;
LinkedList<PlayerInfo> players = this.getPlayers();
double noRaiseProb = 1; //probability of there being no raise if we call
double foldTo4BetProb = 1; // probability of everyone folding if we 4bet
int player = -1; // variable representing seat of player in question
PlayerStats stat; // our data on a certain player
double call4Bet = 1; //prob of someone calling
for (int k = 0; k < players.size(); k++) {
player = players.get(k).getSeat();
stat = this.find(stats, player);
if (stat.preRespondTo2 > 0) {
noRaiseProb *= (1 - stat.percent4Bet());
}
if (stat.preRespondTo4 > 0) {
foldTo4BetProb *= stat.foldTo4Bet();
call4Bet *= stat.call4Bet();
} else if (stat.preHands > 0) {
foldTo4BetProb *= 1 - (stat.vpmip());
}
}
call4Bet = 1 - call4Bet;
//how much we gain bt raising == size of pot * chance of everyone folding - cost to raise
double profitRaise = ((this.raise(toCall).getAmount() + gi.getTotalPotSize()) * foldTo4BetProb) - this.raise(toCall).getAmount();
//also we gain the cost of a call + chance of win
profitRaise = profitRaise + (winChance * call4Bet * ((this.raise(toCall).getAmount() * 2) + gi.getTotalPotSize()));
if (profitRaise >= profitCall && profitRaise >= 0) {
return this.raise(toCall);
} else if (profitCall >= 0) {
return this.call(toCall);
} else {
return Action.checkOrFoldAction(toCall);
}
}
} else if (action == bots.smarterbot.Action.PREMIUM) {
return this.raise(toCall);
}
// should be unreachable
return Action.checkOrFoldAction(toCall);
}
/**
* Calls on hands with 50% > x <= 70% of winning
* Raise/Bet on hands with x > 70% chance of winning
* Goes all in if it knows it has the best hand
* Fold all other hands
*/
private Action postFlopAction() {
double toCall = gi.getAmountToCall(ourSeat);
int numPlayers = gi.getNumActivePlayers() - 1;
double potSize = gi.getTotalPotSize();
double handRank = HandEvaluator.handRank(c1, c2, gi.getBoard(), numPlayers);
LinkedList<PlayerInfo> players = this.getPlayers();
PlayerStats player;
//first we consider a c bet before anything else
if (numPlayers <= 3 && this.didRaise && gi.getNumRaises() == 0 && gi.getStage() == Holdem.FLOP) { // c bet
return Action.betAction(potSize * 0.5 + (0.1 * numPlayers));
}
//what if someone has cBet? consider raising them off the hand
if (gi.getNumRaises() == 1 && this.cBetter == this.lastRaiser && gi.getStage() == Holdem.FLOP) {
player = find(stats, cBetter);
boolean considerRaise = true;
for (PlayerInfo listPlayer : players) {
if (listPlayer.getAmountInPotThisRound() != 0 && listPlayer.getSeat() != this.cBetter) {
considerRaise = false;
break;
}
}
if (considerRaise) {
if (player.cBetRaised != 0) {
double raiseValue = player.foldToCBetRaise();
raiseValue = (raiseValue * (gi.getTotalPotSize() + this.raise(toCall).getAmount())) - this.raise(toCall).getAmount();
if (raiseValue > 0) {
return this.raise(toCall);
}
}
}
}
// now consider a bet if no one has raised based on chance of them folding
if (gi.getStage() == Holdem.PREFLOP && gi.getNumRaises() == 0) {
double probFold = 1;
for (PlayerInfo stat : players) {
player = find(stats, stat.getSeat());
if (player.preHands != 0) {
probFold *= (1 - player.vpmip());
}
}
if ((probFold * (gi.getTotalPotSize() + this.raise(toCall).getAmount())) - this.raise(toCall).getAmount() > 0) {
return this.raise(toCall);
}
}
// now consider a call with a draw
boolean flushDraw = false;
if (c1.getSuit() == c2.getSuit()) {
int suited = 0;
for (int i = 0; i < gi.getBoard().size(); i++) {
if (gi.getBoard().getCard(i).getSuit() == c1.getSuit()) {
suited++;
}
}
if (suited == 2) {
flushDraw = true;
}
}
if (gi.getNumRaises() > 0 && flushDraw) {
if ((9.0 / (double) (50 - gi.getBoard().size())) * ((gi.getTotalPotSize() + toCall) * 1.25) - toCall > 0) {
return this.call(toCall);
}
}
if (handRank >= 0.85) {
return this.raise(toCall);
} else if (handRank > 0.75) {
return this.call(toCall);
} else {
return Action.checkOrFoldAction(toCall);
}
}
private Action call(double toCall) { //Aaron's
if (toCall >= gi.getPlayer(ourSeat).getBankRoll() / 3) {
this.didRaise = true;
return Action.raiseAction(toCall, gi.getPlayer(ourSeat).getBankRoll());
} else {
this.didRaise = false;
return Action.callAction(toCall);
}
}
private Action raise(double toCall) { //Aaron's
this.didRaise = true;
LinkedList<PlayerInfo> list = new LinkedList<PlayerInfo>();
double largestStack = 0;
list = getPlayers();
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getBankRoll() > largestStack) {
largestStack = list.get(i).getBankRoll();
}
}
double raiseTo = 0;
if (gi.getStage() != Holdem.PREFLOP) {
if (gi.getNumRaises() == 0) {
raiseTo = gi.getTotalPotSize() * (0.5 + (gi.getNumActivePlayersNotAllIn() * 0.1));
} else {
if (gi.getTotalPotSize() * (0.5 + (gi.getNumActivePlayersNotAllIn() * 0.1)) > toCall * 2.5) {
raiseTo = gi.getTotalPotSize() * (0.5 + (gi.getNumActivePlayersNotAllIn() * 0.1));
} else {
raiseTo = toCall * 2.5;
}
}
} else if (gi.getNumRaises() == 0) { //preflop
raiseTo = gi.getBigBlindSize() * 4;
} else { // preflop with a raise already out
raiseTo = gi.getTotalPotSize() * 2;
}
if (raiseTo > gi.getPlayer(ourSeat).getBankRoll() / 2 || raiseTo > largestStack / 2) {
return Action.raiseAction(toCall, gi.getPlayer(ourSeat).getBankRoll());
} else {
return Action.raiseAction(toCall, raiseTo);
}
}
private LinkedList<PlayerInfo> getPlayers() {
LinkedList<PlayerInfo> list = new LinkedList<PlayerInfo>();
int player = this.ourSeat;
player = gi.nextActivePlayer(player);
while (player != this.ourSeat) {
list.add(gi.getPlayer(player));
player = gi.nextActivePlayer(player);
}
return list;
}
/**
* @author nalemromandi
* Tracks all the stats about players.
*/
private class PlayerStats {
int seat; // the seat the player is sitting at
String name; // the name of the player
int preHands, preRaise, prePutMoney;
int pre3Bet, pre4Bet;
int preRespondTo2, preRespondTo3, preRespondTo4;
int preColdCall, preColdCallPos;
int preLimp, preLimpPos;
int preFoldTo3, preFoldTo4;
int preCall4;
int cBet, cBetPos;
int foldToCBetRaise, cBetRaised;
public PlayerStats(int seat, String name) {
this.seat = seat;
this.name = name;
preHands = preRaise = prePutMoney = 0;
pre3Bet = pre4Bet = 0;
preRespondTo2 = preRespondTo3 = preRespondTo4 = 0;
preColdCall = preColdCallPos = 0;
preLimp = preLimpPos = 0;
preFoldTo3 = preFoldTo4 = 0;
preCall4 = 0;
cBet = cBetPos = 0;
foldToCBetRaise = cBetRaised = 0;
}
public double vpmip() {
if (preHands == 0) {
return -1;
}
return (double) prePutMoney / (double) preHands;
}
public double af() {
if (preHands == 0) {
return -1;
}
return (double) preRaise / (double) preHands;
}
public double limp() {
if (preLimpPos == 0) {
return -1;
}
return (double) preLimp / (double) preLimpPos;
}
public double coldCall() {
if (preColdCallPos == 0) {
return -1;
}
return (double) preColdCall / (double) preColdCallPos;
}
public double percent3Bet() {
if (preRespondTo2 == 0) {
return -1;
}
return (double) pre3Bet / (double) preRespondTo2;
}
public double percent4Bet() {
if (preRespondTo3 == 0) {
return -1;
}
return (double) pre4Bet / (double) preRespondTo3;
}
public double foldTo3Bet() {
if (preRespondTo3 == 0) {
return -1;
}
return (double) preFoldTo3 / (double) preRespondTo3;
}
public double foldTo4Bet() {
if (preRespondTo4 == 0) {
return -1;
}
return (double) preFoldTo4 / (double) preRespondTo4;
}
public double call3Bet() {
if (preRespondTo3 == 0) {
return -1;
}
return 1.0 - foldTo3Bet() - percent4Bet();
}
public double call4Bet() {
if (preRespondTo4 == 0) {
return -1;
}
return (double) preCall4 / (double) preRespondTo4;
}
public double cBetChance() {
if (cBetPos == 0) {
return -1;
}
return (double) cBet / cBetPos;
}
public double foldToCBetRaise() {
if (cBetRaised == 0) {
return -1;
}
return (double) foldToCBetRaise / (double) cBetRaised;
}
}
private PlayerStats find(LinkedList<PlayerStats> list, int seat) {
PlayerStats player = null;
int i;
for (i = 0; i < list.size(); i++) {
if (seat == list.get(i).seat) {
player = list.get(i);
break;
}
}
if (player == null || !gi.getPlayerName(seat).equals(player.name)) {
if (player != null) {
list.remove(i);
}
player = new PlayerStats(seat, gi.getPlayerName(seat));
list.add(player);
}
return player;
}
private double handMatcher(Card c1, Card c2, int index) {
boolean usPair = c1.getRank() == c2.getRank();
int rank1 = c1.getRank(), rank2 = c2.getRank();
if (rank1 < rank2) {
int temp = rank1;
rank1 = rank2;
rank2 = temp;
}
int them1 = this.bestHands[index][0], them2 = this.bestHands[index][1];
if (them1 < them2) {
int temp = them1;
them1 = them2;
them2 = temp;
}
boolean themPair = this.bestHands[index][0] == this.bestHands[index][1];
if (usPair && themPair) {
if (rank1 > them1) {
return 0.8;
} else if (rank1 < them1) {
return 0.2;
} else {
return 0.5;
}
} else if (usPair) {
if (rank1 > them1 && rank1 > them2) {
return 0.85;
} else if ((rank1 > them1 && rank1 < them2) || (rank1 < them1 && rank1 > them2)) {
return 0.7;
} else if (rank1 < them1 && rank1 < them2) {
return 0.55;
} else if ((rank1 == them1 && rank1 > them2) || (rank1 == them2 && rank1 > them1)) {
return 0.9;
} else if ((rank1 == them1 && rank1 < them2) || (rank1 == them2 && rank1 < them1)) {
return 0.7;
}
} else if (themPair) {
if (them1 > rank1 && them1 > rank2) {
return 0.15;
} else if ((them1 > rank1 && them1 < rank2) || (them1 < rank1 && them1 > rank2)) {
return 0.3;
} else if (them1 < rank1 && them1 < rank2) {
return 0.45;
} else if ((them1 == rank1 && them1 > rank2) || (them1 == rank2 && them1 > rank1)) {
return 0.1;
} else if ((them1 == rank1 && them1 < rank2) || (them1 == rank2 && them1 < rank1)) {
return 0.3;
}
} else { // no one has a pair
if (rank2 > them1) {
return 0.65;
} else if (them2 > rank1) {
return 0.35;
} else if (rank1 > them1 && them1 > rank2 && rank2 > them2) {
return 0.65;
} else if (them1 > rank1 && rank1 > them2 && them2 > rank2) {
return 0.35;
} else if (rank1 > them1 && them2 > rank2) {
return 0.6;
} else if (them1 > rank1 && rank2 > them2) {
return 0.4;
} else if (rank2 == them1) {
return 0.75;
} else if (them2 == rank1) {
return 0.25;
}
}
return 0.5; //should never happen
}
}