Open Side Menu Go to the Top
Register
SnG Luck Analyzer (Beta) SnG Luck Analyzer (Beta)

01-24-2008 , 05:04 PM
So, here is my actual Graph and it looks quite right:



But atm I'm running totally bad and I'm missing almost 800 bucks in Winnings Right?
SnG Luck Analyzer (Beta) Quote
01-24-2008 , 07:29 PM
Quote:
Originally Posted by jukofyork
Yep, I'm only considering 2-way all-ins. I class a 2-way all-in as either:

A) We put all our chips in the pot on our first action (push or call all-in). Whatever the action was before or after, it's still a 2-way all-in so long as only one opponent remains when we leave the pre-flop stage (all dead bets from other players are properly handled, so there can be raises and folds from other players, eg: UTG raises, we push, BB calls, UTG folds, etc are all handled correctly...).

B) We make a call for less than our total stack and there are no non-all-in players left (ie: this can't handle BTN pushing and us just calling from SB as BB isn't all-in already, but it can handle say BTN push all-in, SB fold and we call in BB).

(I can fix these to handle any 2-way all-in where we are all-in with a single a opponent as we leave the pre-flop stage, but atm that's what it does and that's why I'm getting slightly less data-points per SNG...).

I also take ties into account and have three separate outcomes for every all-in (ie: we lose, we win, we tie).

I think a much easier way will be to compare outputs for the same HH files. What I've done it output a ".csv" file which has a row for every all-in, eg:

Code:
HandNumber, P_Outcome1,ExpPrizeEquity1,ExpChipEquity1, P_Outcome2,ExpPrizeEquity2,ExpChipEquity2, P_Outcome3,ExpPrizeEquity3,ExpChipEquity3, ExpPrizeEquity,ActPrizeEquity,PrizeEquityDiff, ExpChipEquity,ActChipEquity,ChipEquityDiff, CumSumPrizeEquityDiff%,CumSumChipEquityDiff%, CumMeanPLoseDiff%
6438834895, 0.578456,0.266926,6180, 0.416829,0,0, 0.0047147,0.158965,3090, 0.155155,0.266926,0.111772, 3589.43,6180,2590.57, 11.1772,259057, -41.6829
6438844098, 0.636412,0.387597,10880, 0.359697,0.314306,7580, 0.00389023,0.352481,9230, 0.361098,0.387597,0.0264992, 9686.58,10880,1193.42, 13.8271,378399, -38.8263
6438865986, 0.351842,0.42351,12660, 0.643951,0.334212,7380, 0.00420647,0.380797,10020, 0.365827,0.334212,-0.0316146, 9248.83,7380,-1868.83, 10.6656,191516, -14.0159
When I post the exe you should just be able to run it on your own hands and compare my output with yours.

EDIT: Made my example ".csv" file easier to understand - I was using the term "EV" when I meant "Equity" and since dollar equity is measured in terms of "% of prize pool", it seems clearer to measure chip equity in terms of "our total chips" rather than "our chips gained/lost since start of hand".

Juk
will yours work for multitable sng's?

will sngluckanalyzer eventually work for mtt sng's?
SnG Luck Analyzer (Beta) Quote
01-24-2008 , 08:06 PM
Quote:
will yours work for multitable sng's?

will sngluckanalyzer eventually work for mtt sng's?
I don't see how multi-table SNGs would be possible outside of doing chip EV only calcs, since you'd need data on other tables (how many players left, stacksizes etc) to do full ICM calcs. And that data won't be available from your HHs. It could do it for final tables though I suppose.
SnG Luck Analyzer (Beta) Quote
01-24-2008 , 10:42 PM
Well after twice finding bugs, I've decided to simplify and improve my code to make it much easier to run very large "backtest" experiments:

A) Have simplified my code to make it much easier to spot any potential bugs (no more Open/NoOpen breakdown, etc).

B) Have made it so that any hand where two all-in players leave the flop is now analyzed. This means any possible preflop action can have occurred: limps, raises, re-raises, folds, etc... so long as it ends up with two players all-in at the end of the preflop round.

C) Have made it so we can examine any 2-way all-in (not just our own) from the perspective of both the players who were all-in.

D) I now detect if a player has lost the hand (for the "Mean P(lose) difference" calculations), by using a proper hand evaluator and not just testing if they lost chips during the hand. It is possible sometimes to lose a hand but still end up winning chips (ie: if the SBs fold after a micro-shorties push, etc). This might have caused problems in earlier versions.


Experiment 1: Run against every all-in from both player's perspectives

NumSNGs = 77619 / NumDataPoints = 1335816
$EV Luck: -3956% (-0.00296182% per all-in)
cEV Luck: 0t (0t per all-in)
Mean P(lose) difference: 0.00813648%


Even though we have diverged by nearly 4000%, this is over 1.3 million data-points (77.5k SNGs) and if you look at the diverge per hand it is pretty small. Note how cEV is neutral if you consider both sides of every all-in (ie: one players "bad luck" is directly transferred to the other as "good luck" and vice versa). Also note how the "Mean P(lose) difference" has converged much more towards zero (mainly due to using so many more samples, but possibly using the hand evaluator [see (D) above] has had some effect too).


This looks like it might be diverging, but if you look at the results of Experiment 2 (see below) it is much more likely that it's just part of a "random walk".


This looks much better that the old (possibly bugged) version that didn't use the hand evaluator to decide who lost the hand. It has almost converged to zero and it looks very hopeful that after more runs it will get even closer.

Experiment 2: Run against every all-in from just one (randomly chosen) player's perspective

I ran this five times, so as to get an idea of the sample variance we would expect to see in Experiment 1:

NumSNGs = 77619 / NumDataPoints = 667908
$EV Luck: -9723% (-0.0145579% per all-in)
cEV Luck: -4042007t (-6.05174t per all-in)
Mean P(lose) difference: 0.0470058%

NumSNGs = 77619 / NumDataPoints = 667908
$EV Luck: 1942% (0.00290855% per all-in)
cEV Luck: 2513115t (3.76267t per all-in)
Mean P(lose) difference: 0.0069008%

NumSNGs = 77619 / NumDataPoints = 667908
$EV Luck: 2281% (0.00341588% per all-in)
cEV Luck: 2744274t (4.10876t per all-in)
Mean P(lose) difference: -0.0464558%

NumSNGs = 77619 / NumDataPoints = 667908
$EV Luck: 3470% (0.00519563% per all-in)
cEV Luck: 4580194t (6.85752t per all-in)
Mean P(lose) difference: -0.0354661%

