4 Copyright (c) 2010 Lode Vandevenne
7 This file is part of OOPoker.
9 OOPoker is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 OOPoker is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with OOPoker. If not, see <http://www.gnu.org/licenses/>.
23 #include "statistics.h"
27 double PlayerStats::getVPIP () const
30 VP$IP Percentage: Voluntary Put Money In Pot: number of deals
31 where the player put money in the pot pre-flop. Voluntary means
32 that if the player is big blind and could check, it does NOT count
33 towards this percentage. In other words, this is the amount of times
34 the player chose to pay to see the flop. If the player first voluntary
35 put money in the pot, but later folded due to a raise, his first
36 raise is still added to this statistic. If he called the raise, this
37 is NOT counted double towards this statistic, only the first time he
38 put money in the pot counts.
40 This statistic indicates players tightness.
42 This is normally a percentage, but is given as a value in the range 0.0 - 1.0 (where 1.0 means 100%)
45 Neutral: Between 0.24-0.3
46 Loose: Greater than 0.3
49 return (double)(deal_preflop_calls
+ deal_preflop_bets
+ deal_preflop_raises
) / (double)deals
;
52 double PlayerStats::getPFR() const
54 //Pre-Flop Raise percentage
55 return (double)(deal_preflop_bets
+ deal_preflop_raises
) / (double)deals
;
58 double PlayerStats::get3BetPF() const
60 return (double)(deal_preflop_raises
) / (double)deals
;
63 double PlayerStats::getWSD() const
65 //low means the player plays solid. High means the player overplays his cards.
66 return (double)showdowns_seen
/ (double)flops_seen
;
69 double PlayerStats::getWSDW() const
71 return (double)wins_showdown
/ (double)showdowns_seen
;
74 double PlayerStats::getAF() const
79 Passive: Less than 1.5
80 Neutral: Between 1-1.5
81 Aggressive: Greater than 1.5
83 Measured only after the flop, so all preflop actions are subtracted.
86 return (double)((bets
- preflop_bets
) + (raises
- preflop_raises
)) / (double)(calls
- preflop_calls
);
89 std::string
statisticsToString(const PlayerStats
& stats
)
92 ss
<< std::endl
<< "Player Stats for "<< stats
.name
<< ", AI: " << stats
.ai
<< std::endl
;
93 ss
<< "General: " << "deals: " << stats
.deals
<< ", actions: " << stats
.actions
<< ", preflop actions: " << stats
.preflop_actions
<< std::endl
;
94 ss
<< "Rounds Seen: " << "flops: " << stats
.flops_seen
<< ", turns: " << stats
.turns_seen
<< ", rivers: " << stats
.rivers_seen
<< ", showdowns: " << stats
.showdowns_seen
<< std::endl
;
95 ss
<< "Wins: " << "total: " << stats
.wins_total
<< ", showdown: " << stats
.wins_showdown
<< ", bluff: " << stats
.wins_bluff
<< std::endl
;
96 ss
<< "Chips: " << "won: " << stats
.chips_won
<< ", lost: " << stats
.chips_lost
<< ", bought: " << stats
.chips_bought
<< ", forced bets: " << stats
.forced_bets
<< std::endl
;
97 //ss << "Actions: [Fold, Check, Call, Bet, Raise, All-In]" << std::endl;
98 //ss << "Total: " << stats.folds << " " << stats.checks << " " << stats.calls << " " << stats.bets << " " << stats.raises << " " << stats.allins << std::endl;
99 //ss << "Pre-flop: " << stats.preflop_folds << " " << stats.preflop_checks << " " << stats.preflop_calls << " " << stats.preflop_bets << " " << stats.preflop_raises << " " << stats.preflop_allins << std::endl;
100 //ss << "Flop: " << stats.flop_folds << " " << stats.flop_checks << " " << stats.flop_calls << " " << stats.flop_bets << " " << stats.flop_raises << " " << stats.flop_allins << std::endl;
101 //ss << "Turn: " << stats.turn_folds << " " << stats.turn_checks << " " << stats.turn_calls << " " << stats.turn_bets << " " << stats.turn_raises << " " << stats.turn_allins << std::endl;
102 //ss << "River: " << stats.river_folds << " " << stats.river_checks << " " << stats.river_calls << " " << stats.river_bets << " " << stats.river_raises << " " << stats.river_allins << std::endl;
103 ss
<< "Pre-Flop Stats: " << "VP$IP: " << stats
.getVPIP() << ", PFR: " << stats
.getPFR() << ", 3Bet: " << stats
.get3BetPF() << std::endl
;
104 ss
<< "Post-Flop Stats: " << "AF: " << stats
.getAF() << ", WSD: " << stats
.getWSD() << ", WSDW: " << stats
.getWSDW() << std::endl
;
108 std::string
statisticsToString(const StatKeeper
& statKeeper
)
111 std::vector
<std::string
> players
;
112 statKeeper
.getAllPlayers(players
);
114 for(size_t i
= 0; i
< players
.size(); i
++)
116 const PlayerStats
* stats
= statKeeper
.getPlayerStats(players
[i
]);
117 result
+= statisticsToString(*stats
);
124 PlayerStats::PlayerStats(const std::string
& name
)
126 //set everything automatically to 0
127 size_t start_mem
= sizeof(std::string
) * 2;
128 char* c
= (char*)(this) + start_mem
;
129 for(size_t i
= 0; i
< sizeof(PlayerStats
) - start_mem
; i
++) c
[i
] = 0;
137 TableStats::TableStats()
139 //set everything automatically to 0
140 char* c
= (char*)this;
141 for(size_t i
= 0; i
< sizeof(TableStats
); i
++) c
[i
] = 0;
145 StatKeeper::MyPlayerInfo::MyPlayerInfo(const std::string
& name
)
152 StatKeeper::StatKeeper()
157 StatKeeper::~StatKeeper()
159 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
165 void StatKeeper::getAllPlayers(std::vector
<std::string
>& players
) const
167 for(std::map
<std::string
, MyPlayerInfo
*>::const_iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
169 players
.push_back(it
->first
);
173 StatKeeper::MyPlayerInfo
* StatKeeper::getPlayerStatsInternal(const std::string
& player
)
175 if(statmap
.find(player
) == statmap
.end())
177 statmap
[player
] = new MyPlayerInfo(player
);
180 return statmap
[player
];
183 void StatKeeper::onEvent(const Event
& event
)
185 MyPlayerInfo
* info
= 0;
186 if(!event
.player
.empty()) info
= getPlayerStatsInternal(event
.player
);
187 PlayerStats
* stats
= &info
->stats
;
189 int* round_folds
= 0;
190 int* round_checks
= 0;
191 int* round_calls
= 0;
193 int* round_raises
= 0;
194 int* round_allins
= 0;
195 int* round_actions
= 0;
196 if(round
== R_PRE_FLOP
)
198 round_folds
= &stats
->preflop_folds
;
199 round_checks
= &stats
->preflop_checks
;
200 round_calls
= &stats
->preflop_calls
;
201 round_bets
= &stats
->preflop_bets
;
202 round_raises
= &stats
->preflop_raises
;
203 round_allins
= &stats
->preflop_allins
;
204 round_actions
= &stats
->preflop_actions
;
206 else if(round
== R_FLOP
)
208 round_folds
= &stats
->flop_folds
;
209 round_checks
= &stats
->flop_checks
;
210 round_calls
= &stats
->flop_calls
;
211 round_bets
= &stats
->flop_bets
;
212 round_raises
= &stats
->flop_raises
;
213 round_allins
= &stats
->flop_allins
;
214 round_actions
= &stats
->flop_actions
;
216 else if(round
== R_TURN
)
218 round_folds
= &stats
->turn_folds
;
219 round_checks
= &stats
->turn_checks
;
220 round_calls
= &stats
->turn_calls
;
221 round_bets
= &stats
->turn_bets
;
222 round_raises
= &stats
->turn_raises
;
223 round_allins
= &stats
->turn_allins
;
224 round_actions
= &stats
->turn_actions
;
226 else if(round
== R_RIVER
)
228 round_folds
= &stats
->river_folds
;
229 round_checks
= &stats
->river_checks
;
230 round_calls
= &stats
->river_calls
;
231 round_bets
= &stats
->river_bets
;
232 round_raises
= &stats
->river_raises
;
233 round_allins
= &stats
->river_allins
;
234 round_actions
= &stats
->river_actions
;
237 int numchips_placed
= 0; //used for detecting all-in
243 info
->joined
= false;
249 stats
->chips_bought
+= event
.chips
;
250 info
->stack
+= event
.chips
;
255 stats
->chips_bought
+= event
.chips
;
256 info
->stack
+= event
.chips
;
264 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
266 MyPlayerInfo
* p
= it
->second
;
270 p
->deal_preflop_stat
= 0;
281 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
283 MyPlayerInfo
* p
= it
->second
;
286 int d
= p
->deal_stat
;
287 if(d
== 0) p
->stats
.deal_first_action_folds
++;
288 else if(d
== 1) p
->stats
.deal_checks
++;
289 else if(d
== 2) p
->stats
.deal_calls
++;
290 else if(d
== 3) p
->stats
.deal_bets
++;
291 else if(d
== 4) p
->stats
.deal_raises
++;
293 d
= p
->deal_preflop_stat
;
294 if(d
== 0) p
->stats
.deal_preflop_first_action_folds
++;
295 else if(d
== 1) p
->stats
.deal_preflop_checks
++;
296 else if(d
== 2) p
->stats
.deal_preflop_calls
++;
297 else if(d
== 3) p
->stats
.deal_preflop_bets
++;
298 else if(d
== 4) p
->stats
.deal_preflop_raises
++;
304 case E_REVEAL_AI
: stats
->ai
= event
.ai
; break; //this is the only event we can finally read the ai from!
307 stats
->forced_bets
+= event
.chips
;
308 highestBet
= event
.chips
;
309 numchips_placed
= event
.chips
;
314 stats
->forced_bets
+= event
.chips
;
315 if(event
.chips
> highestBet
) highestBet
= event
.chips
; //it could be that the player is all-in and has less chips
316 numchips_placed
= event
.chips
;
321 stats
->forced_bets
+= event
.chips
;
322 if(event
.chips
> highestBet
) highestBet
= event
.chips
; //happens e.g. if ante is bigger than the stack of the small blind and the big blind when they're both all-in
323 numchips_placed
= event
.chips
;
329 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
331 MyPlayerInfo
* p
= it
->second
;
332 if(!p
->folded
) p
->stats
.flops_seen
++;
339 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
341 MyPlayerInfo
* p
= it
->second
;
342 if(!p
->folded
) p
->stats
.turns_seen
++;
349 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
351 MyPlayerInfo
* p
= it
->second
;
352 if(!p
->folded
) p
->stats
.rivers_seen
++;
359 for(std::map
<std::string
, MyPlayerInfo
*>::iterator it
= statmap
.begin(); it
!= statmap
.end(); ++it
)
361 MyPlayerInfo
* p
= it
->second
;
362 if(!p
->folded
) p
->stats
.showdowns_seen
++;
381 if(info
->deal_stat
< 1) info
->deal_stat
= 1;
382 if(round
== R_PRE_FLOP
&& info
->deal_preflop_stat
< 1) info
->deal_preflop_stat
= 1;
391 if(info
->deal_stat
< 2) info
->deal_stat
= 2;
392 if(round
== R_PRE_FLOP
&& info
->deal_preflop_stat
< 2) info
->deal_preflop_stat
= 2;
394 numchips_placed
= highestBet
- info
->wager
;
400 int callAmount
= highestBet
- info
->wager
;
402 if(callAmount
== 0) //bet
406 if(info
->deal_stat
< 3) info
->deal_stat
= 3;
407 if(round
== R_PRE_FLOP
&& info
->deal_preflop_stat
< 3) info
->deal_preflop_stat
= 3;
413 if(info
->deal_stat
< 4) info
->deal_stat
= 4;
414 if(round
== R_PRE_FLOP
&& info
->deal_preflop_stat
< 4) info
->deal_preflop_stat
= 4;
420 numchips_placed
= callAmount
+ event
.chips
; //since the event only contains the raise amount, we need to add the call amount to get the total amount of chips the player moved to the table
422 highestBet
+= event
.chips
;
428 stats
->chips_won
+= event
.chips
;
429 info
->stack
+= event
.chips
;
431 if(round
== R_SHOWDOWN
) stats
->wins_showdown
++;
432 else stats
->wins_bluff
++;
437 if(numchips_placed
> 0)
439 stats
->chips_lost
+= numchips_placed
;
440 info
->stack
-= numchips_placed
;
441 info
->wager
+= numchips_placed
;
442 bool allin
= info
->stack
<= 0;
451 const PlayerStats
* StatKeeper::getPlayerStats(const std::string
& player
) const
453 std::map
<std::string
, MyPlayerInfo
*>::const_iterator it
= statmap
.find(player
);
455 if(it
== statmap
.end()) return 0;
456 else return &statmap
.find(player
)->second
->stats
;
459 const TableStats
* StatKeeper::getTableStats() const