/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SAGE calculator // // // // application written by Fabio Ruini // // fabio.ruini@plymouth.ac.uk // // http://www.fabioruini.eu // // // // Based on the information found at: // // http://www.cardplayer.com/cardplayer-magazines/65582-19-2/articles/15250-are-you-sage-getting-an-edge-in-heads-up-no-limit-hold-39-em // // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include #include // Gloabal variables used char hole1, hole2, suited, userChoice; bool firstIteration, suitedBool, pocketPair, jam, call; float bigBlind, antes, smallestStack, R_index; int highestCardIndex, powerIndex; // Function that convert a specific picture or ace card (A, K, Q, J or T) into a numerical value int convertCardIntoValue(char holeCard) { // Declaration of a local convertedValue variable int convertedValue; // Convert the card to a numerical value switch (holeCard) { case 'A': convertedValue = 15; break; case 'K': convertedValue = 13; break; case 'Q': convertedValue = 12; break; case 'J': convertedValue = 11; break; case 'T': convertedValue = 10; break; default: convertedValue = atoi(&holeCard); break; } // Return the desired value to the calling function return convertedValue; } // Function that calculate the power index for a specific card int calculatePowerIndex(char holeCard) { // Declaration of a local powerIndex variable int powerIndexLocal; // Compuation of the power index for the current card powerIndexLocal = convertCardIntoValue(holeCard); // Return the desired value to the calling function return powerIndexLocal; } // Function that identify the highest card in the hole int identifyHighestCard() { // Declare the variable to be used within this function int highestCardIndex; // Check which is the highest ranked card if (convertCardIntoValue(hole1) > convertCardIntoValue(hole2)) highestCardIndex = 1; else highestCardIndex = 2; // Return the desired value to the calling function return highestCardIndex; } // Elaborate the move suggested by the SAGE system, according to R and PI void elaborateSuggestedMove() { jam = false; call = false; switch ((int)R_index) { case 0: jam = true; call = true; case 1: if (powerIndex >= 17) jam = true; call = true; break; case 2: if (powerIndex >= 21) jam = true; if (powerIndex >= 17) call = true; break; case 3: if (powerIndex >= 22) jam = true; if (powerIndex >= 24) call = true; break; case 4: if (powerIndex >= 23) jam = true; if (powerIndex >= 26) call = true; break; case 5: if (powerIndex >= 24) jam = true; if (powerIndex >= 28) call = true; break; case 6: if (powerIndex >= 25) jam = true; if (powerIndex >= 29) call = true; break; case 7: if (powerIndex >= 26) jam = true; if (powerIndex >= 30) call = true; break; case 8: // Estimated! if (powerIndex >= 27) jam = true; if (powerIndex >= 31) call = true; break; case 9: // Estimated! if (powerIndex >= 28) jam = true; if (powerIndex >= 32) call = true; break; } } // Display on the screen general information related to the current value of the R index void displayRInformation() { std::cout << "The ranges suggested by the SAGE system according to the current value of the R index are: " << std::endl << std::endl; switch ((int)R_index) { case 0: std::cout << "Pushing from the SB/BTN: top 100% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,82s+,72s+,62s+,52s+,42s+,32s,A2o+,K2o+,Q2o+,J2o+,T2o+,92o+,82o+,72o+,62o+,52o+,42o+,32o), minimum PI: 8" << std::endl; std::cout << "Calling a push from the BB: top 100% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,82s+,72s+,62s+,52s+,42s+,32s,A2o+,K2o+,Q2o+,J2o+,T2o+,92o+,82o+,72o+,62o+,52o+,42o+,32o), minimum PI: 8" << std::endl << std::endl; case 1: std::cout << "Pushing from the SB/BTN: top 89% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,82s+,72s+,62s+,52s+,42s+,32s,A2o+,K2o+,Q2o+,J2o+,T2o+,94o+,84o+,74o+,64o+,53o+), minimum PI: 17" << std::endl; std::cout << "Calling a push from the BB: top 100% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,82s+,72s+,62s+,52s+,42s+,32s,A2o+,K2o+,Q2o+,J2o+,T2o+,92o+,82o+,72o+,62o+,52o+,42o+,32o), minimum PI: 8" << std::endl << std::endl; break; case 2: std::cout << "Pushing from the SB/BTN: top 79% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,82s+,73s+,63s+,52s+,43s,A2o+,K2o+,Q2o+,J3o+,T5o+,95o+,85o+,75o+,65o), minimum PI: 21" << std::endl; std::cout << "Calling a push from the BB: top 89% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,82s+,72s+,62s+,52s+,42s+,32s,A2o+,K2o+,Q2o+,J2o+,T2o+,94o+,84o+,74o+,64o+,53o+), minimum PI: 17" << std::endl << std::endl; break; case 3: std::cout << "Pushing from the SB/BTN: top 74% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,92s+,83s+,73s+,63s+,53s+,43s,A2o+,K2o+,Q2o+,J4o+,T6o+,96o+,86o+,76o,65o), minimum PI: 22" << std::endl; std::cout << "Calling a push from the BB: top 70% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,93s+,84s+,74s+,63s+,53s+,43s,A2o+,K2o+,Q3o+,J5o+,T6o+,96o+,86o+,76o), , minimum PI: 24" << std::endl << std::endl; break; case 4: std::cout << "Pushing from the SB/BTN: top 71% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,93s+,84s+,74s+,63s+,53s+,43s,A2o+,K2o+,Q3o+,J5o+,T6o+,96o+,86o+,76o), minimum PI: 23" << std::endl; std::cout << "Calling a push from the BB: top 60% (22+,A2s+,K2s+,Q2s+,J2s+,T3s+,95s+,85s+,75s+,64s+,54s,A2o+,K3o+,Q5o+,J7o+,T7o+,97o+,87o), minimum PI: 26" << std::endl << std::endl; break; case 5: std::cout << "Pushing from the SB/BTN: top 68% (22+,A2s+,K2s+,Q2s+,J2s+,T3s+,95s+,85s+,75s+,64s+,54s,A2o+,K3o+,Q5o+,J7o+,T7o+,97o+,87o), minimum PI: 24" << std::endl; std::cout << "Calling a push from the BB: top 53% (33+,A2s+,K2s+,Q2s+,J4s+,T5s+,96s+,86s+,75s+,65s,A2o+,K4o+,Q6o+,J7o+,T7o+,98o), minimum PI: 28" << std::endl << std::endl; break; case 6: std::cout << "Pushing from the SB/BTN: top 64% (22+,A2s+,K2s+,Q2s+,J2s+,T2s+,95s+,84s+,74s+,64s+,54s,A2o+,K2o+,Q4o+,J6o+,T7o+,97o+,87o,76o), minimum PI: 25" << std::endl; std::cout << "Calling a push from the BB: top 48% (44+,A2s+,K2s+,Q2s+,J4s+,T6s+,96s+,86s+,76s,A2o+,K5o+,Q7o+,J8o+,T8o+,98o), minimum PI: 29" << std::endl << std::endl; break; case 7: std::cout << "Pushing from the SB/BTN: top 61% (22+,A2s+,K2s+,Q2s+,J2s+,T3s+,95s+,85s+,75s+,64s+,54s,A2o+,K3o+,Q5o+,J7o+,T7o+,97o+,87o), minimum PI: 26" << std::endl; std::cout << "Calling a push from the BB: top 42% (44+,A2s+,K2s+,Q4s+,J6s+,T7s+,97s+,87s,A3o+,K6o+,Q8o+,J8o+,T8o+), minimum PI: 30" << std::endl << std::endl; break; case 8: std::cout << "Pushing from the SB/BTN: top 58% (33+,A2s+,K2s+,Q2s+,J2s+,T4s+,95s+,85s+,75s+,65s,54s,A2o+,K3o+,Q6o+,J7o+,T7o+,97o+,87o), minimum PI: 27 (estimated PI)" << std::endl; std::cout << "Calling a push from the BB: top 39% (44+,A2s+,K2s+,Q5s+,J7s+,T7s+,97s+,87s,A3o+,K7o+,Q8o+,J9o+,T9o), minimum PI: 31 (estimated PI)" << std::endl << std::endl; break; case 9: std::cout << "Pushing from the SB/BTN: top 55% (33+,A2s+,K2s+,Q2s+,J3s+,T5s+,95s+,85s+,75s+,65s,A2o+,K4o+,Q6o+,J7o+,T7o+,98o,87o), minimum PI: 28 (estimated)" << std::endl; std::cout << "Calling a push from the BB: top 36% (55+,A2s+,K3s+,Q5s+,J7s+,T7s+,97s+,87s,A4o+,K7o+,Q9o+,J9o+,T9o), minimum PI: 32 (estimated)" << std::endl << std::endl; break; } } // Display information void displaySplashScreen() { if (firstIteration) { std::cout << "/////////////////////////////////////////////////////////////////////////" << std::endl; std::cout << "// SAGE (Sit And Go Endgame System) calculator //" << std::endl; std::cout << "// //" << std::endl; std::cout << "// Application written by Fabio Ruini (fabio.ruini@plymouth.ac.uk) //" << std::endl; std::cout << "// http://www.fabioruini.eu //" << std::endl; std::cout << "// //" << std::endl; std::cout << "// Based upon the information found at: //" << std::endl; std::cout << "// http://www.cardplayer.com/cardplayer-magazines/65582-19-2/articles/ //" << std::endl; std::cout << "// 15250-are-you-sage-getting-an-edge-in-heads-up-no-limit-hold-39-em //" << std::endl; std::cout << "/////////////////////////////////////////////////////////////////////////" << std::endl << std::endl; } std::cout << "Enter the details about the hand to be analysed: " << std::endl << std::endl; } // Main function int main (int argc, char * const argv[]) { firstIteration = true; do { // Display introductory information displaySplashScreen(); firstIteration = false; ///////////////////////////////////////////////// // Read the information about the current spot // ///////////////////////////////////////////////// // Big blind std::cout << "Big blind: "; std::cin >> bigBlind; // Antes std::cout << "Antes: "; std::cin >> antes; // Smallest stack size std::cout << "Smallest stack size (after posting BB and antes): "; std::cin >> smallestStack; // First hole card std::cout << "Hole card #1 [A|K|Q|J|T|9|8|7|6|5|4|3|2]: "; std::cin >> hole1; hole1 = toupper(hole1); // Second hole card std::cout << "Hole card #2 [A|K|Q|J|T|9|8|7|6|5|4|3|2]: "; std::cin >> hole2; hole2 = toupper(hole2); // Check whether the hand is a pocket pair or not if (hole1 == hole2) pocketPair = true; else pocketPair = false; // If the hand is not a pocket pair, ask the user whether the two hole cards are suited or not if (!pocketPair) { std::cout << "Suited [y|n]: "; std::cin >> suited; if ((suited == 'Y') || (suited == 'y')) suitedBool = true; else suitedBool = false; } ////////////////////////////////////////////////////////////// // Calculate the R index (smallest stack / big blind ratio) // ////////////////////////////////////////////////////////////// R_index = smallestStack / (bigBlind + antes); // not sure about how antes should be included in this formula! std::cout << "\nR index (smallest stack to big blind ratio): " << R_index << std::endl; // Approximate the R index to the closest integer if ((R_index + 0.5) >= (int(R_index) + 1)) R_index = int(R_index) + 1; else R_index = int(R_index); std::cout << "R index (smallest stack to big blind ratio) (rounded): " << R_index << std::endl << std::endl; /////////////////////////////////////////////// // Compute the power index (PI) for the hand // /////////////////////////////////////////////// // Identify which is the biggest card out of the two in the hole if (!pocketPair) highestCardIndex = identifyHighestCard(); else highestCardIndex = 1; // Calculate the power index for both the two cards held (doubling the value for the biggest one) if (highestCardIndex == 1) powerIndex = (calculatePowerIndex(hole1) * 2) + calculatePowerIndex(hole2); else powerIndex = (calculatePowerIndex(hole2) * 2) + calculatePowerIndex(hole1); // Add 'bonuses' if hero is holding a pocket pair or two suited cards if (pocketPair) powerIndex += 22; else if (suitedBool) powerIndex += 2; std::cout << "Current holding: "; if (highestCardIndex == 1) std::cout << hole1 << hole2; else std::cout << hole2 << hole1; if (suitedBool) std::cout << "s"; else std::cout << "o"; std::cout << "\nThe Power Index (PI) for the current holding is: " << powerIndex << std::endl << std::endl; ////////////////////////////////////////////////////////////////////////////// // Calculate the suggested action according to the SAGE system for R and PI // ////////////////////////////////////////////////////////////////////////////// if (R_index <= 9) { displayRInformation(); elaborateSuggestedMove(); if (jam) std::cout << "The suggested play from the SB/BTN is: PUSH" << std::endl; else std::cout << "The suggested play from the SB/BTN is: FOLD" << std::endl; if (call) std::cout << "The suggested play from the BB facing a push is: CALL" << std::endl << std::endl; else std::cout << "The suggested play from the BB facing a push is: FOLD" << std::endl << std::endl; if (R_index >= 7) std::cout << "Please beware that applying the SAGE system for values of R bigger than 6 leads to a slightly negative edge." << std::endl << std::endl; } else std::cout << "SAGE system not appliable on this specific spot" << std::endl << std::endl; // Press std::cout << "Press 'q' (followed by enter) to exit the software, or any other key (except for 'return') to analyse a new hand: "; std::cin >> userChoice; userChoice = toupper(userChoice); std::cout << std::endl << std::endl << std::endl; } while (userChoice != 'Q'); // Exit from the software return 0; }