NumSNGs = 77619 / NumDataPoints = 667908
$EV Luck: -2369% (-0.00354691% per all-in
cEV Luck: -3064346t (-4.58798t per all-in)
Mean P(lose) difference: -0.0299421%


Notice how taking a different sub-sample creates difference divergences each time. It's a good sign that some are +ve and some are -ve. None are really very significant when considering the "divergence per hand".

Experiment 3: Run against every all-in from just one (randomly chosen) player's perspective hundreds of times and aggregate the results

I'm currently running this and will post the results and graphs tomorrow. If all is now working correctly, the "Mean P(lose) difference" should converge much closer towards zero and the aggregated $EV plot should look like a "random walk" with no obvious +ve or -ve divergence. If that is the case, then I will finally be able to conclude that another bug is unlikely.

Juk
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 05:41 AM
Quote:
Originally Posted by NJD77
Just wanted to add my thanks for creating such a great tool. I am a degree qualified Chartered Engineer with a solid understanding of advanced maths but the intelligence required to undertake these tasks blows my mind, and to start bringing it all together into a program like this takes some serious brainpower and talent. Well done! It's worked great for me so far. All the best with it for the future. The market has been crying out for a tool exactly like this for ages.
Wow - these are some nice words! Thanks a lot

Guys, I'm pretty sure that the problem of negative luck is in the way how I'm handling multi-way all-ins.

Currently, if more then 2 players are involved, I put the PF-bet of player > 2 to a dead money pot (like a blind) and calculate a matchup between Hero and the one remaining player who had the highest chip-movement, which is either the winner (if Hero lost) or the biggest loser (if Hero won).

I think this is a stupid thing to do, because Hero's win-chance drops significantly against additional hands, no matter what the 3rd, 4th etc. hand is.

A too high anticipated win-chance results in a too high expected equity gain which will of course reduce the luck.

I am thinking about how to handle multi-way all-ins correctly. A friend of mine is currently coding a dll (or whatever) which I can use to evaluate more then 2 holdem hands, but I'm struggling at the moment how to continou...
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 12:30 PM
Well I've run this all night and need PC for other stuff now, but it looks fine and I think I can be pretty certain there isn't anymore bugs effecting the outcome:


This looks like a classic "random walk" and there is no obvious divergence, even after the equivalent of 25 million all-ins (I re-sampled from 1.3 million real all-ins taken from 77.5k data-mined SNGs).


This also looks fine and is within one 1/100th of a percent of expected. Perhaps if I ran this all year it would get nearer to zero, or perhaps there is some tiny bias from the "bunching effect", etc, but either way it's so tiny it doesn't matter.

Quote:
Originally Posted by Bodypull
I am thinking about how to handle multi-way all-ins correctly. A friend of mine is currently coding a dll (or whatever) which I can use to evaluate more then 2 holdem hands, but I'm struggling at the moment how to continou...
For 3-way matchups you can just use a 3-way lookup table, but all the different outcomes get pretty messy to consider.

For 4-way or more, then I think Monte-Carlo simulation is the only real choice (the lookup would be too huge and manually considering all the outcomes would be a nightmare). This will be pretty slow compared to the lookup table methods, but on the other hand 4-way (+) all-ins are pretty rare.

Juk
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 12:48 PM
Very interesting. You are tracking various players in your last output, not only one, right?

BTW: If anyone could give me a hint in this post How to calculate expected equity from multiway pre-flop all-ins? I would appreciate it...
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 01:26 PM
3-way all-in:

example:
stacks:
5000(hero)
10000(SB)
7000(BB)
all go all-in in a 9 man sng

EV(expected)= weighed sum of results
EV(expected)=P(hero>SB>BB)*EV(icm 15k,7k,0k)+P(hero>BB>SB)*ev(icm 15k,3k,4k)+[P(hero<SB<BB)+P(hero<BB<SB)]*EV(0k,22k,0k)
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 01:30 PM
I am willing to help you more with math problems you might encounter if you add support for non poker tracker hand reading and if you give me a registration code for the final release.
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 02:03 PM
Quote:
Originally Posted by Bodypull
Very interesting. You are tracking various players in your last output, not only one, right?
Yes, everytime an all-in occurs between any two players (ie: not just our hands), I choose one of the two players and update the values based on his luck. I then repeat this process for all my data-points over and over again (ie: I reuse them). It's just a quick and dirty way to get a sub-sample each time.

Quote:
BTW: If anyone could give me a hint in this post How to calculate expected equity from multiway pre-flop all-ins? I would appreciate it...
I know how to do this, but I can't very easily explain how to do it in words...

Basically you need to first work out how many possible outcomes there are for your n-way all-in (IIRC, there are 12 for 3-way matchups) and then create a huge lookup that stores P(Outcome)=[Outcome][P1_Hand][P2_Hand][P3_Hand].

You then need to loop through each outcome and assign each player a "finishing rank" based on the outcome (ie: for the 122 outcome, where player 1 has the best hand and player 2 and 3 split for 2nd best you would give them 1, 2, and 2 respectively). You then need to share out the pot (and side pots) starting with the smallest side-pot (capped at the shortest player's amount put in the pot), remove that sidepot from the process and repeat until it's all chips that were put in the pot are shared out. Then finally for this outcome you need to feed the resultant stack configuration through the ICM function and add the result into the EV sum, weighted by the chance of the outcome you have in your lookup table. Repeat this for all 12 outcomes. Here is the pseudo-code:

Code:
EV=0

For each outcome, J {

  // Make a temp copy of Stack[] and AmountStaked[] arrays to use below.

  // Assign each player their "finishing rank" for this outcome.

  // Share out side pot.
  Loop {

    // Find he smallest amount staked.
    MinAmountStaked=INF
    For each player with AmountStaked[I]>0, I
      MinAmountStaked=Min(MinAmountStaked,AmountStaked[I])

    // Create the side-pot.
    CurrentPot=0
    For each player with AmountStaked[I]>0, I {
      AmountStaked[I]-=MinAmountStaked
      CurrentPot+=MinAmountStaked
    }

    // Test if we are done.
    // NOTE: The last side-pot will just be the biggest stack geting his excess chips returned.
    If CurrentPot==0, we are done so break from the loop.

    // Share out the pot based on the hand rank of each player.
    NumHandWinners=0
    BestHand=INF
    For each player with money in CurrentPot, I {
      if (FinishingRank[I]<BestHand) {
        NumHandWinners=1;
        BestHand=FinishingRank[I];
      }
      else if (HandValue[I]==BestHand) 
        NumHandWinners++;
    }

    // Now lets give the player the share of the pot they need.
    For each player with money in CurrentPot, I {
      if (FinishingRank[I]==BestHand)
        Stack[I]+=CurrentPot/NumHandWinners;
    }

  }

  // Calculate ICM for this stack and weight by the chance of the outcome.
  EV+=ICM(Stack,PlayerIndex)*ChanceOfOutcome[J][Hand1][Hand2][Hand3]

}
To use with more players, simply create ChanceOfOutcome on-the-fly using Monte-Carlo simulation for the specific hand triple.

Juk
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 02:16 PM
Quote:
Originally Posted by Staafy
3-way all-in:

example:
stacks:
5000(hero)
10000(SB)
7000(BB)
all go all-in in a 9 man sng

EV(expected)= weighed sum of results
EV(expected)=P(hero>SB>BB)*EV(icm 15k,7k,0k)+P(hero>BB>SB)*ev(icm 15k,3k,4k)+[P(hero<SB<BB)+P(hero<BB<SB)]*EV(0k,22k,0k)
Common 3-way all-ins often involve match-ups such as AKo+AKo+QQ, so it's very important that you take ties into consideration.

Juk
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 06:51 PM
Something that bothers me about 3-way pots is this: say hero has AK in a 3-way all-in with one of the players shortstacked, if he loses the main pot, doesn't this increase the chance hero also loses the side pot? Because when hero hits an A/K he will usually win the mainpot, since he lost the mainpot there is less chance he hit an A/K and so less chance he wins the sidepot. It seems to me like the correlation between the outcomes for the side/main-pot would be dependent on the hand you are holding (and your opponents ranges).
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 06:57 PM
obviously you need to account for ties, especially if you have the same hand as one of your opponents, there are 9 different outcomes including ties:
ranks:
sb, hero and BB:

S 1 1 1 1 2 2 2 3 3
H 1 2 2 3 1 1 3 2 1
B 1 2 3 2 1 3 1 1 2

as juk pointed out, EV(expected) is
sum(P(outcome)*(ICM value outcome) ) for all 9 outcomes in a 3way matchup.

Last edited by Staafy; 01-25-2008 at 07:21 PM.
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 07:08 PM
Quote:
Something that bothers me about 3-way pots is this: say hero has AK in a 3-way all-in with one of the players shortstacked, if he loses the main pot, doesn't this increase the chance hero also loses the side pot? Because when hero hits an A/K he will usually win the mainpot, since he lost the mainpot there is less chance he hit an A/K and so less chance he wins the sidepot. It seems to me like the correlation between the outcomes for the side/main-pot would be dependent on the hand you are holding (and your opponents ranges).
If you run simulations you can establish the frequencies each outcome happens (from the 9 i listed in a 3way).
independantly.


This error only happens when you assume when
you have AK in a threeway
AK, AK, QQ,

stacks:
5000,3000,5000

and split with shorty
and then you assume you win the sidepot against QQ 45%

pokerstove is useless for this, because you have to make some assumptions like the latter

Last edited by Staafy; 01-25-2008 at 07:19 PM.
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 07:51 PM
Quote:
Originally Posted by Staafy
obviously you need to account for ties, especially if you have the same hand as one of your opponents, there are 9 different outcomes including ties:
ranks:
sb, hero and BB:

S 1 1 1 1 2 2 2 3 3
H 1 2 2 3 1 1 3 2 1
B 1 2 3 2 1 3 1 1 2

as juk pointed out, EV(expected) is
sum(P(outcome)*(ICM value outcome) ) for all 9 outcomes in a 3way matchup.
S 1 1 1 1 2 2 2 3 3 + 1 1 2 2
H 1 2 2 3 1 1 3 2 1 + 1 2 1 2
B 1 2 3 2 1 3 1 1 2 + 2 1 2 1

I think you've missed those 4

I worked mine out by considering all 27 combinations, eg:

111*
112*
113
121*
122*
123*
131
132*
133
211*
212*
213*
221*
222
223
231*
232
233
311
312*
313
321*
322
323
331
332
333

Juk
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 08:04 PM
Quote:
Originally Posted by Staafy
If you run simulations you can establish the frequencies each outcome happens (from the 9 i listed in a 3way).
independantly.
Yep, this is what I've done. Basically I created a huge [13][169][169][169] lookup table by taking each hand triple in turn and then running lots of simulations to see how many times each of the 13 outcomes occurs for each triple. I think trying to kludge something together from HU matchups is asking for problems.

One other thing that's bothering me is that I think he's using the HU match-ups (in his "PFMU.dat" file) from this page which are fine for working with cEV, but the non-linear nature of ICM means he needs the ties separated.

Juk
SnG Luck Analyzer (Beta) Quote
01-25-2008 , 09:18 PM
Well, I've now added the ability to my code to work with 3-way all-ins (using the method described in the other post). I won't post all the graphs again, but after back-testing it on all 77.5K SNGs it seems to be working OK with no obvious divergence problems.

As for adding 4-way (or more) all-ins, then I don't really think it's that worthwhile as they happen so infrequently. For example, out of 77.5K SNGs there were: 667908 2-way all-ins, 33385 3-way all-ins and only 1128 4-way (or more) all-ins.

Rather than release the exe along with a 100MB data-file, I've just elected to post my code:

Code:
// ***************************************************************************
// *                                  INCLUDES                               *
// ***************************************************************************

#include "../core/misc.h"
#include "../core/random_number_generator.h"

#include "../holdem_game_state.h"
#include "../holdem_game_state_with_history.h"
#include "../holdem_hole_card_ranking.h"
#include "../sng_icm_calculator.h"
#include "../holdem_parser.h"

#include "../party_nl_holdem_sng_parser.h"

// ***************************************************************************
// *                                  CONSTANTS                              *
// ***************************************************************************

// Define this to just examine our all-in hands from our perspective.
#define HERO_HANDS_ONLY

// Define this to only look at the 2/3-way all-in from a single randomly chose player's perspective.
// NOTE: Can't use this with HERO_HANDS_ONLY.
//#define RANDOM_PLAYER_PERPECTIVE

// Define this to read all folders again and again in a loop. Thius is usedful for use with
// RANDOM_PLAYER_PERPECTIVE to get an idea of the level of sample variance to expect from
// just doing a single pass (useful for plots, but no good for results...).
//#define REPEAT_LOOP_ALL_FOLDERS

// Set this to >1 to print less data (for plotting, etc).
#define FILE_OUTPUT_INTERVAL 1

// ===========================================================================

void BackTestAllInLuck(const char** Folders,const int NumFolders)
{	

	// The parser we are going to use.
	PartyNLHoldemSNGParser Parser;

	// The vector of hands we get from the parser.
	vector<HoldemGameStateWithHistory> Hands;

	// This is what we use to get the EV at the end of the hand.
	SNG_ICM_Calculator ICM;

	// These are the possible finish rankings for each outcome.
	const int TwoWayFinishRanking[3][2]={{1,1},{1,2},{2,1}};
	const int ThreeWayFinishRanking[13][3]={{1,1,1},{1,1,2},{1,2,1},{1,2,2},{1,2,3},{1,3,2},{2,1,2},
	                                        {2,2,1},{2,1,3},{2,3,1},{2,1,1},{3,1,2},{3,2,1}};

	// These hold the outcomes for each 2-way and 3-way hand matchup.
	static double TwoWayOutcomeChances[3][169][169];
	static double ThreeWayOutcomeChances[13][169][169][169];

	// The sum of differences from our predicted EV.
	double SumPrizeEquityDifferences=0.0;
	double SumChipEquityDifferences=0.0;

	// The number of SNGs we have played.
	int NumSNGs=0;

	// The number of 2-way, 3-way and multi-way data-points seperately.
	int Num2WayAllIns=0;
	int Num3WayAllIns=0;
	int NumMultiWayAllIns=0;										// We don't deal with these atm.

	// The number fo data-points we have used.
	// NOTE: This can be 2x or 3x the number of all-ins, as we can (if asked) take a data-point from
	//       multiple player's perspectives.
	int NumDataPoints=0;

	// The last tourney number.
	char LastTournamentNumber[BUFFER_SIZE+1];
	LastTournamentNumber[0]=0;

	// Use for reading in the outcome look files.
	int H1,H2,H3;
	double Outcome;

	// Open and read each line of the HU data file.
	// NOTE: Inversed as HoldemHoleCardRanking::GetIndex() uses AA in top left, A2s top right, etc.
	ifstream InFile1;
	InFile1.open("HU_HandEval.dat");
	if (InFile1.fail())
		Error(FATAL,"Could not load the 'HU_HandEval.dat'");
	while (InFile1 >> H1 >> H2) {
		for (int I=0;I<3;I++)
			InFile1 >> TwoWayOutcomeChances[I][168-H1][168-H2];
	}
	InFile1.close();

	// Open and read each line of the HU data file.
	// NOTE: Inversed as HoldemHoleCardRanking::GetIndex() uses AA in top left, A2s top right, etc.
	ifstream InFile2;
	InFile2.open("3WAY_HandEval.dat");
	if (InFile2.fail())
		Error(FATAL,"Could not load '3WAY_HandEval.dat'.");
	while (InFile2 >> H1 >> H2 >> H3) {
		for (int I=0;I<13;I++)
			InFile2 >> ThreeWayOutcomeChances[I][168-H1][168-H2][168-H3];
	}
	InFile2.close();

	// Open the output file.
	ofstream Outfile;
	Outfile.open("luck_results.csv");
	if (Outfile.fail())
		Error(FATAL,"Could not open 'luck_restults.csv'");

	// Lets do it on each folder.
	for (int Folder=0;Folder<NumFolders;Folder++) {

		// Lets try and get the first vector of hands.
		Parser.InitHandHistorySearcher(Folders[Folder]);
		while (Parser.SearchForAndParseNextHandHistoryFile(Hands,true)==true) {

			// Lets keep going until we get no more.
			for (int I=0;I<Hands.size();I++) {

				// Is this a new tournament number?
				if (strcmp(LastTournamentNumber,Hands[I].GetTournamentNumber())!=0) {
					NumSNGs++;
					strcpy_s(LastTournamentNumber,BUFFER_SIZE,Hands[I].GetTournamentNumber());
				}

				// If we have gone from pre-flop to river, then this is an all-in hand.
				if (Hands[I].GetNumStates()>=2
					&& Hands[I].GetCurrentGameStage()==RIVER
					&& Hands[I][Hands[I].GetNumStates()-2].GetCurrentGameStage()==PRE_FLOP
					&& Hands[I].GetTotalInHandCurrently()>=2) {

					// This is the number of all-in players we are dealing with here.
					int NumAllInPlayers=Hands[I].GetTotalInHandCurrently();

					// This is the index of each of the players who are left.
					int PlayerIndex[10];	// ### Only use the first 3, but need 10 incase we start to sim for 4+ players...
					int NumValidShowdownHands=0;

					// The (temp) "finishing rank" assigned depending on outcome.
					int PlayersFinishingRank[10];

					// This holds the rank indexes (0..168) for the player's hand.
					int PlayerHandType[10];

					// The stacks for each player at the start and end of hand.
					int StackAtStartOfHand[10];
					int StackAtEndOfHand[10];

					// These are used to temporailty sim the outcomes.
					int CurrentStacks[10];
					int CurrentStakes[10];
					bool InHandCurrently[10];

					// The actaul EV from the start to end of hand.
					double ActualPrizeEquity,ActualChipEquity;

					// Expected average EV based on the hand matchup.
					double ExpectedPrizeEquity;
					double ExpectedChipEquity;

					// If asked to look at the hands from our perspective only. and we're not here, then skip.
					#ifdef HERO_HANDS_ONLY
					if (Hands[I].GetOurSeatIndex()==NO_SEAT)
						continue;
					#endif

					// @@@@@@@@@@@@@@ Don't really need this @@@@@@@@@@@@@@@@@@@@@
					// Lets make sure that 1 or both players are all-in.
					int NumNonAllInPlayers=0;
					for (int J=0;J<10;J++) {
						if (Hands[I].IsInHandCurrently(J)==true && Hands[I].GetCurrentStack(J)!=0)
							NumNonAllInPlayers++;
					}
					if (NumNonAllInPlayers>1) {
						Error(WARNING,"Non all-in hand (should not get here...).");
						continue;
					}
					// @@@@@@@@@@@@@@ Don't really need this @@@@@@@@@@@@@@@@@@@@@

					// If the player is in the hand currently and his cards are known, then lets count him and get his seat index.
					for (int J=0;J<10;J++) {
						if (Hands[I].IsInHandCurrently(J)==true && Hands[I].GetShowdownHoleCard(J,0).Rank!=RANK_NONE) {
							PlayerIndex[NumValidShowdownHands]=J;
							PlayerHandType[J]=HoldemHoleCardRanking::GetIndex((Hands[I].GetShowdownHoleCard(J,0).Suit*13)+Hands[I].GetShowdownHoleCard(J,0).Rank,
																			  (Hands[I].GetShowdownHoleCard(J,1).Suit*13)+Hands[I].GetShowdownHoleCard(J,1).Rank);
							NumValidShowdownHands++;	// One more.
						}
					}

					// If we don't have cards for all the players at the end then skip this hand.
					if (NumValidShowdownHands!=NumAllInPlayers)
						continue;

					// Lets work out the stacks at the start/end of the hand.
					for (int K=0;K<10;K++) {
						StackAtStartOfHand[K]=0;
						StackAtEndOfHand[K]=0;
					}
					for (int K=0;K<10;K++) {
						if (Hands[I].WasInHandAtStartOfHand(K)==true) {
							StackAtStartOfHand[K]=Hands[I].GetStackAtStartOFHand(K);
							StackAtEndOfHand[K]=Hands[I].GetCurrentStack(K)+Hands[I].GetUnCalledBets(K)+Hands[I].GetAmountWon(K);
						}
					}

					// Check we have not lost any chips.
					// #### NOTE: This happens for truncated HH files where their is no "wins" line...
					int TotalChipsStart=0;
					int TotalChipsEnd=0;
					for (int K=0;K<10;K++) {
						TotalChipsStart+=StackAtStartOfHand[K];
						TotalChipsEnd+=StackAtEndOfHand[K];
					}
					if (TotalChipsStart!=TotalChipsEnd) {
						Error(WARNING,Hands[I].GetHandNumber(),"Chips at end of hand != start of hand. Ignoreing hand...");
						continue;
					}

					// At the moment we can only deal with 2-way and 3-way all-in, but we will count the rest.
					if (NumAllInPlayers>3) {
						NumMultiWayAllIns++;
						continue;
					}
					else if (NumAllInPlayers==3) {
						Num3WayAllIns++;						
					}
					else {
						Num2WayAllIns++;						
					}

					// We must make sure that player's indexes are ordered based on their cards (for lookup into the outcome tables).
					// NOTE: Bubble-sort is fine for this...
					for (int J=0;J<(NumAllInPlayers-1);J++) {
						for (int K=J+1;K<NumAllInPlayers;K++) {
							if (PlayerHandType[PlayerIndex[J]]>PlayerHandType[PlayerIndex[K]]) {
								int Temp=PlayerIndex[J];
								PlayerIndex[J]=PlayerIndex[K];
								PlayerIndex[K]=Temp;
							}	
						}
					}

					// Chose one player randomly.
					#ifdef RANDOM_PLAYER_PERPECTIVE
					int ChosenPlayer=RandInt(NumAllInPlayers-1);
					#endif

					// Now lets do the calculation for each of the players.
					for (int Player=0;Player<NumAllInPlayers;Player++) {


						// If asked to, then lets looks at the hands from our perspective only.
						#ifdef HERO_HANDS_ONLY
						if (Hands[I].GetOurSeatIndex()!=PlayerIndex[Player])
							continue;
						#else

						// We can also look the luck from just one of the players involved in the all-in.
						#ifdef RANDOM_PLAYER_PERPECTIVE
						if (Player!=ChosenPlayer)
							continue;
						#endif
						#endif

						// Lets print the hand number first.
						Outfile << Hands[I].GetHandNumber() << ", ";

						// If we didn't just bust out we need to calculate the real EV using ICM.
						if (ICM.TestBustout(StackAtStartOfHand,StackAtEndOfHand,PlayerIndex[Player])==true)
							ActualPrizeEquity=ICM.CalculateBustoutPrizeEV(StackAtStartOfHand,StackAtEndOfHand,PlayerIndex[Player]);
						else
							ActualPrizeEquity=ICM.CalculatePrizeEV(StackAtEndOfHand,PlayerIndex[Player]);

						// Get the cEV too.
						ActualChipEquity=StackAtEndOfHand[PlayerIndex[Player]];

						// Init ready to sum for each outcome.
						ExpectedPrizeEquity=0.0;
						ExpectedChipEquity=0.0;

						// Lets go through the three possible outcomes.
						for (int OutCome=0;OutCome<(NumAllInPlayers==2?3:13);OutCome++) {

							// Lets make a copy of the current stacks, amounts staked and Inhand flags.
							for (int J=0;J<10;J++) {
								CurrentStacks[J]=Hands[I].GetCurrentStack(J);
								CurrentStakes[J]=Hands[I].GetStackAtStartOFHand(J)-Hands[I].GetCurrentStack(J);
								InHandCurrently[J]=Hands[I].IsInHandCurrently(J);
							}

							// Lets assign a finishing rank to each player based on the current outcome index.
							for (int J=0;J<NumAllInPlayers;J++)
								PlayersFinishingRank[PlayerIndex[J]]=TwoWayFinishRanking[OutCome][J];

							// Lets share out all the side-pots.
							for (;;) {

								// Lets set any player who has nothing staked anymore to not be in the hand.
								for (int J=0;J<10;J++)
									if (CurrentStakes[J]==0)
										InHandCurrently[J]=false;					// Not in hand now.

								// Find he smallest amount staked.
								int MinAmountStaked=0x7fffffff;
								for (int J=0;J<10;J++)
									if (InHandCurrently[J]==true)
										MinAmountStaked=Min(MinAmountStaked,CurrentStakes[J]);

								// Create the side-pot and subtract from the staked amounts.
								int CurrentPot=0;
								for (int J=0;J<10;J++) {
									CurrentPot+=Min(CurrentStakes[J],MinAmountStaked);
									CurrentStakes[J]-=Min(CurrentStakes[J],MinAmountStaked);
								}

								// Test if we are done.
								// NOTE: The last side-pot will just be the biggest stack geting his excess chips returned.
								if (CurrentPot==0)
									break;

								// Share out the pot based on the hand rank of each player.
								int NumHandWinners=0;
								int BestHand=0x7fffffff;
								for (int J=0;J<10;J++) {
									if (InHandCurrently[J]==true) {
										if (PlayersFinishingRank[J]<BestHand) {
											NumHandWinners=1;
											BestHand=PlayersFinishingRank[J];
										}
										else if (PlayersFinishingRank[J]==BestHand) {
											NumHandWinners++;
										}
									}
								}

								// Now lets take off the smount they put into the vurrent pot and give them any pot they might need.
								int OddChips=(CurrentPot%NumHandWinners);
								for (int J=0;J<10;J++) {
									if (InHandCurrently[J]==true && PlayersFinishingRank[J]==BestHand) {
										CurrentStacks[J]+=(CurrentPot/NumHandWinners)+OddChips;
										OddChips=0;			// If there were any, they have been assigned now.
									}
								}

							}

							// Get the chance of outcome from the lookup table.
							double ChanceOfOutcome;
							if (NumAllInPlayers==2)
								ChanceOfOutcome=TwoWayOutcomeChances[OutCome][PlayerHandType[PlayerIndex[0]]][PlayerHandType[PlayerIndex[1]]];
							else
								ChanceOfOutcome=ThreeWayOutcomeChances[OutCome][PlayerHandType[PlayerIndex[0]]][PlayerHandType[PlayerIndex[1]]][PlayerHandType[PlayerIndex[2]]];

							double TempExpectedPrizeEquity,TempExpectedChipEquity,TempPLoseDiff;

							// Work out our $EV for this outcome.
							if (ICM.TestBustout(StackAtStartOfHand,CurrentStacks,PlayerIndex[Player])==true)
								TempExpectedPrizeEquity=ICM.CalculateBustoutPrizeEV(StackAtStartOfHand,CurrentStacks,PlayerIndex[Player]);
							else
								TempExpectedPrizeEquity=ICM.CalculatePrizeEV(CurrentStacks,PlayerIndex[Player]);

							// Work out the cEV for this outcome.
							TempExpectedChipEquity=CurrentStacks[PlayerIndex[Player]];

							// Lets add to the sums.
							ExpectedPrizeEquity+=TempExpectedPrizeEquity*ChanceOfOutcome;
							ExpectedChipEquity+=TempExpectedChipEquity*ChanceOfOutcome;

							// Lets print the EVs and the outcomes.
							// @@@ This file is gonna be a nightmare to read, as it will have different numbers of feilds for 2-way and 3-way lines...
							Outfile << ChanceOfOutcome << ',' << TempExpectedPrizeEquity << ',' << TempExpectedChipEquity << ", ";

						} // End for each outcome.

						SumPrizeEquityDifferences+=ActualPrizeEquity-ExpectedPrizeEquity;
						SumChipEquityDifferences+=ActualChipEquity-ExpectedChipEquity;

						// Lets print the EV for this hand.
						Outfile << ExpectedPrizeEquity << ',' << ActualPrizeEquity << ',' << ActualPrizeEquity-ExpectedPrizeEquity << ", "
								<< ExpectedChipEquity << ',' << ActualChipEquity << ',' << ActualChipEquity-ExpectedChipEquity << ", ";

						// Lets print the cumulative versions.
						if (NumDataPoints%FILE_OUTPUT_INTERVAL==0)
							Outfile << SumPrizeEquityDifferences*100.0 << ','<< SumChipEquityDifferences*100.0 << endl;

						NumDataPoints++;

					} // End for each player.

				} // End if valid all-in hand.

			} // End for each hand in hand history.

			// Print the difference between expected and real.
			cout << "NumSNGs: " << NumSNGs
				 << " / NumAllIns[2,3,4+]: " << Num2WayAllIns << ',' << Num3WayAllIns << ',' << NumMultiWayAllIns
				 << " / NumDataPoints: " << NumDataPoints << endl
				 << "$EV Luck: " << (int)(SumPrizeEquityDifferences*100.0)
				 << "% (" << (SumPrizeEquityDifferences*100.0)/(double)(NumDataPoints>0?NumDataPoints:1)
				 << "% per all-in)" << endl
				 << "cEV Luck: " << (int)SumChipEquityDifferences
				 << "t (" << SumChipEquityDifferences/(double)(NumDataPoints>0?NumDataPoints:1)
				 << "t per all-in)" << endl;

		} // End for each hand history in folder.

		// If asked to, then reset to use again.
		#ifdef REPEAT_LOOP_ALL_FOLDERS
		if (Folder==NumFolders-1)
			Folder=-1;
		#endif

	} // For each folder.

	// Close the output file.
	Outfile.close();

} // End BackTestAllInLuck.
You will have to generate your own lookups (it would also be a good idea to write binary versions of the lookups to speed up loading them in...) and write your own helper classes, parsers, etc, but you should be able to at least use my code as a template for your own. I use very clear variable names and lots and lots of comments, so it should be very easy to see what I've done and work from that.

I'm afraid I won't release the rest of my classes, as the last thing I want is for them to be used for bot-building, or IMO just as bad: real-time helper building (ie: SNGEGT, realtime NE calculator, etc). If the sites decide to ban the use of realtime helpers in SNGs (as there are banned for online Chess), then I will release a whole lot of interesting stuff, but until that happens I'm afraid I'm not releasing anything else. I can't see any real possibility to misuse this though, so I'm happy to release what I have to help with the creation of "SnG Luck Analyzer".

Hope this helps and I'd appreciate it if you tell me about any bugs you find.

Juk
SnG Luck Analyzer (Beta) Quote
01-26-2008 , 04:48 PM
Final version for a while now, as I've got other stuff I should be doing and this is becoming a time sapper...

1) Used floats instead of doubles to reduce memory footprint of the 3-way lookups. Tested and almost no significant difference in output.

2) Now creates a binary version of the lookups the first time it's run. This can speed stuff up massively, but it depends on a few things such as: how much memory you have, how often you run the app, how fast your disk is and how fast your compiler's stdin is. Overall on my system after the first run, it's about 20x quicker to load as binary.

