Last active
July 2, 2019 23:06
-
-
Save cjgunnar/18fdac23df41e45c3fc46d78bf63ea2f to your computer and use it in GitHub Desktop.
MatrixBot - Less sketchy than HalBot
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
package PokerBots; | |
import java.io.IOException; | |
import java.lang.reflect.InvocationTargetException; | |
import java.nio.charset.StandardCharsets; | |
import java.nio.file.FileSystems; | |
import java.nio.file.Files; | |
import java.nio.file.Path; | |
import java.nio.file.Paths; | |
import java.util.Collections; | |
import java.util.List; | |
/** | |
* You have to let it all go, Neo. Fear, doubt, and disbelief. Free your mind. | |
*/ | |
public class MatrixBot extends PokerBot | |
{ | |
//Simulation Settings | |
private static final int GAMES_PER_BOT = 100; | |
private static final int HANDS_PER_GAME = 10000; | |
/** | |
* The different bots that will be tested against the opponent | |
*/ | |
PokerBot[] strategies = new PokerBot[] | |
{ | |
new BPB(5,3), | |
new BPB(5,6), | |
new BPB(5,10), | |
new BPB(7,3), | |
new BPB(7,6), | |
new BPB(7,10), | |
new BPB(11,3), | |
new BPB(11,6), | |
new BPB(11,10), | |
}; | |
PokerBot opponent; | |
PokerBot bestStrategy; | |
double bestStrategyPercent = 0.00; | |
public MatrixBot() | |
{ | |
//System.out.println("Creating MatrixBot"); | |
//attempt to reflect the other bot, if failed run basic strategy | |
if(!setOpponent()) | |
{ | |
//run basic strategy | |
bestStrategy = new BPB(5,3); | |
} | |
else | |
{ | |
//try each strategy, pick one with highest win percentage | |
for(PokerBot strategy : strategies) | |
{ | |
//run simulations | |
GameSim game = new GameSim(strategy); | |
game.simulateGames(GAMES_PER_BOT); | |
//System.out.println("Strategy " + strategy.getBotName() + " won " + game.getWinPercentage() * 100 + "%"); | |
//see if this strategy is the best | |
if(game.getWinPercentage() > bestStrategyPercent) | |
{ | |
//make new best strategy | |
bestStrategy = strategy; | |
bestStrategyPercent = game.getWinPercentage(); | |
} | |
} | |
//System.out.println("MatrixBot: best strategy found to be " + bestStrategy.getBotName()); | |
//create a new copy incase running the simulation messed them up (i.e. for advanced learning/memory bots) | |
if(bestStrategy instanceof Cloneable && bestStrategy instanceof BPB) | |
{ | |
try | |
{ | |
bestStrategy = (PokerBot)(((BPB)bestStrategy).clone()); | |
} catch (CloneNotSupportedException e) | |
{ | |
e.printStackTrace(); | |
} | |
} | |
else | |
{ | |
try | |
{ | |
this.bestStrategy = bestStrategy.getClass().getConstructor(null).newInstance(null); | |
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | |
| InvocationTargetException | NoSuchMethodException | SecurityException e) | |
{ | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
/** | |
* Copy of Game for use of simulations | |
*/ | |
class GameSim | |
{ | |
private static final int NUM_HANDS = HANDS_PER_GAME; | |
public static final int FOLD = 1; | |
public static final int MATCH = 2; | |
public static final int RAISE = 3; | |
private static final int NUM_PLAYERS = 2; | |
private static final int INITIAL_BET = 5; | |
private static final int STARTING_MONEY = 10000; | |
private static final int MAX_BET = 10; | |
private int moneyInPot; | |
private boolean createdError = false; | |
//bot facing the opponent | |
PokerBot strategyBot; | |
double winPercentage; | |
public GameSim(PokerBot strategyBot) | |
{ | |
this.strategyBot = strategyBot; | |
this.createdError = false; | |
} | |
/** | |
* Simulate a number of games against the opponent and calculate win percentage | |
* @param num | |
*/ | |
public void simulateGames(int num) | |
{ | |
//System.out.println("MatixBot: Simulating " + num + " games with " + strategyBot.getBotName() + "..."); | |
int gamesWon = 0; | |
for(int i = 0; i < num; i++) | |
{ | |
//System.out.println("MatrixBot: Simulating Game #" + (i + 1)); | |
if(simulateGame()) gamesWon++; | |
if(createdError) | |
{ | |
winPercentage = 0.00; | |
return; | |
} | |
} | |
winPercentage = (double)gamesWon / (double)num; | |
} | |
private boolean simulateGame() | |
{ | |
PokerBot player1 = strategyBot; | |
PokerBot player2; | |
try | |
{ | |
player2 = opponent.getClass().getConstructor(null).newInstance(null); | |
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) | |
{ | |
e.printStackTrace(); | |
} | |
finally | |
{ | |
//use BasicPokerBot() if opponent can't be duplicated | |
player2 = new BasicPokerBot(); | |
} | |
//START GAME SIMULATION | |
player1.setCurrentMoney(STARTING_MONEY); | |
player2.setCurrentMoney(STARTING_MONEY); | |
playPoker(player1, player2, NUM_HANDS); | |
//return true for strategy winning, false otherwise | |
if(player1.getCurrentMoney() == player2.getCurrentMoney()) | |
{ | |
return false; //tied | |
} | |
else if(player1.getCurrentMoney() > player2.getCurrentMoney()) | |
{ | |
return true; //won | |
} | |
else //Player 2 must have won. | |
{ | |
return false; //lost | |
} | |
} | |
private void playPoker(PokerBot player1, PokerBot player2, | |
int numberOfHands) | |
{ | |
int player1Card, player2Card; | |
for(int i = 1; i <= numberOfHands; ++i) | |
{ | |
moneyInPot = 0; | |
addStartingMoney(player1, player2); | |
player1Card = (int)(Math.random() * 13) + 1; | |
player2Card = (int)(Math.random() * 13) + 1; | |
if(i % 2 == 0) | |
{ | |
playHand(player1, player1Card, player2, player2Card); | |
} | |
else | |
{ | |
playHand(player2, player2Card, player1, player1Card); | |
} | |
if(player1.getCurrentMoney() <= 0 || player2.getCurrentMoney() <= 0) | |
{ | |
//player ran out of money, break the loop | |
i = numberOfHands + 1; | |
} | |
} | |
} | |
private void addStartingMoney(PokerBot player1, PokerBot player2) | |
{ | |
//Each player adds initial Bet to the pot. | |
moneyInPot = NUM_PLAYERS * INITIAL_BET; | |
player1.setCurrentBet(INITIAL_BET); | |
player2.setCurrentBet(INITIAL_BET); | |
} | |
private void playHand(PokerBot player1, int player1Card, | |
PokerBot player2, int player2Card) | |
{ | |
moneyInPot -= player1.getCurrentBet(); | |
int previousDecision = player1.makeDecision(INITIAL_BET, 1, | |
player2Card, moneyInPot); | |
moneyInPot += player1.getCurrentBet(); | |
validateBet(previousDecision, INITIAL_BET, player1, 1); | |
moneyInPot -= player2.getCurrentBet(); | |
int currentDecision = player2.makeDecision(player1.getCurrentBet(), 2, | |
player1Card, moneyInPot); | |
moneyInPot += player2.getCurrentBet(); | |
validateBet(currentDecision, player1.getCurrentBet(), player2, 2); | |
int betCount = 3; | |
while(betCount <= 6 && currentDecision == RAISE) | |
{ | |
previousDecision = currentDecision; | |
//Player1 bets on odd bet counts | |
if(betCount % 2 == 1) | |
{ | |
moneyInPot -= player1.getCurrentBet(); | |
currentDecision = player1.makeDecision(player2.getCurrentBet(), | |
betCount, player2Card, moneyInPot); | |
moneyInPot += player1.getCurrentBet(); | |
validateBet(currentDecision, player2.getCurrentBet(), player1, betCount); | |
} | |
else | |
{ | |
moneyInPot -= player2.getCurrentBet(); | |
currentDecision = player2.makeDecision(player1.getCurrentBet(), | |
betCount, player1Card, moneyInPot); | |
moneyInPot += player2.getCurrentBet(); | |
validateBet(currentDecision, player1.getCurrentBet(), player2, betCount); | |
} | |
++betCount; | |
} | |
if(betCount % 2 == 1) | |
{ | |
//Player2 made the most current Bet | |
determineWinner(player1, previousDecision, player1Card, | |
player2, currentDecision, player2Card); | |
} | |
else | |
{ | |
//Player1 made the most current Bet | |
determineWinner(player1, currentDecision, player1Card, | |
player2, previousDecision, player2Card); | |
} | |
} | |
private void validateBet(int playerDecision, int startingBet, PokerBot player, int round) | |
{ | |
if(playerDecision == FOLD) | |
{ | |
//the player's current bet must be within MAX_BET of startingBet | |
if( !( (player.getCurrentBet() >= (startingBet - MAX_BET)) && | |
(player.getCurrentBet() <= startingBet))) | |
{ | |
System.out.println("ERROR #1: " + player.getBotName() + ": if you fold, your current bet" | |
+ " needs to stay at what you previously bet. For example if you bet $5 dollars, and the" | |
+ " other players raises to $12 and you fold, your current bet should stay at $5."); | |
} | |
} | |
if(playerDecision == MATCH) | |
{ | |
if(player.getCurrentBet() != startingBet) | |
{ | |
System.out.println("ERROR #2: " + player.getBotName() + | |
" did not correctly match the bet. Set" | |
+ " current bet to be equal to" | |
+ " otherPlayersBet."); | |
} | |
} | |
if(playerDecision == RAISE) | |
{ | |
if(player.getCurrentBet() <= startingBet | |
|| player.getCurrentBet() > (startingBet + MAX_BET)) | |
{ | |
System.out.println("ERROR #3: " + player.getBotName() + | |
": If you choose raise, your bet amount " | |
+ "must be set to a number 1 to 10 dollars greater" | |
+ " than otherPlayersBet. Example: otherPlayers bet is 22," | |
+ " if you raise your bet should be 23 to 32 dollars."); | |
} | |
} | |
if(playerDecision != RAISE && playerDecision != FOLD && playerDecision != MATCH) | |
{ | |
System.out.println("ERROR #4: " + player.getBotName() + | |
" did not return RAISE, FOLD or CALL"); | |
} | |
if(round == 1 && playerDecision == FOLD) | |
{ | |
System.out.println("ERROR #5: " + player.getBotName() + ": you cannot fold in round 1"); | |
} | |
if(round == 6 && playerDecision == RAISE) | |
{ | |
System.out.println("ERROR #6: " + player.getBotName() + ": you cannot raise in round 6"); | |
} | |
} | |
private void determineWinner(PokerBot player1, int player1Bet, int player1Card, | |
PokerBot player2, int player2Bet, int player2Card) | |
{ | |
//Subtract whatever they bet. | |
//the winner will have money added in the code below | |
player1.setCurrentMoney(player1.getCurrentMoney() - player1.getCurrentBet()); | |
player2.setCurrentMoney(player2.getCurrentMoney() - player2.getCurrentBet()); | |
if(player2Bet == FOLD) | |
{ | |
// System.out.println(player1.getBotName() + " won " | |
// + moneyInPot + " dollars this hand."); | |
player1.setCurrentMoney(player1.getCurrentMoney() + moneyInPot); | |
} | |
else if(player1Bet == FOLD) | |
{ | |
// System.out.println(player2.getBotName() + " won " | |
// + moneyInPot + " dollars this hand."); | |
player2.setCurrentMoney(player2.getCurrentMoney() + moneyInPot); | |
} | |
else | |
{ | |
if(player1Card == player2Card) | |
{ | |
//tie, they get their money back | |
//Must be an even amount of money, so don't have | |
//to worry about rounding problems... | |
player1.setCurrentMoney(player1.getCurrentMoney() + (moneyInPot/2)); | |
player2.setCurrentMoney(player2.getCurrentMoney() + (moneyInPot/2)); | |
// System.out.println("Game was a tie. Both players" | |
// + " get their money back."); | |
} | |
else if(player1Card > player2Card) | |
{ | |
player1.setCurrentMoney(player1.getCurrentMoney() + moneyInPot); | |
// System.out.println(player1.getBotName() + " won " | |
// + moneyInPot + " this hand."); | |
} | |
else //player2 must have won... | |
{ | |
player2.setCurrentMoney(player2.getCurrentMoney() + moneyInPot); | |
// System.out.println(player2.getBotName() + " won " | |
// + moneyInPot + " this hand."); | |
} | |
} | |
} | |
public PokerBot getStrategyBot() | |
{ | |
return strategyBot; | |
} | |
public double getWinPercentage() | |
{ | |
return winPercentage; | |
} | |
} | |
private boolean setOpponent() | |
{ | |
//System.out.println("Finding Opponent..."); | |
try | |
{ | |
//read Game.java file for mention of opponent | |
Path gameFile = FileSystems.getDefault().getPath("src/PokerBots/Game.java").toAbsolutePath(); | |
List<String> lines = Collections.emptyList(); | |
lines = Files.readAllLines(Paths.get(gameFile.toUri()), StandardCharsets.UTF_8); | |
String opponentName = ""; | |
for(String s : lines) | |
{ | |
if(s.trim().contains("PokerBot player1") || s.trim().contains("PokerBot player2") && s.contains("()")) | |
{ | |
//don't simulate against itself | |
if(!s.contains("MatrixBot")) | |
{ | |
//based on the line being: "PokerBot playerX = new OpponentBot();" get the name of the opponent class | |
int newIdx = s.indexOf("new "); | |
int bracketIdx = s.indexOf("("); | |
opponentName = s.substring(newIdx + 4, bracketIdx).trim(); | |
//System.out.println("Opponent is " + opponentName); | |
break; | |
} | |
} | |
} | |
//safety check | |
if(!opponentName.equals("")) | |
{ | |
//get instance of opponent for simulation | |
@SuppressWarnings("unchecked") | |
Class<PokerBot> opponentClass = (Class<PokerBot>) Class.forName("PokerBots."+opponentName); | |
opponent = opponentClass.getDeclaredConstructor(null).newInstance(null); | |
return true; | |
} | |
} | |
catch (IOException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | ClassNotFoundException e) | |
{ | |
//error | |
e.printStackTrace(); | |
} | |
return false; | |
} | |
@Override | |
public String getBotName() | |
{ | |
return "MatrixBot [" + bestStrategy.getBotName() + " " + bestStrategyPercent*100 + "%]"; | |
} | |
@Override | |
public void setCurrentBet(int betIn) | |
{ | |
super.setCurrentBet(betIn); | |
//pass down to strategy bot | |
bestStrategy.setCurrentBet(betIn); | |
} | |
@Override | |
public void setCurrentMoney(int moneyIn) | |
{ | |
super.setCurrentMoney(moneyIn); | |
//pass down to strategy bot | |
bestStrategy.setCurrentMoney(moneyIn); | |
} | |
@Override | |
public int makeDecision(int otherPlayersBet, int round, int otherPlayersCard, int moneyInPot) | |
{ | |
//use internal strategy bot to make the decision | |
int decision = bestStrategy.makeDecision(otherPlayersBet, round, otherPlayersCard, moneyInPot); | |
int bet = bestStrategy.getCurrentBet(); | |
//use for MatrixBot so Game can read correct numbers | |
this.currentBet = bet; | |
return decision; | |
} | |
//Strategies | |
//what card number (1 to 13) to fold on | |
//RISKY fold if other > 10 | |
//STANDARD 7 | |
//SAFE 5 | |
//what flat-rate to raise by | |
//RAISE_HIGH raise by 10 | |
//RAISE_MID 6 | |
//RAISE_LOW 3 | |
/** | |
* Configurable BasicPokerBot | |
*/ | |
class BPB extends PokerBot implements Cloneable | |
{ | |
int riskThreshold; | |
int raiseAmount; | |
public BPB(int riskThreshold, int raiseAmount) | |
{ | |
this.riskThreshold = riskThreshold; | |
this.raiseAmount = raiseAmount; | |
} | |
@Override | |
public Object clone() throws CloneNotSupportedException | |
{ | |
return new BPB(this.riskThreshold, this.raiseAmount); | |
} | |
/** | |
* Name is in the form "BPB_(riskThreshold)_(raiseAmount) | |
* ex BPB_5_3 | |
*/ | |
@Override | |
public String getBotName() | |
{ | |
return "BPB_" + riskThreshold +"_" + raiseAmount; | |
} | |
/** | |
* Copied from original BasicPokerBot, but with configurable settings added | |
*/ | |
@Override | |
public int makeDecision(int otherPlayersBet, int round, int otherPlayersCard, int moneyInPot) | |
{ | |
if(round == 6) | |
{ | |
currentBet = otherPlayersBet; | |
return Game.MATCH; | |
} | |
else | |
{ | |
if(otherPlayersCard > riskThreshold) | |
{ | |
//Not necessary, just showing that we keep current bet | |
//currentBet = currentBet; | |
if(currentBet == otherPlayersBet) | |
return Game.MATCH; | |
else | |
return Game.FOLD; | |
} | |
else | |
{ | |
currentBet = otherPlayersBet + raiseAmount; | |
return Game.RAISE; | |
} | |
} | |
} | |
} | |
//TODO new and crazy strategies that only work against 1% of all bots? | |
//just have to create the class here and add to array above | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment