update readme
[rofl0r-oopoker.git] / statistics.cpp
blob89419d3c34038ecf2b392f6ddbd14f6820165489
1 /*
2 OOPoker
4 Copyright (c) 2010 Lode Vandevenne
5 All rights reserved.
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"
25 #include <sstream>
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%)
44 Tight: Less than 0.24
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
77 Aggression factor:
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)
91 std::stringstream ss;
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;
105 return ss.str();
108 std::string statisticsToString(const StatKeeper& statKeeper)
110 std::string result;
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);
118 result += "\n";
121 return result;
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;
131 this->name = name;
132 this->ai = "";
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)
146 : stats(name)
147 , stack(0)
148 , wager(0)
152 StatKeeper::StatKeeper()
153 : round(R_PRE_FLOP)
157 StatKeeper::~StatKeeper()
159 for(std::map<std::string, MyPlayerInfo*>::iterator it = statmap.begin(); it != statmap.end(); ++it)
161 delete it->second;
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;
192 int* round_bets = 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
239 switch(event.type)
241 case E_QUIT:
243 info->joined = false;
244 break;
246 case E_JOIN:
248 info->joined = true;
249 stats->chips_bought += event.chips;
250 info->stack += event.chips;
251 break;
253 case E_REBUY:
255 stats->chips_bought += event.chips;
256 info->stack += event.chips;
257 break;
259 case E_NEW_DEAL:
261 round = R_PRE_FLOP;
262 highestBet = 0;
264 for(std::map<std::string, MyPlayerInfo*>::iterator it = statmap.begin(); it != statmap.end(); ++it)
266 MyPlayerInfo* p = it->second;
267 p->wager = 0;
268 p->folded = false;
269 p->deal_stat = 0;
270 p->deal_preflop_stat = 0;
271 if(p->joined)
273 p->stats.deals++;
277 break;
279 case E_POT_DIVISION:
281 for(std::map<std::string, MyPlayerInfo*>::iterator it = statmap.begin(); it != statmap.end(); ++it)
283 MyPlayerInfo* p = it->second;
284 if(p->joined)
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++;
302 break;
304 case E_REVEAL_AI: stats->ai = event.ai; break; //this is the only event we can finally read the ai from!
305 case E_SMALL_BLIND:
307 stats->forced_bets += event.chips;
308 highestBet = event.chips;
309 numchips_placed = event.chips;
310 break;
312 case E_BIG_BLIND:
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;
317 break;
319 case E_ANTE:
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;
324 break;
326 case E_FLOP:
328 round = R_FLOP;
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++;
334 break;
336 case E_TURN:
338 round = R_TURN;
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++;
344 break;
346 case E_RIVER:
348 round = R_RIVER;
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++;
354 break;
356 case E_SHOWDOWN:
358 round = R_SHOWDOWN;
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++;
364 break;
366 case E_FOLD:
368 info->folded = true;
369 stats->folds++;
370 (*round_folds)++;
371 stats->actions++;
372 (*round_actions)++;
373 break;
375 case E_CHECK:
377 stats->checks++;
378 (*round_checks)++;
379 stats->actions++;
380 (*round_actions)++;
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;
383 break;
385 case E_CALL:
387 stats->calls++;
388 (*round_calls)++;
389 stats->actions++;
390 (*round_actions)++;
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;
396 break;
398 case E_RAISE:
400 int callAmount = highestBet - info->wager;
402 if(callAmount == 0) //bet
404 stats->bets++;
405 (*round_bets)++;
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;
409 else //raise
411 stats->raises++;
412 (*round_raises)++;
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;
417 stats->actions++;
418 (*round_actions)++;
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;
424 break;
426 case E_WIN:
428 stats->chips_won += event.chips;
429 info->stack += event.chips;
430 stats->wins_total++;
431 if(round == R_SHOWDOWN) stats->wins_showdown++;
432 else stats->wins_bluff++;
434 default: break;
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;
443 if(allin)
445 stats->allins++;
446 (*round_allins)++;
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
461 return &tableStats;