3) Have optimized the inner loop by taking advantage of the fact that you can use much simpler pot share-out code if it's only 2-way. Even so, most of my overhead comes from reading the hands off the disk, but if you say had them all in memory then this would yield about a 15-20% speedup.

Code:
// ***************************************************************************
// *                                  INCLUDES                               *
// ***************************************************************************

#include "../core/misc.h"
#include "../core/random_number_generator.h"

#include "../holdem_game_state.h"
#include "../holdem_game_state_with_history.h"
#include "../holdem_hole_card_ranking.h"
#include "../sng_icm_calculator.h"
#include "../holdem_parser.h"

#include "../party_nl_holdem_sng_parser.h"

// ***************************************************************************
// *                                  CONSTANTS                              *
// ***************************************************************************

// Define this to just examine our all-in hands from our perspective.
#define HERO_HANDS_ONLY

// Define this to only look at the 2/3-way all-in from a single randomly chose player's perspective.
// NOTE: Can't use this with HERO_HANDS_ONLY.
//#define RANDOM_PLAYER_PERPECTIVE

// Define this to read all folders again and again in a loop. Thius is usedful for use with
// RANDOM_PLAYER_PERPECTIVE to get an idea of the level of sample variance to expect from
// just doing a single pass (useful for plots, but no good for results...).
//#define REPEAT_LOOP_ALL_FOLDERS

