Code:
NE Push [R=8]:
A K Q J T 9 8 7 6 5 4 3 2
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 A
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 K
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 Q
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 J
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 T
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 0.000 9
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 0.000 8
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 7
1.000 1.000 1.000 0.000 0.000 0.000 0.000 1.000 1.000 1.000 1.000 0.000 0.000 6
1.000 1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 1.000 1.000 0.000 5
1.000 1.000 0.192 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.000 0.000 4
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.000 3
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 2
NE Call [R=8]:
A K Q J T 9 8 7 6 5 4 3 2
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 A
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 K
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 Q
1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 0.000 0.000 0.000 J
1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 T
1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 9
1.000 1.000 1.000 0.000 0.000 0.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 8
1.000 1.000 0.528 0.000 0.000 0.000 0.000 1.000 0.000 0.000 0.000 0.000 0.000 7
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.000 0.000 0.000 0.000 6
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.000 0.000 0.000 5
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.000 0.000 4
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 0.000 3
1.000 1.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 1.000 2
SB's EV: -0.0041376 BB/hand (Push=61.8629%)
BB's EV: 0.00413759 BB/hand (Call=44.973%)
and: -0.0041376 + 0.5 = 0.4958624.
Computed using 100k iterations of fictitious play:
Code:
#define RAKE 0.0 // 0.5=1BB from the 20BB pot (ie: 0.5 each, so if tie we get back 9.5BB).
// Use this to calculate the push/call matrices for a given R.
void RunFictitiousPlay(const double RRatio,const int MaxIters,const double UpdateRate,
double* PushMatrix,double* CallMatrix)
{
// These are the temp matrices that hold the final NE strategy.
double TempPushMatrix[169];
double TempCallMatrix[169];
// Init the matrices to 1.0 for all values.
// NOTE: Either 0.5 or 1 seems to converge at about the same rate (0.0 was very slow...)
for (int I=0;I<169;I++) {
PushMatrix[I]=1.0;
CallMatrix[I]=1.0;
}
// Keep running the fictitious play for a set number of iterations.
for (int Iter=0;Iter<MaxIters;Iter++) {
// If we have been passed UpdateRate=0.0 then use true game theory method, else just
// use UpdateRate as the weight.
double WeightNew=(UpdateRate<EPSILON?(1.0/(double)(Iter+1)):UpdateRate);
// Update SB strategy...
// For each possible hand work out if it is +EV or -EV to call vs the main matrix.
for (int I=0;I<169;I++) {
// Init EV_Sum to 0 for calling with this hand.
double EV_Push=0.0;
// This is the sum of hole multiplicity values.
double NormalizationSum=0.0;
// Work out the EV vs each of the opposing hands.
for (int J=0;J<169;J++) {
// EV when we push and he don't fold.
EV_Push+=CallMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(((2.0*(RRatio+RAKE)*PWin[I][J])+((RRatio+RAKE)*PTie[I][J]))-RRatio);
// EV when we push and he folds.
EV_Push+=(1.0-CallMatrix[J])*(double)HoleMultiplicityIfHolding[I][J]*1.0;
// Keep a total of the sums so we can use to normalize after.
NormalizationSum+=(double)HoleMultiplicityIfHolding[I][J];
}
// Normalize the EV_Push value.
EV_Push/=NormalizationSum;
// If the sum of EV is +ve then we should play this hand, else we shouldn't.
if (EV_Push>(-0.5))
TempPushMatrix[I]=1.0;
else
TempPushMatrix[I]=0.0;
}
// Update the push matrix.
// NOTE: We tend to get better convergence if we replace this before updating the BB strategy below.
for (int I=0;I<169;I++)
PushMatrix[I]=((1.0-WeightNew)*PushMatrix[I]) + (WeightNew*TempPushMatrix[I]);
// Update BB strategy...
// For each possible hand work out if it is +EV or -EV to call vs the main matrix.
for (int I=0;I<169;I++) {
// Init EV_Sum to 0 for calling with this hand.
double EV_Call=0.0;
// This is the sum of hole multiplicity values.
double NormalizationSum=0.0;
// Work out the EV vs each of the opposing hands.
for (int J=0;J<169;J++) {
// EV when he pushes and we call.
EV_Call+=PushMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(((2.0*(RRatio+RAKE)*PWin[I][J])+((RRatio+RAKE)*PTie[I][J]))-RRatio);
// Keep a total of the sums so we can use to normalize after.
NormalizationSum+=PushMatrix[J]*(double)HoleMultiplicityIfHolding[I][J];
}
// Normalize the EV_Push value.
EV_Call/=NormalizationSum;
// If the sum of EV is +ve then we should play this hand, else we shouldn't.
if (EV_Call>(-1.0))
TempCallMatrix[I]=1.0;
else
TempCallMatrix[I]=0.0;
}
// Update the call matrix.
for (int I=0;I<169;I++)
CallMatrix[I]=((1.0-WeightNew)*CallMatrix[I]) + (WeightNew*TempCallMatrix[I]);
}
} // End RunFictitiousPlay.
(just set the RAKE constant to zero - that was just a test to see how it changed things).
I haven't got code to run limp/jam/fold, but can easily write some if you still haven't figured out your problem by tomorrow (I'm about to crash out and likely to make a silly mistake with the if I try to write it now sorry...).
Here's the code for the minraise/jam/fold game:
Code:
#define RAISE_PLAY_PENALTY 0.0 // The amount of EV in BBs we penalize making a raise play (to force push is near EV).
// Use this to calculate the matrices for a given R.
void RunFictitiousPlayRaisePlay(const double RRatio,const int MaxIters,const double UpdateRate,
double* PushMatrix, double* RaiseCallMatrix,double* RaiseFoldMatrix,
double* CallPushMatrix,double* PushOverRaiseMatrix)
{
// These are the temp matrices that hold the final NE strategy.
double TempPushMatrix[169];
double TempRaiseCallMatrix[169];
double TempRaiseFoldMatrix[169];
double TempCallPushMatrix[169];
double TempPushOverRaiseMatrix[169];
// Init the matrices for all values.
for (int I=0;I<169;I++) {
PushMatrix[I]=0.0;//0.5;
RaiseCallMatrix[I]=0.0;//0.25;
RaiseFoldMatrix[I]=0.0;//0.25;
CallPushMatrix[I]=0.0; //1.0;
PushOverRaiseMatrix[I]=0.0; //1.0;
}
// Keep running the fictitious play for a set number of iterations.
for (int Iter=0;Iter<MaxIters;Iter++) {
// If we have been passed UpdateRate=0.0 then use true game theory method, else just use UpdateRate as the weight.
double WeightNew=(UpdateRate<EPSILON?(1.0/(double)(Iter+1)):UpdateRate);
// Update SB's strategy.
for (int I=0;I<169;I++) {
// Init EV_Sum to 0 for each option.
double EV_Push=0.0;
double EV_RaiseCall=0.0;
double EV_RaiseFold=0.0;
// This is the sum of hole multiplicity values.
double NormalizationSum=0.0;
// Work out the EV vs each of the opposing hands.
for (int J=0;J<169;J++) {
// EV when we push and he does/doesn't fold.
EV_Push+=(1.0-CallPushMatrix[J])*(double)HoleMultiplicityIfHolding[I][J]*1.0;
EV_Push+=CallPushMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(((2.0*RRatio*PWin[I][J])+(RRatio*PTie[I][J]))-RRatio);
// EV when we raise/call and he does/doesn't fold.
EV_RaiseCall+=(1.0-PushOverRaiseMatrix[J])*(double)HoleMultiplicityIfHolding[I][J]*1.0;
EV_RaiseCall+=PushOverRaiseMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(((2.0*RRatio*PWin[I][J])+(RRatio*PTie[I][J]))-RRatio);
// EV when we raise/fold and he does/doesn't fold.
EV_RaiseFold+=(1.0-PushOverRaiseMatrix[J])*(double)HoleMultiplicityIfHolding[I][J]*1.0;
EV_RaiseFold+=PushOverRaiseMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(-2.0);
// Keep a total of the sums so we can use to normalize after.
NormalizationSum+=(double)HoleMultiplicityIfHolding[I][J];
}
// Normalize the EV values.
EV_Push/=NormalizationSum;
EV_RaiseCall/=NormalizationSum;
EV_RaiseFold/=NormalizationSum;
// Init all to zero ready to set below if found to be +EV.
TempPushMatrix[I]=0.0;
TempRaiseCallMatrix[I]=0.0;
TempRaiseFoldMatrix[I]=0.0;
// If the sum of EV for the best option is +ve then we should play this hand, else we shouldn't.
if (EV_Push>=(Max(EV_RaiseFold,EV_RaiseCall)-RAISE_PLAY_PENALTY)) {
if (EV_Push>(-0.5))
TempPushMatrix[I]=1.0;
}
else if (EV_RaiseCall>=Max(EV_Push,EV_RaiseFold)) {
if (EV_RaiseCall>(-0.5))
TempRaiseCallMatrix[I]=1.0;
}
else { //if (EV_RaiseFold>=(Max(EV_Push,EV_RaiseCall)-EPSILON)) {
if (EV_RaiseFold>(-0.5))
TempRaiseFoldMatrix[I]=1.0;
}
}
// Update the SB's matrices.
// NOTE: We tend to get better convergence if we replace this before updating the BB strategy below.
for (int I=0;I<169;I++) {
PushMatrix[I]=((1.0-WeightNew)*PushMatrix[I])+(WeightNew*TempPushMatrix[I]);
RaiseCallMatrix[I]=((1.0-WeightNew)*RaiseCallMatrix[I])+(WeightNew*TempRaiseCallMatrix[I]);
RaiseFoldMatrix[I]=((1.0-WeightNew)*RaiseFoldMatrix[I])+(WeightNew*TempRaiseFoldMatrix[I]);
}
// Update BB strategy vs a push.
for (int I=0;I<169;I++) {
// Init EV sums 0 for calling with this hand.
double EV_CallPush=0.0;
// This is the sum of hole multiplicity values.
double NormalizationSum=0.0;
// Work out the EV vs each of the opposing hands.
for (int J=0;J<169;J++) {
// EV when he pushes and we call.
EV_CallPush+=PushMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(((2.0*RRatio*PWin[I][J])+(RRatio*PTie[I][J]))-RRatio);
// Keep a total of the sums so we can use to normalize after.
NormalizationSum+=PushMatrix[J]*(double)HoleMultiplicityIfHolding[I][J];
}
// Normalize the EV_Push value.
EV_CallPush/=NormalizationSum;
// If the sum of EV is +ve then we should play this hand, else we shouldn't.
if (EV_CallPush>(-1.0))
TempCallPushMatrix[I]=1.0;
else
TempCallPushMatrix[I]=0.0;
}
// Update the call push matrix.
for (int I=0;I<169;I++)
CallPushMatrix[I]=((1.0-WeightNew)*CallPushMatrix[I]) + (WeightNew*TempCallPushMatrix[I]);
// ---------------------
// Update BB strategy vs a raise.
for (int I=0;I<169;I++) {
// Init EV sums 0 for calling with this hand.
double EV_PushOverRaise=0.0;
// This is the sum of hole multiplicity values.
double NormalizationSum=0.0;
// Work out the EV vs each of the opposing hands.
for (int J=0;J<169;J++) {
// EV when he raises, we push over him and he folds/calls.
EV_PushOverRaise+=RaiseCallMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*(((2.0*RRatio*PWin[I][J])+(RRatio*PTie[I][J]))-RRatio);
EV_PushOverRaise+=RaiseFoldMatrix[J]*(double)HoleMultiplicityIfHolding[I][J]*2.0;
// Keep a total of the sums so we can use to normailize after.
NormalizationSum+=(RaiseCallMatrix[J]+RaiseFoldMatrix[J])*(double)HoleMultiplicityIfHolding[I][J];
}
// Normalize the EV_Push value.
EV_PushOverRaise/=NormalizationSum;
// If the sum of EV is +ve then we should play this hand, else we shouln't.
if (EV_PushOverRaise>(-1.0))
TempPushOverRaiseMatrix[I]=1.0;
else
TempPushOverRaiseMatrix[I]=0.0;
}
// Update the push-over matrix.
for (int I=0;I<169;I++)
PushOverRaiseMatrix[I]=((1.0-WeightNew)*PushOverRaiseMatrix[I]) + (WeightNew*TempPushOverRaiseMatrix[I]);
}
} // End RunFictitiousPlayRaisePlay.
(again, ignore RAISE_PLAY_PENALTY - that was just me fiddling to see what effect it had)
From experience, my best guess of what would cause a small error in EV like you have is something to do with card-removal (see the use of HoleMultiplicityIfHolding[][] in my code).
Anyway, hope this helps.
Juk