| src/blackjack.cpp \ | src/blackjack.cpp \ | ||||
| src/cards.cpp \ | src/cards.cpp \ | ||||
| src/stdinout.cpp \ | src/stdinout.cpp \ | ||||
| src/tty.cpp | |||||
| src/tty.cpp \ | |||||
| src/internal.cpp |
| * handle no readline | * handle no readline | ||||
| * named pipes (a.k.a FIFOs) | |||||
| * eV = expected value = return | |||||
| * check that the card distribution is uniform | * check that the card distribution is uniform | ||||
| * initial bankroll | * initial bankroll | ||||
| * report | * report | ||||
| * as a file | * as a file | ||||
| * format (yaml, json, markdown table) | * format (yaml, json, markdown table) | ||||
| * size (extra small, small, medium, large, extra large) | |||||
| * flat_bet implies =1 | |||||
| * parse 1e5 in hands | |||||
| * verbosity (extra small, small, medium, large, extra large) | |||||
| * trap ctrl+c and write report anyway (not sure how to pass arguments to signal handler) | * trap ctrl+c and write report anyway (not sure how to pass arguments to signal handler) | ||||
| * flag to see if a conf string was used or not | * flag to see if a conf string was used or not | ||||
| * to_string() for floats | * to_string() for floats |
| }; | }; | ||||
| struct reportItem { | struct reportItem { | ||||
| reportItem(std::string k, std::string f, double v) : key(k), format(f), value(v) {}; | |||||
| reportItem(int l, std::string k, std::string f, double v) : key(k), format(f), value(v), level(l) {}; | |||||
| std::string key; | std::string key; | ||||
| std::string format; | std::string format; | ||||
| double value; | double value; | ||||
| int level; | |||||
| }; | }; | ||||
| class Dealer { | class Dealer { | ||||
| void reportPrepare(void); | void reportPrepare(void); | ||||
| int writeReportYAML(void); | int writeReportYAML(void); | ||||
| protected: | protected: | ||||
| // TODO: multiple players | // TODO: multiple players | ||||
| Player *player; | Player *player; | ||||
| // std::list <Hand> hands; | // std::list <Hand> hands; | ||||
| Hand hand; | Hand hand; | ||||
| double error_standard_deviations = 1.0; | |||||
| int n_decks = -1; | |||||
| unsigned long int n_hands = 0; | |||||
| // how many standard deviations does the reported error mean? | |||||
| double error_standard_deviations = 3.0; | |||||
| // default infinite number of decks (it's faster) | |||||
| int n_decks = -1; | |||||
| // default one million hands | |||||
| unsigned long int n_hands = 1000000; | |||||
| unsigned long int n_hand = 0; | unsigned long int n_hand = 0; | ||||
| struct { | struct { | ||||
| std::list<PlayerHand>::iterator currentHand; | std::list<PlayerHand>::iterator currentHand; | ||||
| unsigned int splits = 0; | unsigned int splits = 0; | ||||
| // unsigned int currentBet = 0; | |||||
| // TODO: separate handsDealt from handsPlayed | |||||
| unsigned int n_hands = 0; // this is different from the dealer's due to splitting | unsigned int n_hands = 0; // this is different from the dealer's due to splitting | ||||
| unsigned int handsInsured = 0; | unsigned int handsInsured = 0; | ||||
| unsigned int pushes = 0; | unsigned int pushes = 0; | ||||
| unsigned int losses = 0; | unsigned int losses = 0; | ||||
| // TODO: blackjack_pushes? | |||||
| // TODO: blackjack_pushes? | |||||
| double bankroll = 0; | double bankroll = 0; | ||||
| double worstBankroll = 0; | double worstBankroll = 0; | ||||
| double totalMoneyWaged = 0; | double totalMoneyWaged = 0; | ||||
| double result = 0; | |||||
| // these variables are used to compute the running mean and variance | |||||
| double currentOutcome = 0; | |||||
| double mean = 0; | double mean = 0; | ||||
| double M2 = 0; | double M2 = 0; | ||||
| double variance = 0; | double variance = 0; | ||||
| } playerStats; | } playerStats; | ||||
| void updateMeanAndVariance(void); | |||||
| private: | private: | ||||
| bool done = false; | bool done = false; | ||||
| std::list<reportItem> report; | std::list<reportItem> report; | ||||
| int reportVerbosity = 3; | |||||
| }; | }; | ||||
| template <typename ... Args> std::string string_format( const std::string& format, Args ... args); | |||||
| #endif | #endif |
| return; | return; | ||||
| } | } | ||||
| // update the uncertainty (knuth citing welford) | |||||
| // The Art of Computer Programming, volume 2: Seminumerical Algorithms, 3rd edn., p. 232 | |||||
| // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm | |||||
| if (n_hand != 0) { | if (n_hand != 0) { | ||||
| double delta = playerStats.result - playerStats.mean; | |||||
| playerStats.mean += delta / (double)(n_hand); | |||||
| playerStats.M2 += delta * (playerStats.result - playerStats.mean); | |||||
| playerStats.variance = playerStats.M2 / (double)(n_hand); | |||||
| updateMeanAndVariance(); | |||||
| } | } | ||||
| i_arranged_cards = 0; | i_arranged_cards = 0; | ||||
| playerStats.currentOutcome = 0; | |||||
| n_hand++; | n_hand++; | ||||
| // clear dealer's hand | // clear dealer's hand | ||||
| // pay him (her) | // pay him (her) | ||||
| playerStats.bankroll += (1.0 + 0.5) * playerStats.currentHand->bet; | playerStats.bankroll += (1.0 + 0.5) * playerStats.currentHand->bet; | ||||
| playerStats.result += playerStats.currentHand->bet; | |||||
| playerStats.currentOutcome += playerStats.currentHand->bet; | |||||
| info(Libreblackjack::Info::PlayerWinsInsurance, 1e3*playerStats.currentHand->bet); | info(Libreblackjack::Info::PlayerWinsInsurance, 1e3*playerStats.currentHand->bet); | ||||
| playerStats.winsInsured++; | playerStats.winsInsured++; | ||||
| } else { | } else { | ||||
| playerStats.result -= playerStats.currentHand->bet; | |||||
| playerStats.currentOutcome -= playerStats.currentHand->bet; | |||||
| info(Libreblackjack::Info::PlayerLosses, 1e3*playerStats.currentHand->bet); | info(Libreblackjack::Info::PlayerLosses, 1e3*playerStats.currentHand->bet); | ||||
| playerStats.losses++; | playerStats.losses++; | ||||
| // pay him (her) | // pay him (her) | ||||
| playerStats.bankroll += (1.0 + blackjack_pays) * playerStats.currentHand->bet; | playerStats.bankroll += (1.0 + blackjack_pays) * playerStats.currentHand->bet; | ||||
| playerStats.result += blackjack_pays * playerStats.currentHand->bet; | |||||
| playerStats.currentOutcome += blackjack_pays * playerStats.currentHand->bet; | |||||
| info(Libreblackjack::Info::PlayerWins, 1e3 * blackjack_pays*playerStats.currentHand->bet); | info(Libreblackjack::Info::PlayerWins, 1e3 * blackjack_pays*playerStats.currentHand->bet); | ||||
| playerStats.blackjacksPlayer++; | playerStats.blackjacksPlayer++; | ||||
| if (playerHand.busted() == false) { | if (playerHand.busted() == false) { | ||||
| // pay him (her) | // pay him (her) | ||||
| playerStats.bankroll += 2 * playerHand.bet; | playerStats.bankroll += 2 * playerHand.bet; | ||||
| playerStats.result += playerHand.bet; | |||||
| playerStats.currentOutcome += playerHand.bet; | |||||
| info(Libreblackjack::Info::PlayerWins, 1e3*playerHand.bet); | info(Libreblackjack::Info::PlayerWins, 1e3*playerHand.bet); | ||||
| playerStats.wins++; | playerStats.wins++; | ||||
| if (std::abs(player->dealerValue) > std::abs(player->playerValue)) { | if (std::abs(player->dealerValue) > std::abs(player->playerValue)) { | ||||
| playerStats.result -= playerHand.bet; | |||||
| playerStats.currentOutcome -= playerHand.bet; | |||||
| info(Libreblackjack::Info::PlayerLosses, 1e3*playerHand.bet, player->playerValue); | info(Libreblackjack::Info::PlayerLosses, 1e3*playerHand.bet, player->playerValue); | ||||
| playerStats.losses++; | playerStats.losses++; | ||||
| // pay him (her) | // pay him (her) | ||||
| playerStats.bankroll += 2 * playerHand.bet; | playerStats.bankroll += 2 * playerHand.bet; | ||||
| playerStats.result += playerHand.bet; | |||||
| playerStats.currentOutcome += playerHand.bet; | |||||
| info(Libreblackjack::Info::PlayerWins, 1e3*playerHand.bet, player->playerValue); | info(Libreblackjack::Info::PlayerWins, 1e3*playerHand.bet, player->playerValue); | ||||
| playerStats.wins++; | playerStats.wins++; | ||||
| playerStats.winsDoubled += playerHand.doubled; | playerStats.winsDoubled += playerHand.doubled; | ||||
| if (playerStats.bankroll < playerStats.worstBankroll) { | if (playerStats.bankroll < playerStats.worstBankroll) { | ||||
| playerStats.worstBankroll = playerStats.bankroll; | playerStats.worstBankroll = playerStats.bankroll; | ||||
| } | } | ||||
| playerStats.totalMoneyWaged += playerStats.currentHand->bet; | |||||
| nextAction = Libreblackjack::DealerAction::DealPlayerFirstCard; | nextAction = Libreblackjack::DealerAction::DealPlayerFirstCard; | ||||
| return 1; | return 1; | ||||
| if (playerStats.currentHand->busted()) { | if (playerStats.currentHand->busted()) { | ||||
| info(Libreblackjack::Info::PlayerLosses, 1e3*playerStats.currentHand->bet, player->playerValue); | info(Libreblackjack::Info::PlayerLosses, 1e3*playerStats.currentHand->bet, player->playerValue); | ||||
| playerStats.result -= playerStats.currentHand->bet; | |||||
| playerStats.currentOutcome -= playerStats.currentHand->bet; | |||||
| playerStats.bustsPlayer++; | playerStats.bustsPlayer++; | ||||
| playerStats.losses++; | playerStats.losses++; | ||||
| } | } | ||||
| if (playerStats.currentHand->busted()) { | if (playerStats.currentHand->busted()) { | ||||
| playerStats.result -= playerStats.currentHand->bet; | |||||
| playerStats.currentOutcome -= playerStats.currentHand->bet; | |||||
| info(Libreblackjack::Info::PlayerLosses, 1e3*playerStats.currentHand->bet); | info(Libreblackjack::Info::PlayerLosses, 1e3*playerStats.currentHand->bet); | ||||
| playerStats.bustsPlayer++; | playerStats.bustsPlayer++; | ||||
| playerStats.losses++; | playerStats.losses++; |
| break; | break; | ||||
| case 'f': | case 'f': | ||||
| if (optarg != NULL) { | if (optarg != NULL) { | ||||
| data["flat_bet"] = optarg; | |||||
| data["flat_bet"] = optarg; | |||||
| } else { | } else { | ||||
| data["flat_bet"] = "yes"; | data["flat_bet"] = "yes"; | ||||
| } | } | ||||
| set(&error_standard_deviations, {"error_standard_deviations"}); | set(&error_standard_deviations, {"error_standard_deviations"}); | ||||
| set(yaml_report_path, {"yaml_report", "yaml_report_path"}); | set(yaml_report_path, {"yaml_report", "yaml_report_path"}); | ||||
| return; | |||||
| } | } | ||||
| bool Configuration::set(unsigned long int *value, std::list<std::string> key) { | bool Configuration::set(unsigned long int *value, std::list<std::string> key) { | ||||
| for (auto it : key) { | for (auto it : key) { | ||||
| if (exists(*(&it))) { | if (exists(*(&it))) { | ||||
| *value = std::stoi(data[*(&it)]); | |||||
| *value = (unsigned long int)std::stod(data[*(&it)]); | |||||
| return true; | return true; | ||||
| } | } | ||||
| } | } |
| /*------------ -------------- -------- --- ----- --- -- - - | |||||
| * Libre Blackjack - tty interactive player | |||||
| * | |||||
| * Copyright (C) 2020 jeremy theler | |||||
| * | |||||
| * This file is part of Libre Blackjack. | |||||
| * | |||||
| * Libre Blackjack is free software: you can redistribute it and/or modify | |||||
| * it under the terms of the GNU General Public License as published by | |||||
| * the Free Software Foundation, either version 3 of the License, or | |||||
| * (at your option) any later version. | |||||
| * | |||||
| * Libre Blackjack is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with Libre Blackjack. If not, see <http://www.gnu.org/licenses/>. | |||||
| *------------------- ------------ ---- -------- -- - - - | |||||
| */ | |||||
| #include "conf.h" | |||||
| #include "blackjack.h" | |||||
| #include "internal.h" | |||||
| Internal::Internal(Configuration &conf) { | |||||
| return; | |||||
| } | |||||
| void Internal::info(Libreblackjack::Info msg, int p1, int p2) { | |||||
| return; | |||||
| } | |||||
| int Internal::play() { | |||||
| switch (actionRequired) { | |||||
| case Libreblackjack::PlayerActionRequired::Bet: | |||||
| currentBet = 1; | |||||
| actionTaken = Libreblackjack::PlayerActionTaken::Bet; | |||||
| break; | |||||
| case Libreblackjack::PlayerActionRequired::Insurance: | |||||
| actionTaken = Libreblackjack::PlayerActionTaken::DontInsure; | |||||
| break; | |||||
| case Libreblackjack::PlayerActionRequired::Play: | |||||
| actionTaken = (playerValue < 12) ? Libreblackjack::PlayerActionTaken::Hit : Libreblackjack::PlayerActionTaken::Stand; | |||||
| break; | |||||
| case Libreblackjack::PlayerActionRequired::None: | |||||
| break; | |||||
| } | |||||
| return 0; | |||||
| } |
| /*------------ -------------- -------- --- ----- --- -- - - | |||||
| * Libre Blackjack - internal automatic player | |||||
| * | |||||
| * Copyright (C) 2020 jeremy theler | |||||
| * | |||||
| * This file is part of Libre Blackjack. | |||||
| * | |||||
| * Libre Blackjack is free software: you can redistribute it and/or modify | |||||
| * it under the terms of the GNU General Public License as published by | |||||
| * the Free Software Foundation, either version 3 of the License, or | |||||
| * (at your option) any later version. | |||||
| * | |||||
| * Libre Blackjack is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with Libre Blackjack. If not, see <http://www.gnu.org/licenses/>. | |||||
| *------------------- ------------ ---- -------- -- - - - | |||||
| */ | |||||
| #ifndef INTERNAL_H | |||||
| #define INTERNAL_H | |||||
| #include "blackjack.h" | |||||
| class Internal : public Player { | |||||
| public: | |||||
| Internal(Configuration &); | |||||
| ~Internal() { }; | |||||
| int play(void) override; | |||||
| void info(Libreblackjack::Info = Libreblackjack::Info::None, int = 0, int = 0) override; | |||||
| private: | |||||
| }; | |||||
| #endif |
| #include "blackjack.h" | #include "blackjack.h" | ||||
| #include "tty.h" | #include "tty.h" | ||||
| #include "stdinout.h" | #include "stdinout.h" | ||||
| #include "internal.h" | |||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
| player = new Tty(conf); | player = new Tty(conf); | ||||
| } else if (conf.getPlayerName() == "stdinout" || conf.getPlayerName() == "stdio") { | } else if (conf.getPlayerName() == "stdinout" || conf.getPlayerName() == "stdio") { | ||||
| player = new StdInOut(conf); | player = new StdInOut(conf); | ||||
| // TODO: player strategy from file | |||||
| } else if (conf.getPlayerName() == "internal") { | |||||
| player = new Internal(conf); | |||||
| } else { | } else { | ||||
| std::cerr << "Unknown player '" << conf.getPlayerName() <<".'" << std::endl; | std::cerr << "Unknown player '" << conf.getPlayerName() <<".'" << std::endl; | ||||
| return -1; | return -1; |
| #include <iostream> | #include <iostream> | ||||
| // #include <sys/time.h> | |||||
| // #include <sys/resource.h> | |||||
| #include <cmath> | |||||
| #include <memory> | |||||
| #include <string> | |||||
| #include "base.h" | #include "base.h" | ||||
| // https://stackoverflow.com/questions/2342162/stdstring-formatting-like-sprintf | |||||
| template<typename ... Args> | |||||
| std::string string_format( const std::string& format, Args ... args) | |||||
| { | |||||
| size_t size = snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0' | |||||
| if (size <= 0) { | |||||
| return std::string(""); | |||||
| } | |||||
| std::unique_ptr<char[]> buf(new char[size]); | |||||
| snprintf(buf.get(), size, format.c_str(), args ...); | |||||
| return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside | |||||
| } | |||||
| void Dealer::updateMeanAndVariance(void) { | |||||
| double delta = playerStats.currentOutcome - playerStats.mean; | |||||
| playerStats.mean += delta / (double)(n_hand); | |||||
| playerStats.M2 += delta * (playerStats.currentOutcome - playerStats.mean); | |||||
| playerStats.variance = playerStats.M2 / (double)(n_hand); | |||||
| return; | |||||
| } | |||||
| void Dealer::reportPrepare(void) { | void Dealer::reportPrepare(void) { | ||||
| double ev = (double) playerStats.result / (double) n_hand; | |||||
| double error = error_standard_deviations * sqrt (playerStats.variance / (double) n_hand); | |||||
| report.push_back(reportItem("bankroll", "%g", playerStats.bankroll)); | |||||
| report.push_back(reportItem("result", "%g", playerStats.result)); | |||||
| report.push_back(reportItem("ev", "%g", ev)); | |||||
| report.push_back(reportItem("hands", "%g", n_hand)); | |||||
| report.push_back(reportItem("variance", "%g", playerStats.variance)); | |||||
| report.push_back(reportItem("deviation", "%g", sqrt(playerStats.variance))); | |||||
| report.push_back(reportItem("error", "%g", error)); | |||||
| // we need to update these statistics after the last played hand | |||||
| updateMeanAndVariance(); | |||||
| report.push_back(reportItem("played_hands", "%g", n_hands)); | |||||
| report.push_back(reportItem("total_money_waged", "%g", playerStats.totalMoneyWaged)); | |||||
| report.push_back(reportItem("bustsPlayer", "%g", playerStats.bustsPlayer)); | |||||
| report.push_back(reportItem("bustsDealer", "%g", playerStats.bustsDealer)); | |||||
| double error = error_standard_deviations * sqrt (playerStats.variance / (double) n_hand); | |||||
| report.push_back(reportItem("wins", "%g", playerStats.wins)); | |||||
| report.push_back(reportItem("pushes", "%g", playerStats.pushes)); | |||||
| report.push_back(reportItem("losses", "%g", playerStats.losses)); | |||||
| int precision = (int) (std::ceil(-std::log10(error))) - 2; | |||||
| if (precision < 0) { | |||||
| precision = 0; | |||||
| } | |||||
| std::string format = "\"(%+." + std::to_string(precision) + "f ± %." + std::to_string(precision) + "f) %%%%\""; | |||||
| report.push_back(reportItem(1, "result", string_format(format, 100*playerStats.mean, 100*error), 0.0)); | |||||
| report.push_back(reportItem(2, "mean", "%g", playerStats.mean)); | |||||
| report.push_back(reportItem(2, "error", "%g", error)); | |||||
| report.push_back(reportItem(2, "hands", "%g", n_hand)); | |||||
| report.push_back(reportItem(2, "bankroll", "%g", playerStats.bankroll)); | |||||
| report.push_back(reportItem(3, "bustsPlayer", "%g", playerStats.bustsPlayer / (double) n_hand)); | |||||
| report.push_back(reportItem(3, "bustsDealer", "%g", playerStats.bustsDealer / (double) n_hand)); | |||||
| report.push_back(reportItem(3, "wins", "%g", playerStats.wins / (double) n_hand)); | |||||
| report.push_back(reportItem(3, "pushes", "%g", playerStats.pushes / (double) n_hand)); | |||||
| report.push_back(reportItem(3, "losses", "%g", playerStats.losses / (double) n_hand)); | |||||
| report.push_back(reportItem(4, "total_money_waged", "%g", playerStats.totalMoneyWaged)); | |||||
| report.push_back(reportItem(5, "variance", "%g", playerStats.variance)); | |||||
| report.push_back(reportItem(5, "deviation", "%g", sqrt(playerStats.variance))); | |||||
| return; | return; | ||||
| } | } | ||||
| // } | // } | ||||
| // TODO: choose if comments with explanations are to be added | // TODO: choose if comments with explanations are to be added | ||||
| // TODO: choose verbosity level | |||||
| std::cerr << "---" << std::endl; | std::cerr << "---" << std::endl; | ||||
| for (auto item : report) { | for (auto item : report) { | ||||
| std::cerr << item.key << ": "; | |||||
| fprintf(stderr, item.format.c_str(), item.value); | |||||
| std::cerr << std::endl; | |||||
| if (item.level <= reportVerbosity) { | |||||
| std::cerr << item.key << ": "; | |||||
| fprintf(stderr, item.format.c_str(), item.value); | |||||
| std::cerr << std::endl; | |||||
| } | |||||
| } | } | ||||
| // std::cerr << "rules:" << std::endl; | // std::cerr << "rules:" << std::endl; |
| */ | */ | ||||
| #include <iostream> | #include <iostream> | ||||
| #include <cstring> | |||||
| #include <thread> | #include <thread> | ||||
| #include <chrono> | #include <chrono> | ||||
| break; | break; | ||||
| case Libreblackjack::Info::NewHand: | case Libreblackjack::Info::NewHand: | ||||
| // s = "new_hand"; | |||||
| std::cout << std::endl; | std::cout << std::endl; | ||||
| s = "Starting new hand #" + std::to_string(p1) + " with bankroll " + std::to_string(1e-3*p2); | |||||
| s = "Starting new hand #" + std::to_string(p1) + " with bankroll " + string_format("%g", 1e-3*p2, 0.0); | |||||
| // clear dealer's hand | // clear dealer's hand | ||||
| dealerHand.cards.clear(); | dealerHand.cards.clear(); | ||||
| s = "Dealer's up card is " + card[p1].utf8(); | s = "Dealer's up card is " + card[p1].utf8(); | ||||
| break; | break; | ||||
| default: | default: | ||||
| // s = "card_dealer"; | |||||
| s = "Dealer's card is " + card[p1].utf8(); | s = "Dealer's card is " + card[p1].utf8(); | ||||
| break; | break; | ||||
| } | } | ||||
| break; | break; | ||||
| case Libreblackjack::Info::PlayerDoubleInvalid: | case Libreblackjack::Info::PlayerDoubleInvalid: | ||||
| // s = "player_double_invalid"; | |||||
| s = "Cannot double down"; | s = "Cannot double down"; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::PlayerNextHand: | case Libreblackjack::Info::PlayerNextHand: | ||||
| // s = "player_next_hand"; | |||||
| s = "Playing next hand #" + std::to_string(p1); | s = "Playing next hand #" + std::to_string(p1); | ||||
| render = true; | render = true; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::PlayerPushes: | case Libreblackjack::Info::PlayerPushes: | ||||
| // s = "player_pushes"; | |||||
| s = "Player pushes " + std::to_string(1e-3*p1) + ((p2 > 0) ? (" with " + std::to_string(p2)) : ""); | |||||
| s = "Player pushes " + string_format("%g", 1e-3*p1, 0.0) + ((p2 > 0) ? (" with " + std::to_string(p2)) : ""); | |||||
| render = true; | render = true; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::PlayerLosses: | case Libreblackjack::Info::PlayerLosses: | ||||
| // s = "player_losses"; | |||||
| s = "Player losses " + std::to_string(1e-3*p1) + ((p2 > 0) ? (" with " + std::to_string(p2)) : ""); | |||||
| s = "Player losses " + string_format("%g", 1e-3*p1, 0.0) + ((p2 > 0) ? (" with " + std::to_string(p2)) : ""); | |||||
| render = true; | render = true; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::PlayerBlackjack: | case Libreblackjack::Info::PlayerBlackjack: | ||||
| // s = "blackjack_player"; | |||||
| s = "Player has Blackjack"; | s = "Player has Blackjack"; | ||||
| render = true; | render = true; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::PlayerWins: | case Libreblackjack::Info::PlayerWins: | ||||
| // s = "player_wins"; | |||||
| s = "Player wins " + std::to_string(1e-3*p1) + ((p2 > 0) ? (" with " + std::to_string(p2)) : ""); | |||||
| s = "Player wins " + string_format("%g", 1e-3*p1, 0.0) + ((p2 > 0) ? (" with " + std::to_string(p2)) : ""); | |||||
| render = true; | render = true; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::NoBlackjacks: | case Libreblackjack::Info::NoBlackjacks: | ||||
| // s = "no_blackjacks"; | |||||
| s = "No blackjacks"; | s = "No blackjacks"; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::DealerBusts: | case Libreblackjack::Info::DealerBusts: | ||||
| // s = "no_blackjacks"; | |||||
| s = "Dealer busts with " + std::to_string(p1); | s = "Dealer busts with " + std::to_string(p1); | ||||
| break; | break; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::Bankroll: | case Libreblackjack::Info::Bankroll: | ||||
| std::cout << "Your bankroll is " << std::to_string(1e-3*p1) << std::endl; | |||||
| std::cout << "Your bankroll is " << string_format("%g", 1e-3*p1, 0.0) << std::endl; | |||||
| break; | break; | ||||
| case Libreblackjack::Info::CommandInvalid: | case Libreblackjack::Info::CommandInvalid: | ||||
| // s = "command_invalid"; | |||||
| s = "Invalid command"; | s = "Invalid command"; | ||||
| break; | break; | ||||
| case Libreblackjack::Info::Bye: | case Libreblackjack::Info::Bye: | ||||
| // s = "bye"; | |||||
| s = "Bye bye! We'll play Blackjack again next time"; | s = "Bye bye! We'll play Blackjack again next time"; | ||||
| break; | break; | ||||