// Set this to >1 to print less data (for plotting, etc).
#define FILE_OUTPUT_INTERVAL 1

// ===========================================================================

void BackTestAllInLuck(const char** Folders,const int NumFolders)
{	

	// The parser we are going to use.
	PartyNLHoldemSNGParser Parser;

	// The vector of hands we get from the parser.
	vector<HoldemGameStateWithHistory> Hands;

	// This is what we use to get the EV at the end of the hand.
	SNG_ICM_Calculator ICM;

	// These are the possible finish rankings for each outcome.
	const int TwoWayFinishRanking[3][2]    = {{1,1},  {1,2},  {2,1}};
	const int ThreeWayFinishRanking[13][3] = {{1,1,1},{1,1,2},{1,2,1},
											  {1,2,2},{1,2,3},{1,3,2},
											  {2,1,2},{2,2,1},{2,1,3},
											  {2,3,1},{2,1,1},{3,1,2},
	                                          {3,2,1}};

	// These hold the outcomes for each 2-way and 3-way hand matchup.
	// NOTE: Use floats to reduce memory consumption.
	static float TwoWayOutcomeChances[3][169][169];
	static float ThreeWayOutcomeChances[13][169][169][169];

	// The sum of differences from our predicted EV.
	double SumPrizeEquityDifferences=0.0;
	double SumChipEquityDifferences=0.0;

	// The number of SNGs we have played.
	int NumSNGs=0;

	// The number of 2-way, 3-way and multi-way data-points seperately.
	int Num2WayAllIns=0;
	int Num3WayAllIns=0;
	int NumMultiWayAllIns=0;										// We don't deal with these atm.

	// The number fo data-points we have used.
	// NOTE: This can be 2x or 3x the number of all-ins, as we can (if asked) take a data-point from
	//       multiple player's perspectives.
	int NumDataPoints=0;

	// The last tourney number.
	char LastTournamentNumber[BUFFER_SIZE+1];
	LastTournamentNumber[0]=0;

	// *************************************************************************
	// ************** LOAD THE LOOKUP AND/OR CREATE BINARY VERSIONS ************
	// *************************************************************************

	// Have to use two different once because C++ messes up on 2nd use...
	ifstream InFile1,InFile2;

	// This flag gets unset if we failed to loaded either of the (fast) binary versions ok.
	bool LoadedBinary=true;

	// Lets first try and open the binary versions.
	InFile1.open("2WAY_HandEval_binary.dat",ios::binary);
	InFile2.open("3WAY_HandEval_binary.dat",ios::binary);

	// If all ok, then read these instead.
	if (!InFile1.fail() && !InFile2.fail()) {
		InFile1.read((char*)&TwoWayOutcomeChances,sizeof(TwoWayOutcomeChances));
		InFile2.read((char*)&ThreeWayOutcomeChances,sizeof(ThreeWayOutcomeChances));
	}		
	else {
		LoadedBinary=false;
	}

	// Close the input files.
	InFile1.close();
	InFile2.close();

	// if we couldn't open the binary versions, lets load from the test and then save the binary again.
	if (LoadedBinary==false) {

		// Have to use two different once because C++ messes up on 2nd use...
		ifstream InFileT1,InFileT2;

		// Have to use two different once because C++ messes up on 2nd use...
		ofstream OutFile1,OutFile2;

		// Use for reading in the outcome look files.
		int H1,H2,H3;
		double Outcome;

		// Tell the user what's going on.
		cout << "Failed to load binary equity lookup tables. Re-creating them now... "; cout.flush();

		// Open and read each line of the test data files.
		// NOTE: Inversed as HoldemHoleCardRanking::GetIndex() uses AA in top left, A2s top right, etc.
		InFileT1.open("2WAY_HandEval.dat");
		InFileT2.open("3WAY_HandEval.dat");

		// If all ok, then read these in.
		if (InFileT1.fail() || InFileT2.fail())
			Error(FATAL,"Could not load the (text) equity lookups.");
		while (InFileT1 >> H1 >> H2) {
			for (int I=0;I<3;I++)
				InFileT1 >> TwoWayOutcomeChances[I][168-H1][168-H2];
		}
		while (InFileT2 >> H1 >> H2 >> H3) {
			for (int I=0;I<13;I++)
				InFileT2 >> ThreeWayOutcomeChances[I][168-H1][168-H2][168-H3];
		}

		// Close the input files.
		InFileT1.close();
		InFileT2.close();

		// Lets save the binary versions for use next time.
		OutFile1.open("2WAY_HandEval_binary.dat",ios::binary);
		OutFile2.open("3WAY_HandEval_binary.dat",ios::binary);

		// If all OK, save them out.
		if (OutFile1.fail() || OutFile2.fail())
			Error(FATAL,"Could not save the (binary) equity lookups.");
		OutFile1.write((char*)&TwoWayOutcomeChances,sizeof(TwoWayOutcomeChances));
		OutFile2.write((char*)&ThreeWayOutcomeChances,sizeof(ThreeWayOutcomeChances));

		// Close the output files.
		OutFile1.close();
		OutFile2.close();

		// All done creating them now.
		cout << "Done." << endl;

	}

	// *************************************************************************
	// *************************************************************************

	// Open the output file.
	ofstream Outfile;
	Outfile.open("luck_results.csv");
	if (Outfile.fail())
		Error(FATAL,"Could not open 'luck_results.csv'");

	// Lets do it on each folder.
	for (int Folder=0;Folder<NumFolders;Folder++) {

		// Lets try and get the first vector of hands.
		Parser.InitHandHistorySearcher(Folders[Folder]);
		while (Parser.SearchForAndParseNextHandHistoryFile(Hands,true)==true) {

			// Lets keep going until we get no more.
			for (int I=0;I<Hands.size();I++) {

				// Is this a new tournament number?
				if (strcmp(LastTournamentNumber,Hands[I].GetTournamentNumber())!=0) {
					NumSNGs++;
					strcpy_s(LastTournamentNumber,BUFFER_SIZE,Hands[I].GetTournamentNumber());
				}

				// If we have gone from pre-flop to river, then this is an all-in hand.
				if (Hands[I].GetNumStates()>=2
					&& Hands[I].GetCurrentGameStage()==RIVER
					&& Hands[I][Hands[I].GetNumStates()-2].GetCurrentGameStage()==PRE_FLOP
					&& Hands[I].GetTotalInHandCurrently()>=2) {

					// This is the number of all-in players we are dealing with here.
					int NumAllInPlayers=Hands[I].GetTotalInHandCurrently();

					// This is the index of each of the players who are left.
					int PlayerIndex[10];	// ### Only use the first 3, but need 10 incase we start to sim for 4+ players...
					int NumValidShowdownHands=0;

					// The (temp) "finishing rank" assigned depending on outcome.
					int PlayersFinishingRank[10];

					// The chance of each outcome (retrived from the lookup table).
					double ChanceOfOutcome;

					// This holds the rank indexes (0..168) for the player's hand.
					int PlayerHandType[10];

					// The stacks for each player at the start and end of hand.
					int StackAtStartOfHand[10];
					int StackAtEndOfHand[10];

					// These are used to temporailty sim the outcomes.
					int CurrentStacks[10];
					int CurrentStakes[10];
					bool InHandCurrently[10];

					// The actaul EV from the start to end of hand.
					double ActualPrizeEquity,ActualChipEquity;

					// Expected average EV based on the hand matchup.
					double ExpectedPrizeEquity;
					double ExpectedChipEquity;

					// If asked to look at the hands from our perspective only. and we're not here, then skip.
					#ifdef HERO_HANDS_ONLY
					if (Hands[I].GetOurSeatIndex()==NO_SEAT)
						continue;
					#endif

					// @@@@@@@@@@@@@@ Don't really need this @@@@@@@@@@@@@@@@@@@@@
					// Lets make sure that 1 or both players are all-in.
					int NumNonAllInPlayers=0;
					for (int J=0;J<10;J++) {
						if (Hands[I].IsInHandCurrently(J)==true && Hands[I].GetCurrentStack(J)!=0)
							NumNonAllInPlayers++;
					}
					if (NumNonAllInPlayers>1) {
						Error(WARNING,"Non all-in hand (should not get here...).");
						continue;
					}
					// @@@@@@@@@@@@@@ Don't really need this @@@@@@@@@@@@@@@@@@@@@

					// If the player is in the hand currently and his cards are known, then lets count him and get his seat index.
					for (int J=0;J<10;J++) {
						if (Hands[I].IsInHandCurrently(J)==true && Hands[I].GetShowdownHoleCard(J,0).Rank!=RANK_NONE) {
							PlayerIndex[NumValidShowdownHands]=J;
							PlayerHandType[J]=HoldemHoleCardRanking::GetIndex((Hands[I].GetShowdownHoleCard(J,0).Suit*13)+Hands[I].GetShowdownHoleCard(J,0).Rank,
																			  (Hands[I].GetShowdownHoleCard(J,1).Suit*13)+Hands[I].GetShowdownHoleCard(J,1).Rank);
							NumValidShowdownHands++;	// One more.
						}
					}

					// If we don't have cards for all the players at the end then skip this hand.
					if (NumValidShowdownHands!=NumAllInPlayers)
						continue;

					// Lets work out the stacks at the start/end of the hand.
					for (int K=0;K<10;K++) {
						StackAtStartOfHand[K]=0;
						StackAtEndOfHand[K]=0;
					}
					for (int K=0;K<10;K++) {
						if (Hands[I].WasInHandAtStartOfHand(K)==true) {
							StackAtStartOfHand[K]=Hands[I].GetStackAtStartOFHand(K);
							StackAtEndOfHand[K]=Hands[I].GetCurrentStack(K)+Hands[I].GetUnCalledBets(K)+Hands[I].GetAmountWon(K);
						}
					}

					// Check we have not lost any chips.
					// #### NOTE: This happens for truncated HH files where their is no "wins" line...
					int TotalChipsStart=0;
					int TotalChipsEnd=0;
					for (int K=0;K<10;K++) {
						TotalChipsStart+=StackAtStartOfHand[K];
						TotalChipsEnd+=StackAtEndOfHand[K];
					}
					if (TotalChipsStart!=TotalChipsEnd) {
						Error(WARNING,Hands[I].GetHandNumber(),"Chips at end of hand != start of hand. Ignoreing hand...");
						continue;
					}

					// At the moment we can only deal with 2-way and 3-way all-in, but we will count the rest.
					if (NumAllInPlayers>3) {
						NumMultiWayAllIns++;
						continue;
					}
					else if (NumAllInPlayers==3) {
						Num3WayAllIns++;
					}
					else {
						Num2WayAllIns++;
					}

					// We must make sure that player's indexes are ordered based on their cards (for lookup into the outcome tables).
					// NOTE: Bubble-sort is fine for this...
					for (int J=0;J<(NumAllInPlayers-1);J++) {
						for (int K=J+1;K<NumAllInPlayers;K++) {
							if (PlayerHandType[PlayerIndex[J]]>PlayerHandType[PlayerIndex[K]]) {
								int Temp=PlayerIndex[J];
								PlayerIndex[J]=PlayerIndex[K];
								PlayerIndex[K]=Temp;
							}	
						}
					}

					// Chose one player randomly.
					#ifdef RANDOM_PLAYER_PERPECTIVE
					int ChosenPlayer=RandInt(NumAllInPlayers-1);
					#endif

					// Now lets do the calculation for each of the players.
					for (int Player=0;Player<NumAllInPlayers;Player++) {


						// If asked to, then lets looks at the hands from our perspective only.
						#ifdef HERO_HANDS_ONLY
						if (Hands[I].GetOurSeatIndex()!=PlayerIndex[Player])
							continue;
						#else

						// We can also look the luck from just one of the players involved in the all-in.
						#ifdef RANDOM_PLAYER_PERPECTIVE
						if (Player!=ChosenPlayer)
							continue;
						#endif
						#endif

						// Lets print the hand number first. @@@ DEBUG ONLY @@@
						//Outfile << Hands[I].GetHandNumber() << ", ";

						// If we didn't just bust out we need to calculate the real EV using ICM.
						if (ICM.TestBustout(StackAtStartOfHand,StackAtEndOfHand,PlayerIndex[Player])==true)
							ActualPrizeEquity=ICM.CalculateBustoutPrizeEV(StackAtStartOfHand,StackAtEndOfHand,PlayerIndex[Player]);
						else
							ActualPrizeEquity=ICM.CalculatePrizeEV(StackAtEndOfHand,PlayerIndex[Player]);

						// Get the cEV too.
						ActualChipEquity=StackAtEndOfHand[PlayerIndex[Player]];

						// Init ready to sum for each outcome.
						ExpectedPrizeEquity=0.0;
						ExpectedChipEquity=0.0;

						// Lets go through the three possible outcomes.
						for (int OutCome=0;OutCome<(NumAllInPlayers==2?3:13);OutCome++) {

							// We can share the pot much faster if only 2 players.
							if (NumAllInPlayers==2) {

								// Lets assign a finishing rank to each player based on the current outcome index.
								for (int J=0;J<2;J++)
									PlayersFinishingRank[PlayerIndex[J]]=TwoWayFinishRanking[OutCome][J];

								// Get the chance of outcome from the lookup table.
								ChanceOfOutcome=TwoWayOutcomeChances[OutCome][PlayerHandType[PlayerIndex[0]]][PlayerHandType[PlayerIndex[1]]];

								// Lets make a copy of the current stacks and amounts staked.
								// NOTE: For the 2-way code we need to take off the amount staked AFTER we have equalized the stakes below.
								for (int J=0;J<10;J++) {
									CurrentStacks[J]=Hands[I].GetStackAtStartOFHand(J);
									CurrentStakes[J]=Hands[I].GetStackAtStartOFHand(J)-Hands[I].GetCurrentStack(J);
								}

								// Get the chance of outcome from the lookup table.
								ChanceOfOutcome=TwoWayOutcomeChances[OutCome][PlayerHandType[PlayerIndex[0]]][PlayerHandType[PlayerIndex[1]]];

								// Lets return any uncalled bets so they have both staked the same.
								CurrentStakes[PlayerIndex[0]]=Min(CurrentStakes[PlayerIndex[0]],CurrentStakes[PlayerIndex[1]]);
								CurrentStakes[PlayerIndex[1]]=CurrentStakes[PlayerIndex[0]];

								// Now work out the size of the pot available to each left in the hand.
								int CurrentPot=0;
								for (int J=0;J<10;J++) {
									CurrentPot+=CurrentStakes[J];
									CurrentStacks[J]-=CurrentStakes[J]; // Take off now.
								}

								// Lets work out the chance dependig on the outcome type.
								// NOTE: We assign any odd chips to an aribtary player.
								if (PlayersFinishingRank[PlayerIndex[0]]<PlayersFinishingRank[PlayerIndex[1]]) {
									CurrentStacks[PlayerIndex[0]]+=CurrentPot;
								}
								else if (PlayersFinishingRank[PlayerIndex[1]]<PlayersFinishingRank[PlayerIndex[0]]) {
									CurrentStacks[PlayerIndex[1]]+=CurrentPot;
								}
								else {
									CurrentStacks[PlayerIndex[0]]+=(CurrentPot/2)+(CurrentPot%2);
									CurrentStacks[PlayerIndex[1]]+=(CurrentPot/2);
								}

							} // End 2-way pot shareout.

							// Will have to do the slower way for 3 players them.
							else {

								// Lets assign a finishing rank to each player based on the current outcome index.
								for (int J=0;J<3;J++)
									PlayersFinishingRank[PlayerIndex[J]]=ThreeWayFinishRanking[OutCome][J];

								// Get the chance of outcome from the lookup table.
								ChanceOfOutcome=ThreeWayOutcomeChances[OutCome][PlayerHandType[PlayerIndex[0]]][PlayerHandType[PlayerIndex[1]]][PlayerHandType[PlayerIndex[2]]];

								// Lets make a copy of the current stacks, amounts staked and Inhand flags.
								// NOTE: For the 3-way code we need to take off the amount staked BEFORE.
								for (int J=0;J<10;J++) {
									CurrentStacks[J]=Hands[I].GetCurrentStack(J);
									CurrentStakes[J]=Hands[I].GetStackAtStartOFHand(J)-Hands[I].GetCurrentStack(J);
									InHandCurrently[J]=Hands[I].IsInHandCurrently(J);
								}

								// Lets share out all the side-pots.
								for (;;) {

									// Lets set any player who has nothing staked anymore to not be in the hand.
									for (int J=0;J<10;J++)
										if (CurrentStakes[J]==0)
											InHandCurrently[J]=false;					// Not in hand now.

									// Find he smallest amount staked.
									int MinAmountStaked=0x7fffffff;
									for (int J=0;J<10;J++)
										if (InHandCurrently[J]==true)
											MinAmountStaked=Min(MinAmountStaked,CurrentStakes[J]);

									// Create the side-pot and subtract from the staked amounts.
									int CurrentPot=0;
									for (int J=0;J<10;J++) {
										CurrentPot+=Min(CurrentStakes[J],MinAmountStaked);
										CurrentStakes[J]-=Min(CurrentStakes[J],MinAmountStaked);
									}

									// Test if we are done.
									// NOTE: The last side-pot will just be the biggest stack geting his excess chips returned.
									if (CurrentPot==0)
										break;

									// Share out the pot based on the hand rank of each player.
									int NumHandWinners=0;
									int BestHand=0x7fffffff;
									for (int J=0;J<10;J++) {
										if (InHandCurrently[J]==true) {
											if (PlayersFinishingRank[J]<BestHand) {
												NumHandWinners=1;
												BestHand=PlayersFinishingRank[J];
											}
											else if (PlayersFinishingRank[J]==BestHand) {
												NumHandWinners++;
											}
										}
									}

									// Now lets take off the smount they put into the vurrent pot and give them any pot they might need.
									// NOTE: We assign any odd chips to an aribtary player.
									int OddChips=(CurrentPot%NumHandWinners);
									for (int J=0;J<10;J++) {
										if (InHandCurrently[J]==true && PlayersFinishingRank[J]==BestHand) {
											CurrentStacks[J]+=(CurrentPot/NumHandWinners)+OddChips;
											OddChips=0;			// If there were any, they have been assigned now.
										}
									}

								}

							} // End 3-way pot shareout.

							double TempExpectedPrizeEquity,TempExpectedChipEquity,TempPLoseDiff;

							// Work out our $EV for this outcome.
							if (ICM.TestBustout(StackAtStartOfHand,CurrentStacks,PlayerIndex[Player])==true)
								TempExpectedPrizeEquity=ICM.CalculateBustoutPrizeEV(StackAtStartOfHand,CurrentStacks,PlayerIndex[Player]);
							else
								TempExpectedPrizeEquity=ICM.CalculatePrizeEV(CurrentStacks,PlayerIndex[Player]);

							// Work out the cEV for this outcome.
							TempExpectedChipEquity=CurrentStacks[PlayerIndex[Player]];

							// Lets add to the sums.
							ExpectedPrizeEquity+=TempExpectedPrizeEquity*ChanceOfOutcome;
							ExpectedChipEquity+=TempExpectedChipEquity*ChanceOfOutcome;

							// Lets print the EVs and the outcomes. @@@ DEBUG ONLY @@@
							// @@@ This file is gonna be a nightmare to read, as it will have different numbers of feilds for 2-way and 3-way lines...
							//Outfile << ChanceOfOutcome << ',' << TempExpectedPrizeEquity << ',' << TempExpectedChipEquity << ", ";

						} // End for each outcome.

						SumPrizeEquityDifferences+=ActualPrizeEquity-ExpectedPrizeEquity;
						SumChipEquityDifferences+=ActualChipEquity-ExpectedChipEquity;

						// Lets print the EV for this hand. @@@ DEBUG ONLY @@@
						//Outfile << ExpectedPrizeEquity << ',' << ActualPrizeEquity << ',' << ActualPrizeEquity-ExpectedPrizeEquity << ", "
						//		<< ExpectedChipEquity << ',' << ActualChipEquity << ',' << ActualChipEquity-ExpectedChipEquity << ", ";

						// Lets print the cumulative versions for plotting, etc.
						if (NumDataPoints%FILE_OUTPUT_INTERVAL==0)
							Outfile << SumPrizeEquityDifferences*100.0 << ','<< SumChipEquityDifferences*100.0 << endl;

						NumDataPoints++;

					} // End for each player.

				} // End if valid all-in hand.

			} // End for each hand in hand history.

			// Print the difference between expected and real.
			cout << "NumSNGs: " << NumSNGs
				 << " / NumAllIns[2,3,4+]: " << Num2WayAllIns << ',' << Num3WayAllIns << ',' << NumMultiWayAllIns
				 << " / NumDataPoints: " << NumDataPoints << endl
				 << "$EV Luck: " << (int)(SumPrizeEquityDifferences*100.0)
				 << "% (" << (SumPrizeEquityDifferences*100.0)/(double)(NumDataPoints>0?NumDataPoints:1)
				 << "% per all-in)" << endl
				 << "cEV Luck: " << (int)SumChipEquityDifferences
				 << "t (" << SumChipEquityDifferences/(double)(NumDataPoints>0?NumDataPoints:1)
				 << "t per all-in)" << endl;

		} // End for each hand history in folder.

		// If asked to, then reset to use again.
		#ifdef REPEAT_LOOP_ALL_FOLDERS
		if (Folder==NumFolders-1)
			Folder=-1;
		#endif

	} // For each folder.

	// Close the output file.
	Outfile.close();

} // End BackTestAllInLuck.
Juk
SnG Luck Analyzer (Beta) Quote
01-26-2008 , 06:06 PM
I've managed to compress the binary version of the lookups quite well now and I think I've managed to turn off all the Intel-specific optimizations I was using, so here is the EXE (with lookups) created from the above source code: http://www.jukofyork.com/Juks_BackTestAllInLuck.rar

