import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
public class BlackJackPlayer {
public static long statesGenerated = 0;
public static int rewardStatesReached = 0;
public static class Util
{
public static <K, V> Map<K, V> create(Collection<K> keys, V value) {
Map<K, V> map = new LinkedHashMap<K, V>();
for (K k : keys) {
map.put(k, value);
}
return map;
}
}
// all the available actions
public static class A implements Comparable <A>
{
char action;
public A (char c)
{
this.action = c;
}
@Override
public int compareTo(A foo)
{
if (foo.action == this.action)
return 0;
if (foo.action < this.action)
return -1;
if (foo.action > this.action)
return 1;
return 1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + action;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
A other = (A) obj;
if (action != other.action) {
return false;
}
return true;
}
}
public static class Mdp
{
private HashMap<Integer, State> states = null;
private int numStates = 0;
private double faceCardProbability = -1;
private double otherCardsProbability = -1;
public HashMap<Integer, State> getStatesHashMap()
{
assert(states!=null);
return states;
}
public Mdp(double faceCardProbability)
{
this.faceCardProbability = faceCardProbability;
this.otherCardsProbability = (1 - faceCardProbability) / 9.0;
}
public Collection<State> states()
{
// if we've already generated states, just return them
if (states != null)
return states.values();
// let's generate all possible states and cache them
states = new HashMap<Integer, State>(220000);
numStates = 0;
// start with the dealer state
for (int dealerSum = 2; dealerSum<=11; dealerSum++)
{
int dealerNumAces = 0;
if (dealerSum == 11)
dealerNumAces = 1;
// generate starting states
int numAces = 0;
boolean isTwoCards = true;
boolean playerTurn = true;
int pairValue = 0;
for (int minSum = 5; minSum <= 19; minSum++)
{
State s = new State();
s.minSum = minSum;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = minSum + "";
s.generateNextStates(states, this.faceCardProbability, this.otherCardsProbability);
states.put(s.hashCode(), s);
}
numAces = 1;
for (int minSum = 3; minSum <= 10; minSum++)
{
State s = new State();
s.minSum = minSum;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = "A" + minSum;
s.generateNextStates(states, this.faceCardProbability, this.otherCardsProbability);
states.put(s.hashCode(), s);
}
// add pairs
numAces = 0;
for (pairValue = 2; pairValue <= 10; pairValue++)
{
State s = new State();
s.minSum = pairValue * 2;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = pairValue + "" + pairValue;
s.generateNextStates(states, this.faceCardProbability, this.otherCardsProbability);
states.put(s.hashCode(), s);
}
// add the pair of aces
numAces = 2;
for (pairValue = 1; pairValue <= 1; pairValue++)
{
State s = new State();
s.minSum = pairValue * 2;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = "AA";
s.generateNextStates(states, this.faceCardProbability, this.otherCardsProbability);
states.put(s.hashCode(), s);
}
numStates = states.size();
}
System.out.println("Done (states): " + numStates);
return states.values();
}
public double reward(State s) {
// at the start of blackjack, there is a buyin. thus the reward is -1
return s.reward;
}
public Set<A> actions(State s) {
return s.availableActions;
}
public Double transitionProbability(State sDelta, State s, A a) {
switch(a.action)
{
case 'S':
return s.standStates.get(sDelta);
case 'H':
return s.hitStates.get(sDelta);
case 'P':
return s.splitStates.get(sDelta);
case 'D':
return s.doubleStates.get(sDelta);
default:
assert(false);
return 0.0;
}
}
}
public static class ValueIteration
{
private double gamma;
public ValueIteration(double gamma)
{
this.gamma = gamma;
}
public Map<State, Double> valueIteration(Mdp mdp,
double epsilon)
{
//
// local variables: U, U', vectors of utilities for states in S,
// initially zero
Map<State, Double> U = Util.create(mdp.states(), new Double(0));
Map<State, Double> Udelta = Util.create(mdp.states(), new Double(0));
// δ the maximum change in the utility of any state in an
// iteration
double delta = 0;
// Note: Just calculate this once for efficiency purposes:
// ε(1 - γ)/γ
double minDelta = epsilon;//;0.05;//epsilon * (1 - gamma) / gamma;
System.out.println("U+" + U.size());
System.out.println("UDelta:" + Udelta.size());
// repeat
do {
// U <- U'; δ <- 0
U.putAll(Udelta);
delta = 0;
// for each state s in S do
for (State s : mdp.states()) {
// max<sub>a ∈ A(s)</sub>
Set<A> actions = mdp.actions(s);
// Handle terminal states (i.e. no actions).
double aMax = 0;
if (actions.size() > 0) {
aMax = Double.NEGATIVE_INFINITY;
}
for (A a : actions) {
// Σ<sub>s'</sub>P(s' | s, a) U[s']
double aSum = 0;
Collection<State> deltaStates = s.statesMatchingAction(a);
for (State sDelta : deltaStates) {
Double uget = U.get(sDelta);
aSum += mdp.transitionProbability(sDelta, s, a) * uget;
}
// if (s.minSum == 2 && s.numAces == 2 && s.dMinSum == 1 &&
// s.dNumAces == 1 && s.isTwoCards && s.playerTurn)
// {
// System.out.println (a.action + ":" + aSum + s);
// }
if (aSum >= aMax) {
aMax = aSum;
s.bestPolicy = a.action;
}
}
// U'[s] <- R(s) + γ
// max<sub>a ∈ A(s)</sub>
Udelta.put(s, mdp.reward(s) + gamma * aMax);
// if |U'[s] - U[s]| > δ then δ <- |U'[s] - U[s]|
double aDiff = Math.abs(Udelta.get(s) - U.get(s));
s.expectedValue = Udelta.get(s);
if (aDiff > delta) {
delta = aDiff;
}
}
// until δ < ε(1 - γ)/γ
if (isDebug)
System.out.println("D:" + delta);
} while (delta > minDelta);
// return U
return U;
}
}
public static class State implements Comparable<State>
{
@Override
public String toString() {
return String
.format("State [minSum=%s, numAces=%s, dMinSum=%s, dNumAces=%s, isTwoCards=%s, isBlackjack=%s, pair=%s, playerTurn=%s, doubleReward=%s, reward=%s, depth=%s]",
minSum, numAces, dMinSum, dNumAces, isTwoCards,
isBlackjack, pair, playerTurn, doubleReward,
reward, depth);
}
public State() {
super();
BlackJackPlayer.statesGenerated++;
}
int minSum = 0;
int numAces = 0;
int dMinSum = 0;
int dNumAces = 0;
boolean isTwoCards = true;
boolean isBlackjack = false;
/// 0 = no pair. otherwise value indicates which number the pair is of (1 = 1, 1 cards)
int pair = 0;
/// if playerTurn is false, then it's the dealer's turn
boolean playerTurn = true;
String friendlyName = null;
boolean doubleReward = false;
// each state has an associated reward
double reward = 0;
int depth = 0;
// each state has available actions
// if we're in an end state, then there are no available actions
Set<A> availableActions = new HashSet<A>();
// expected value and best policy
double expectedValue = 0;
char bestPolicy = 'X';
// for each action there are associated neighbour states
// list of states and associated transition probability
HashMap<State, Double> hitStates = new HashMap<State,Double>(0);
HashMap<State, Double> standStates = new HashMap<State,Double>(0);
HashMap<State, Double> splitStates = new HashMap<State,Double>(0);
HashMap<State, Double> doubleStates = new HashMap<State,Double>(0);
public int playerScore()
{
// if the lower score more than 21, then we're in trouble
// no need to figure out a better score
if (minSum > 21)
{
return this.minSum;
}
int sumWithoutAces = minSum - numAces;
int maxSum = minSum;
int newSum = 0;
int oldSum = minSum;
// how many aces can we set to be 11
for (int i=1;i <= numAces; i++)
{
int numAcesToUseAsEleven = i;
int numAcesToUseAsOne = numAces - i;
newSum = sumWithoutAces + (11*numAcesToUseAsEleven) + (1*numAcesToUseAsOne);
if (newSum > 21)
{
maxSum = oldSum;
break;
}
else
{
maxSum = newSum;
oldSum = newSum;
}
}
return maxSum;
}
public boolean isDealerBlackjack()
{
if (dNumAces!=1)
{
return false;
}
if (dMinSum == 11)
{
return true;
}
else
{
return false;
}
}
public int dealerScore()
{
// if the lower score more than 21, then we're in trouble
// no need to figure out a better score
if (dMinSum > 21)
{
return this.dMinSum;
}
int sumWithoutAces = dMinSum - dNumAces;
int maxSum = dMinSum;
int newSum = 0;
int oldSum = dMinSum;
// how many aces can we set to be 11
for (int i=1;i <= dNumAces; i++)
{
int numAcesToUseAsEleven = i;
int numAcesToUseAsOne = dNumAces - i;
newSum = sumWithoutAces + (11*numAcesToUseAsEleven) + (1*numAcesToUseAsOne);
if (newSum > 21)
{
maxSum = oldSum;
break;
}
else
{
maxSum = newSum;
oldSum = newSum;
}
}
return maxSum;
}
public Set<State> statesMatchingAction(A a)
{
switch(a.action)
{
case 'S':
return this.standStates.keySet();
case 'H':
return this.hitStates.keySet();
case 'P':
return this.splitStates.keySet();
case 'D':
return this.doubleStates.keySet();
default:
System.out.println("Weird state " + a.action);
assert(false);
return null;
}
}
public void generateNextStates(HashMap<Integer,State> states,
double faceCardProbability, double otherCardProbability)
{
//System.out.println (states.size());
// let's see what actions are possible
if (playerTurn)
{
// can I double ?
doDoubleAction(states, faceCardProbability, otherCardProbability);
// can I hit ?
doHitAction(states, faceCardProbability, otherCardProbability);
// can I stand ?
doStandAction(states, faceCardProbability, otherCardProbability);
// can I split ?
doSplitAction(states, faceCardProbability, otherCardProbability);
}
// what about if it's the dealers turn ?
// the next action will be to continue standing, but follow the dealer policy
if (!playerTurn)
{
doDealerPolicy(states, faceCardProbability, otherCardProbability);
}
}
private void generateNextStatesNoSplit(HashMap<Integer, State> states,
double faceCardProbability, double otherCardProbability) {
// let's see what actions are possible
if (playerTurn)
{
// can I double ?
doDoubleAction(states, faceCardProbability, otherCardProbability);
// can I hit ?
doHitAction(states, faceCardProbability, otherCardProbability);
// can I stand ?
doStandAction(states, faceCardProbability, otherCardProbability);
}
// what about if it's the dealers turn ?
// the next action will be to continue standing, but follow the dealer policy
if (!playerTurn)
{
doDealerPolicy(states, faceCardProbability, otherCardProbability);
}
}
private void doStandAction(HashMap<Integer,State> states, double faceCardProbability,
double otherCardProbability) {
// generate states from double
State s = new State();
s.minSum = this.minSum;
s.numAces = this.numAces;
s.dMinSum = this.dMinSum;
s.dNumAces = this.dNumAces;
s.isTwoCards = this.isTwoCards; // we've just done a hit
s.isBlackjack = this.isBlackjack;
s.pair = this.pair;
s.playerTurn = false;
assert (s.doubleReward == false);
assert (s.reward == 0);
Double transitionProb = 1.0;
if (states.containsKey(s.hashCode()) && s.equals(states.get(s.hashCode())))
{
this.standStates.put(states.get(s.hashCode()), transitionProb);
}
else
{
s.generateNextStates(states, faceCardProbability, otherCardProbability);
this.standStates.put(s, transitionProb);
states.put(s.hashCode(), s); // TODO ????? reverse lines
}
this.availableActions.add(new A('S'));
}
private void doHitAction(HashMap<Integer,State> states, double faceCardProbability,
double otherCardProbability) {
if (this.playerTurn && this.minSum < 21)
{
for (int newCard = 1; newCard <= 10; newCard++)
{
State s = new State();
s.minSum = this.minSum + newCard;
if (newCard == 1)
{
s.numAces = numAces + 1;
}
else
{
s.numAces = numAces;
}
s.dMinSum = this.dMinSum;
s.dNumAces = this.dNumAces;
s.isTwoCards = false; // we've just done a hit
s.isBlackjack = this.isBlackjack;
s.pair = 0;
if (s.minSum > 21)
{
// don't have to worry about double awards
s.reward = -1;
s.playerTurn = false;
}
else
{
s.playerTurn = playerTurn;
assert (s.doubleReward == false);
}
Double transitionProb;
if (newCard == 10)
{
transitionProb = faceCardProbability;
}
else
{
transitionProb = otherCardProbability;
}
if (states.containsKey(s.hashCode()) && s.equals(states.get(s.hashCode())))
{
this.hitStates.put(states.get(s.hashCode()), transitionProb);
}
else
{
s.generateNextStates(states, faceCardProbability, otherCardProbability);
this.hitStates.put(s, transitionProb);
states.put(s.hashCode(), s);
}
this.availableActions.add(new A('H'));
}
}
}
private void doDealerPolicy(HashMap<Integer,State> states,
double faceCardProbability, double otherCardProbability) {
// if this state has a non-zero reward with it, we're done. no actions need to be generated
if (reward != 0)
{
assert (hitStates.size() == 0);
assert (doubleStates.size() == 0);
assert (splitStates.size() == 0);
assert (standStates.size() == 0);
return;
}
// calculate currentPlayer score
int playerScore = this.playerScore();
double rewardMultiplier = 1;
if (doubleReward)
{
rewardMultiplier = 2;
}
if (this.dealerScore() >= 17 || this.minSum > 21)
{ // the dealer has stopped hitting or the player has busted
return;
}
else
{
for (int newCard = 1; newCard <= 10; newCard++)
{
// generate states from double
State s = new State();
s.depth = this.depth + 1;
//System.out.print(s.depth + " ");
s.minSum = this.minSum;
s.numAces = this.numAces;
if (newCard == 1)
{
s.dNumAces = this.dNumAces + 1;
}
else
{
s.dNumAces = this.dNumAces;
}
s.dMinSum = this.dMinSum + newCard;
s.isTwoCards = this.isTwoCards;
s.isBlackjack = this.isBlackjack;
s.pair = this.pair;
s.playerTurn = this.playerTurn;
s.doubleReward = this.doubleReward;
Double transitionProb;
if (newCard == 10)
{
transitionProb = faceCardProbability;
}
else
{
transitionProb = otherCardProbability;
}
int newDealerScore = s.dealerScore();
if (newDealerScore >= 17)
{
if (newDealerScore > 21)
{
s.reward = 1 * rewardMultiplier;
}
else if (newDealerScore > playerScore)
{ // dealer wins
s.reward = -1 * rewardMultiplier;
}
else if (newDealerScore < playerScore)
{
s.reward = 1 * rewardMultiplier;
}
else if (newDealerScore == playerScore)
{
if (s.isDealerBlackjack())
{
s.reward = -1 * rewardMultiplier;
}
else
{
s.reward = 0;
}
}
}
if (states.containsKey(s.hashCode()) && s.equals(states.get(s.hashCode())))
{
this.standStates.put(states.get(s.hashCode()), transitionProb);
}
else
{
this.standStates.put(s, transitionProb);
if (newDealerScore < 17)
{
s.generateNextStates(states, faceCardProbability, otherCardProbability);
}
states.put(s.hashCode(), s);
}
this.availableActions.add(new A('S'));
}
return;
}
}
private void doDoubleAction(HashMap<Integer,State> states,
double faceCardProbability, double otherCardProbability) {
if (isTwoCards && playerTurn)
{
// generate states from double
for (int newCard = 1; newCard <= 10; newCard++)
{
State s = new State();
s.minSum = this.minSum + newCard;
if (newCard == 1)
{
s.numAces = this.numAces + 1;
}
else
{
s.numAces = this.numAces;
}
s.dMinSum = this.dMinSum;
s.dNumAces = this.dNumAces;
s.isTwoCards = false; // isTwoCards tells us that this is a pair that is being doubled
s.isBlackjack = this.isBlackjack;
s.pair = this.pair;
s.playerTurn = false; // we can no longer take any action
s.doubleReward = true;
if (s.minSum > 21)
{
// don't have to worry about double awards here
s.reward = -2;
}
Double transitionProb;
if (newCard == 10)
{
transitionProb = faceCardProbability;
}
else
{
transitionProb = otherCardProbability;
}
if (states.containsKey(s.hashCode()) && s.equals(states.get(s.hashCode())))
{
this.doubleStates.put(states.get(s.hashCode()), transitionProb);
}
else
{
s.generateNextStates(states, faceCardProbability, otherCardProbability);
this.doubleStates.put(s, transitionProb);
states.put(s.hashCode(), s);
}
this.availableActions.add(new A('D'));
}
}
}
private void doSplitAction(HashMap<Integer,State> states,
double faceCardProbability, double otherCardProbability) {
if (playerTurn && pair > 0 && isTwoCards)
{
if (numAces!=2) // normal splitting
{
// generate states from double
for (int newCard = 1; newCard <= 10; newCard++)
{
State s = new State();
s.minSum = this.pair + newCard;
if (newCard == 1)
{
s.numAces = numAces + 1;
}
else
{
s.numAces = numAces;
}
s.dMinSum = this.dMinSum;
s.dNumAces = this.dNumAces;
s.isTwoCards = this.isTwoCards; // isTwoCards tells us that this is a pair that is being doubled
s.isBlackjack = this.isBlackjack;
if (newCard == pair)
{
s.pair = this.pair;
}
else
{
s.pair = 0;
}
s.playerTurn = this.playerTurn;
// if the new card is an ace while pair is a 10, we have blackjack
if (this.pair == 10 && newCard == 1)
{
s.isBlackjack = true;
s.reward = 3.0;
s.playerTurn = false;
}
else
{
s.doubleReward = true;
}
Double transitionProb;
if (newCard == 10)
{
transitionProb = faceCardProbability;
}
else
{
transitionProb = otherCardProbability;
}
if (states.containsKey(s.hashCode()) && s.equals(states.get(s.hashCode())))
{
this.splitStates.put(states.get(s.hashCode()), transitionProb);
}
else
{
this.splitStates.put(s, transitionProb);
s.generateNextStatesNoSplit(states, faceCardProbability, otherCardProbability);
states.put(s.hashCode(), s);
}
this.availableActions.add(new A('P'));
}
}
else if (numAces == 2) // splitting 2 aces
{
assert (this.pair == 1); // (2 Aces)
// generate states from double
for (int newCard = 1; newCard <= 10; newCard++)
{
State s = new State();
s.minSum = this.pair + newCard;
if (newCard != 1) // we now only have 1 aces in the newer state
{
s.numAces = this.numAces - 1;
}
else
{
s.numAces = this.numAces;
}
s.dMinSum = this.dMinSum;
s.dNumAces = this.dNumAces;
s.isTwoCards = this.isTwoCards; // isTwoCards tells us that this is a pair that is being doubled
s.isBlackjack = this.isBlackjack;
if (newCard != pair)
{
s.pair = 0;
}
// this now the dealers turn. we only get 1 additional card
s.playerTurn = false;
s.doubleReward = true;
Double transitionProb;
if (newCard == 10)
{
transitionProb = faceCardProbability;
}
else
{
transitionProb = otherCardProbability;
}
if (newCard == 10)
{
s.isBlackjack = false;
s.reward = 0;
s.playerTurn = false;
}
if (states.containsKey(s.hashCode()) && s.equals(states.get(s.hashCode())))
{
this.splitStates.put(states.get(s.hashCode()), transitionProb);
}
else
{
this.splitStates.put(s, transitionProb);
s.generateNextStates(states, faceCardProbability, otherCardProbability);
states.put(s.hashCode(), s);
}
this.availableActions.add(new A('P'));
}
}
}
}
@Override
public int compareTo(State s1)
{
if (s1.minSum == this.minSum &&
s1.numAces == this.numAces &&
s1.dMinSum == this.dMinSum &&
s1.dNumAces == this.dNumAces &&
s1.isTwoCards == this.isTwoCards &&
s1.isBlackjack == this.isBlackjack &&
s1.pair == this.pair &&
s1.playerTurn == this.playerTurn)
{
return 0;
}
if (this.minSum > s1.minSum)
return 1;
else
return -1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + dMinSum;
result = prime * result + dNumAces;
result = prime * result + (doubleReward ? 1231 : 1237);
result = prime * result + (isBlackjack ? 1231 : 1237);
result = prime * result + (isTwoCards ? 1231 : 1237);
result = prime * result + minSum;
result = prime * result + numAces;
result = prime * result + pair;
result = prime * result + (playerTurn ? 1231 : 1237);
long temp;
temp = Double.doubleToLongBits(reward);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
State other = (State) obj;
if (dMinSum != other.dMinSum) {
return false;
}
if (dNumAces != other.dNumAces) {
return false;
}
if (doubleReward != other.doubleReward) {
return false;
}
if (isBlackjack != other.isBlackjack) {
return false;
}
if (isTwoCards != other.isTwoCards) {
return false;
}
if (minSum != other.minSum) {
return false;
}
if (numAces != other.numAces) {
return false;
}
if (pair != other.pair) {
return false;
}
if (playerTurn != other.playerTurn) {
return false;
}
if (Double.doubleToLongBits(reward) != Double
.doubleToLongBits(other.reward)) {
return false;
}
return true;
}
}
private static boolean isDebug = true;
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
if (args.length != 1)
{
System.out.println("you need to specify the probability of a face card");
return;
}
double faceCardProbability = Double.parseDouble(args[0]);
Mdp mdp = new Mdp(faceCardProbability);
double gamma = 1;
double epsilon = 0.000001;
ValueIteration vi = new ValueIteration(gamma);
vi.valueIteration(mdp, epsilon);
System.out.println("Face card probability: " + faceCardProbability);
System.out.println("Num state objects created: " + BlackJackPlayer.statesGenerated);
createOutputFile(mdp, "Policy.txt");
}
public static void createOutputFile(Mdp mdp, String fileName)
{
String newLine = System.getProperty("line.separator");
String tabChar = "\t";
FileWriter outputFw;
try {
outputFw = new FileWriter(fileName, false /*append*/);
BufferedWriter bw = new BufferedWriter(outputFw);
// if we've already generated states, just return them
// let's generate all possible states and cache them
HashMap<Integer, State> states = mdp.getStatesHashMap();
// generate starting states
int numAces = 0;
boolean isTwoCards = true;
boolean playerTurn = true;
int pairValue = 0;
for (int minSum = 5; minSum <= 19; minSum++)
{
bw.write(minSum + tabChar);
for (int dealerSum = 2; dealerSum <=11; dealerSum++)
{
int dealerNumAces = 0;
if (dealerSum == 11)
dealerNumAces = 1;
State s = new State();
s.minSum = minSum;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = minSum + "";
bw.write(states.get(s.hashCode()).bestPolicy);
if (dealerSum!=11)
bw.write(" ");
}
bw.write(newLine);
}
numAces = 1;
for (int minSum = 3; minSum <= 10; minSum++)
{
bw.write("A" + (minSum-1) + tabChar);
for (int dealerSum = 2; dealerSum <=11; dealerSum++)
{
int dealerNumAces = 0;
if (dealerSum == 11)
dealerNumAces = 1;
State s = new State();
s.minSum = minSum;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = "A" + minSum;
bw.write(states.get(s.hashCode()).bestPolicy);
if (dealerSum!=11)
bw.write(" ");
}
bw.write(newLine);
}
// add pairs
numAces = 0;
for (pairValue = 2; pairValue <= 10; pairValue++)
{
bw.write(pairValue + "" + pairValue + tabChar);
for (int dealerSum = 2; dealerSum <=11; dealerSum++)
{
int dealerNumAces = 0;
if (dealerSum == 11)
dealerNumAces = 1;
State s = new State();
s.minSum = pairValue * 2;
s.numAces = numAces;
s.dMinSum = dealerSum - 10*dealerNumAces;
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = pairValue + "" + pairValue;
bw.write(states.get(s.hashCode()).bestPolicy);
if (dealerSum!=11)
bw.write(" ");
}
bw.write(newLine);
}
// add the pair of aces
numAces = 2;
for (pairValue = 1; pairValue <= 1; pairValue++)
{
bw.write("AA" + tabChar);
for (int dealerSum = 2; dealerSum <=11; dealerSum++)
{
int dealerNumAces = 0;
if (dealerSum == 11)
dealerNumAces = 1;
State s = new State();
s.minSum = pairValue * 2;
s.numAces = numAces;
s.dMinSum = (dealerSum - 10*dealerNumAces);
s.dNumAces = dealerNumAces;
s.isTwoCards = isTwoCards;
s.isBlackjack = false;
s.pair = pairValue;
s.playerTurn = playerTurn;
s.friendlyName = "AA";
bw.write(states.get(s.hashCode()).bestPolicy);
if (dealerSum!=11)
bw.write(" ");
}
}
bw.close();
outputFw.close();
} catch (IOException e) {
System.out.println("Unable to output file");
e.printStackTrace();
}
}
}