Instructions
1) Download it and unrar it to a folder somewhere.
2) Copy some of your 10-man Party HH files into the same folder.
3) Run the batch file called "RunMe.bat" and leave it to do it's work
4) After it's finished it will tell you your total $EV (and cEV) luck differences.
5) It will also create a file called "luck_results.csv" which you can load into Excel to plot your cumulative $EV (and cEV) luck.

NOTE: It doesn't work with anything other than Party 10-man SNGs.


Please post your results/graphs (or any problems you have), as I'd be interested to see just what other people's results are looking like. I seem to have been a huge luckbox last year, but now I'm paying it back this year:





Juk

PS: I should mention it expects your HH files to be named alphabetically in order of there creation. This is fine for hands that have either been passed through PT or hands which have been exported from PT, but those saved by the Party client itself might not be in order (you can check by looking in the DOS window as each file gets processed). This will only effect the cumulative graph though; the total luck amounts will be unaffected.

Last edited by jukofyork; 01-26-2008 at 06:16 PM.
SnG Luck Analyzer (Beta) Quote
01-26-2008 , 08:21 PM
Juk, would this work on older Party SNGs (processed through PT)? Say before 10/06?
SnG Luck Analyzer (Beta) Quote
01-26-2008 , 09:14 PM
Quote:
Originally Posted by jukofyork
NOTE: It doesn't work with anything other than Party 10-man SNGs.
I got all excited until I read this.
SnG Luck Analyzer (Beta) Quote
01-27-2008 , 12:37 PM
Juk i tried your script on this month's SnGs (all hands exported from PT) and had quite a few error messages: "Tournament number not found" (or something to this effect). Did you get any of these?

I've played 896 SnGs this month at a 5.31% roi but your script only read 513 of them.

fwiw the numbers were:
513 played: -220% / -10490t

any ideas what caused the errors? otherwise, very nice app! - thanks
SnG Luck Analyzer (Beta) Quote
01-27-2008 , 12:51 PM
Quote:
Originally Posted by magog
Juk i tried your script on this month's SnGs (all hands exported from PT) and had quite a few error messages: "Tournament number not found" (or something to this effect). Did you get any of these?

I've played 896 SnGs this month at a 5.31% roi but your script only read 513 of them.

fwiw the numbers were:
513 played: -220% / -10490t

any ideas what caused the errors? otherwise, very nice app! - thanks
Not sure what could be causing this, but it's most likely something simple to fix. Could you sent me 2/3 of the exported tournaments that have hands in them that fail and a copy of the "error.log" file that is created when you run on these tournaments.

Juk
SnG Luck Analyzer (Beta) Quote
01-27-2008 , 02:47 PM
Juk - I've copied and pasted the error messages from two hands as well as the hands themselves below. hope this helps

WARNING: [6648695720|Player acted out-of-turn.] Continuing...
WARNING: [6648695720|NewAction() FOLD failed... Skipping to next hand.] Continuing...
WARNING: [6648717882|Player acted out-of-turn.] Continuing...
WARNING: [6648717882|NewAction() FOLD failed... Skipping to next hand.] Continuing...


#Game No : 6648695720
Game #<do not remove this line!> starts.
***** Hand History for Game 6648695720 *****
NL Texas Hold'em $55 USD Buy-in Trny: 37600751 Level: 2 Blinds(30/60) - Wednesday, January 02, 09:27:12 ET 2008
Table Speed #1330312 (Real Money)
Seat 2 is the button
Total number of players : 10
Seat 9: Leendo ( 1,940 )
Seat 4: Hero ( 2,040 )
Seat 2: staabbes ( 2,190 )
Seat 3: lifthrasir1 ( 2,510 )
Seat 10: Koin1234 ( 1,600 )
Seat 5: Clamps ( 1,940 )
Seat 7: alina155 ( 1,700 )
Seat 6: Chimaera777 ( 3,060 )
Seat 1: Nedbo ( 1,960 )
Seat 8: Belmondo21 ( 1,060 )
Trny: 37600751 Level: 2
Blinds(30/60)
** Dealing down cards **
Dealt to Hero [ 9h Ks ]
Chimaera777 folds.
Clamps folds.
alina155 calls [60]
Belmondo21 calls [60]
Leendo calls [60]
Koin1234 folds.
Nedbo folds.
staabbes folds.
>You have options at Speed #1330663 Table!.
lifthrasir1 folds.
Hero checks.
** Dealing Flop ** [ 2d, Jd, 6c ]
Hero checks.
alina155 checks.
Belmondo21 checks.
Leendo checks.
** Dealing Turn ** [ 7s ]
Hero checks.
alina155 checks.
Belmondo21 checks.
Leendo checks.
** Dealing River ** [ Jh ]
Hero checks.
alina155 checks.
Belmondo21 checks.
Leendo checks.
Hero shows [ 9h, Ks ]a pair of Jacks.
alina155 shows [ Qc, Ah ]a pair of Jacks.
Belmondo21 doesn't show [ 4s, As ]a pair of Jacks.
Leendo doesn't show [ Ad, Th ]a pair of Jacks.
alina155 wins 270 chips from the main pot with a pair of Jacks with Queen kicker.


#Game No : 6648717882
Game #<do not remove this line!> starts.
***** Hand History for Game 6648717882 *****
NL Texas Hold'em $55 USD Buy-in Trny: 37600751 Level: 3 Blinds(50/100) - Wednesday, January 02, 09:36:07 ET 2008
Table Speed #1330312 (Real Money)
Seat 5 is the button
Total number of players : 8
Seat 9: Leendo ( 1,880 )
Seat 4: Hero ( 4,210 )
Seat 2: staabbes ( 1,940 )
Seat 3: lifthrasir1 ( 2,330 )
Seat 10: Koin1234 ( 3,230 )
Seat 5: Clamps ( 1,790 )
Seat 7: alina155 ( 3,620 )
Seat 6: Chimaera777 ( 1,000 )
Trny: 37600751 Level: 3
Blinds(50/100)
** Dealing down cards **
Dealt to Hero [ 4h 2h ]
Koin1234 folds.
Leendo folds.
staabbes folds.
lifthrasir1 folds.
Hero folds.
Clamps folds.
Chimaera777 folds.
alina155 does not show cards.
alina155 wins 150 chips
SnG Luck Analyzer (Beta) Quote
01-27-2008 , 03:17 PM
It doesn't seem to be any problem with my parser, as the players are truly acting "out-of-turn", eg:

Quote:
#Game No : 6648695720
Game #<do not remove this line!> starts.
***** Hand History for Game 6648695720 *****
NL Texas Hold'em $55 USD Buy-in Trny: 37600751 Level: 2 Blinds(30/60) - Wednesday, January 02, 09:27:12 ET 2008
Table Speed #1330312 (Real Money)
Seat 2 is the button
Total number of players : 10
Seat 9: Leendo ( 1,940 )
Seat 4: Hero ( 2,040 )
Seat 2: staabbes ( 2,190 )
Seat 3: lifthrasir1 ( 2,510 )
Seat 10: Koin1234 ( 1,600 )
Seat 5: Clamps ( 1,940 )
Seat 7: alina155 ( 1,700 )
Seat 6: Chimaera777 ( 3,060 )
Seat 1: Nedbo ( 1,960 )
Seat 8: Belmondo21 ( 1,060 )
Trny: 37600751 Level: 2
Blinds(30/60)
** Dealing down cards **
Dealt to Hero [ 9h Ks ]
Chimaera777 folds.
Clamps folds.
If Seat 2 is the button (and there is no dead SB), then Seat 3 should be the SB, Seat 4 should be the BB and Seat 5 should be UTG. But the hand history has is as UTG+1 folds (Chimaera777 / Seat 6) then UTG folds (Clamps / Seat 5).

Quote:
#Game No : 6648717882
Game #<do not remove this line!> starts.
***** Hand History for Game 6648717882 *****
NL Texas Hold'em $55 USD Buy-in Trny: 37600751 Level: 3 Blinds(50/100) - Wednesday, January 02, 09:36:07 ET 2008
Table Speed #1330312 (Real Money)
Seat 5 is the button
Total number of players : 8
Seat 9: Leendo ( 1,880 )
Seat 4: Hero ( 4,210 )
Seat 2: staabbes ( 1,940 )
Seat 3: lifthrasir1 ( 2,330 )
Seat 10: Koin1234 ( 3,230 )
Seat 5: Clamps ( 1,790 )
Seat 7: alina155 ( 3,620 )
Seat 6: Chimaera777 ( 1,000 )
Trny: 37600751 Level: 3
Blinds(50/100)
** Dealing down cards **
Dealt to Hero [ 4h 2h ]
Koin1234 folds.
Leendo folds.
Same strange problem: if Seat 5 is the button (and there is no dead SB), then Seat 6 should be the SB, Seat 7 should be the BB and Seat 9 should be UTG (Seat 8 not in anymore). But again, the hand history has is as UTG+1 folds (Koin1234 / Seat 10) then UTG folds (Leendo / Seat 9).

From your other post it sounds like this must be happening an awful lot, as the only way the total # of SNGs printed could be less than what you put in is if entire files full of hands are being rejected by the parser.

I've never seen this problem before and I wonder if it's related to PT messing up the exported files - Have you updated your PT recently? Also, do you save the original HH files? If so, then try looking to see if they have the same "out-of-order" problem and if not, try running my app on them instead.

Juk
SnG Luck Analyzer (Beta) Quote

      
m