| @@ -1,4 +1,5 @@ | |||
| flat_bet = 1 | |||
| # flat_bet = 1 | |||
| # max_bet = 2 | |||
| arranged_cards = 2,4,11,1,7 | |||
| arranged_cards = 9,8,13,12 | |||
| delay = 0 | |||
| @@ -86,6 +86,7 @@ enum class Info { | |||
| DealerBlackjack, | |||
| PlayerWinsInsurance, | |||
| PlayerBlackjackAlso, | |||
| PlayerNextHand, | |||
| PlayerPushes, | |||
| PlayerLosses, | |||
| PlayerBlackjack, | |||
| @@ -140,8 +141,8 @@ class Card { | |||
| std::string singleUTF8; | |||
| }; | |||
| // TODO: static inside a class | |||
| extern Card card[52]; | |||
| // TODO: class static? which class? | |||
| extern Card card[53]; | |||
| class Hand { | |||
| public: | |||
| @@ -226,7 +227,7 @@ class Player { | |||
| unsigned int losses = 0; | |||
| // TODO: blackjack_pushes? | |||
| unsigned int flat_bet = 1; | |||
| unsigned int flat_bet = 0; | |||
| bool no_insurance = false; | |||
| bool always_insure = false; | |||
| @@ -260,16 +261,7 @@ class Dealer { | |||
| virtual void deal(Player *) = 0; | |||
| virtual unsigned int drawCard(Hand * = nullptr) = 0; | |||
| virtual int process(Player *) = 0; | |||
| /* | |||
| void setNextAction(DealerAction a) { | |||
| next_action = a; | |||
| } | |||
| void getNextAction(DealerAction a) { | |||
| next_action = a; | |||
| } | |||
| */ | |||
| bool finished(void) { | |||
| return done; | |||
| } | |||
| @@ -36,7 +36,7 @@ | |||
| #include "blackjack.h" | |||
| Blackjack::Blackjack(Configuration &conf) : rng(dev_random()), fiftyTwoCards(0, 51) { | |||
| Blackjack::Blackjack(Configuration &conf) : rng(dev_random()), fiftyTwoCards(1, 52) { | |||
| conf.set(&n_hands, {"n_hands", "hands"}); | |||
| conf.set(&n_decks, {"decks", "n_decks"}); | |||
| @@ -120,7 +120,6 @@ void Blackjack::deal(Player *player) { | |||
| // state that the player did not win anything nor splitted nor doubled down | |||
| player->current_result = 0; | |||
| player->currentSplits = 0; | |||
| // player->hasDoubled = 0; | |||
| if (lastPass) { | |||
| player->info(Info::Shuffle); | |||
| @@ -137,9 +136,12 @@ void Blackjack::deal(Player *player) { | |||
| if (player->flat_bet) { | |||
| player->currentHand->bet = player->flat_bet; | |||
| player->actionRequired = PlayerActionRequired::None; | |||
| nextAction = DealerAction::DealPlayerFirstCard; | |||
| } else { | |||
| player->actionRequired = PlayerActionRequired::Bet; | |||
| nextAction = DealerAction::AskForBets; | |||
| } | |||
| player->info(Info::NewHand, player->bankroll); | |||
| @@ -582,14 +584,15 @@ int Blackjack::process(Player *player) { | |||
| // deal a card to the first hand | |||
| playerCard = drawCard(&(*player->currentHand)); | |||
| std::cout << "card_player_second " << card[playerCard].utf8() << std::endl; | |||
| player->info(Info::CardPlayer, playerCard); | |||
| // aces get dealt only one card | |||
| // also, if the player gets 21 then we move on to the next hand | |||
| if (card[*player->currentHand->cards.begin()].value == 11 || std::abs(player->currentHand->total()) == 21) { | |||
| if (++player->currentHand != player->hands.end()) { | |||
| player->info(Info::PlayerNextHand, (*player->currentHand).id); | |||
| playerCard = drawCard(&(*player->currentHand)); | |||
| std::cout << "card_player_second " << card[playerCard].utf8() << std::endl; | |||
| player->info(Info::CardPlayer, playerCard); | |||
| // if the player got an ace or 21 again, we are done | |||
| if (card[*player->currentHand->cards.begin()].value == 11 || std::abs(player->currentHand->total()) == 21) { | |||
| @@ -684,7 +687,7 @@ unsigned int Blackjack::drawCard(Hand *hand) { | |||
| if (n_decks == -1) { | |||
| if (n_arranged_cards != 0 && i_arranged_cards < n_arranged_cards) { | |||
| // negative (or invalid) values are placeholder for random cards | |||
| if ((tag = arranged_cards[i_arranged_cards++]) < 0 || tag > 51) { | |||
| if ((tag = arranged_cards[i_arranged_cards++]) <= 0 || tag > 52) { | |||
| tag = fiftyTwoCards(rng); | |||
| } | |||
| } else { | |||
| @@ -26,8 +26,13 @@ static std::string TJQK[4] = {"T", "J", "Q", "K"}; | |||
| static std::string numbers[14] = {"", "ace", "deuce", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "jack", "queen", "king"}; | |||
| Card::Card(unsigned int tag) { | |||
| number = 1 + (tag % 13); | |||
| suit = static_cast<Suit>(tag/13); | |||
| if (tag <= 0 || tag > 52) { | |||
| return; | |||
| } | |||
| number = 1 + ((tag-1) % 13); | |||
| suit = static_cast<Suit>((tag-1)/13); | |||
| value = (number == 1) ? 11 : ((number > 10) ? 10 : number); | |||
| numberASCII = (number == 1) ? "A" : ((number < 11) ? std::to_string(number) : TJQK[number-10]); | |||
| @@ -60,61 +65,61 @@ Card::Card(unsigned int tag) { | |||
| // it would be hacky but hard to understand for humans | |||
| // UNIX rule of representation! | |||
| switch(tag) { | |||
| case 0: singleUTF8 = "🃑"; break; | |||
| case 1: singleUTF8 = "🃒"; break; | |||
| case 2: singleUTF8 = "🃓"; break; | |||
| case 3: singleUTF8 = "🃔"; break; | |||
| case 4: singleUTF8 = "🃕"; break; | |||
| case 5: singleUTF8 = "🃖"; break; | |||
| case 6: singleUTF8 = "🃗"; break; | |||
| case 7: singleUTF8 = "🃘"; break; | |||
| case 8: singleUTF8 = "🃙"; break; | |||
| case 9: singleUTF8 = "🃚"; break; | |||
| case 10: singleUTF8 = "🃛"; break; | |||
| case 11: singleUTF8 = "🃝"; break; | |||
| case 12: singleUTF8 = "🃞"; break; | |||
| case 1: singleUTF8 = "🃑"; break; | |||
| case 2: singleUTF8 = "🃒"; break; | |||
| case 3: singleUTF8 = "🃓"; break; | |||
| case 4: singleUTF8 = "🃔"; break; | |||
| case 5: singleUTF8 = "🃕"; break; | |||
| case 6: singleUTF8 = "🃖"; break; | |||
| case 7: singleUTF8 = "🃗"; break; | |||
| case 8: singleUTF8 = "🃘"; break; | |||
| case 9: singleUTF8 = "🃙"; break; | |||
| case 10: singleUTF8 = "🃚"; break; | |||
| case 11: singleUTF8 = "🃛"; break; | |||
| case 12: singleUTF8 = "🃝"; break; | |||
| case 13: singleUTF8 = "🃞"; break; | |||
| case 13: singleUTF8 = "🃁"; break; | |||
| case 14: singleUTF8 = "🃂"; break; | |||
| case 15: singleUTF8 = "🃃"; break; | |||
| case 16: singleUTF8 = "🃄"; break; | |||
| case 17: singleUTF8 = "🃅"; break; | |||
| case 18: singleUTF8 = "🃆"; break; | |||
| case 19: singleUTF8 = "🃇"; break; | |||
| case 20: singleUTF8 = "🃈"; break; | |||
| case 21: singleUTF8 = "🃉"; break; | |||
| case 22: singleUTF8 = "🃊"; break; | |||
| case 23: singleUTF8 = "🃋"; break; | |||
| case 24: singleUTF8 = "🃍"; break; | |||
| case 14: singleUTF8 = "🃁"; break; | |||
| case 15: singleUTF8 = "🃂"; break; | |||
| case 16: singleUTF8 = "🃃"; break; | |||
| case 17: singleUTF8 = "🃄"; break; | |||
| case 18: singleUTF8 = "🃅"; break; | |||
| case 19: singleUTF8 = "🃆"; break; | |||
| case 20: singleUTF8 = "🃇"; break; | |||
| case 21: singleUTF8 = "🃈"; break; | |||
| case 22: singleUTF8 = "🃉"; break; | |||
| case 23: singleUTF8 = "🃊"; break; | |||
| case 24: singleUTF8 = "🃋"; break; | |||
| case 25: singleUTF8 = "🃍"; break; | |||
| case 26: singleUTF8 = "🃍"; break; | |||
| case 26: singleUTF8 = "🂱"; break; | |||
| case 27: singleUTF8 = "🂲"; break; | |||
| case 28: singleUTF8 = "🂳"; break; | |||
| case 29: singleUTF8 = "🂴"; break; | |||
| case 30: singleUTF8 = "🂵"; break; | |||
| case 31: singleUTF8 = "🂶"; break; | |||
| case 32: singleUTF8 = "🂷"; break; | |||
| case 33: singleUTF8 = "🂸"; break; | |||
| case 34: singleUTF8 = "🂹"; break; | |||
| case 35: singleUTF8 = "🂺"; break; | |||
| case 36: singleUTF8 = "🂻"; break; | |||
| case 37: singleUTF8 = "🂽"; break; | |||
| case 38: singleUTF8 = "🂾"; break; | |||
| case 27: singleUTF8 = "🂱"; break; | |||
| case 28: singleUTF8 = "🂲"; break; | |||
| case 29: singleUTF8 = "🂳"; break; | |||
| case 30: singleUTF8 = "🂴"; break; | |||
| case 31: singleUTF8 = "🂵"; break; | |||
| case 32: singleUTF8 = "🂶"; break; | |||
| case 33: singleUTF8 = "🂷"; break; | |||
| case 34: singleUTF8 = "🂸"; break; | |||
| case 35: singleUTF8 = "🂹"; break; | |||
| case 36: singleUTF8 = "🂺"; break; | |||
| case 37: singleUTF8 = "🂻"; break; | |||
| case 38: singleUTF8 = "🂽"; break; | |||
| case 39: singleUTF8 = "🂾"; break; | |||
| case 39: singleUTF8 = "🂡"; break; | |||
| case 40: singleUTF8 = "🂢"; break; | |||
| case 41: singleUTF8 = "🂣"; break; | |||
| case 42: singleUTF8 = "🂤"; break; | |||
| case 43: singleUTF8 = "🂥"; break; | |||
| case 44: singleUTF8 = "🂦"; break; | |||
| case 45: singleUTF8 = "🂧"; break; | |||
| case 46: singleUTF8 = "🂨"; break; | |||
| case 47: singleUTF8 = "🂩"; break; | |||
| case 48: singleUTF8 = "🂪"; break; | |||
| case 49: singleUTF8 = "🂫"; break; | |||
| case 50: singleUTF8 = "🂭"; break; | |||
| case 51: singleUTF8 = "🂮"; break; | |||
| case 40: singleUTF8 = "🂡"; break; | |||
| case 41: singleUTF8 = "🂢"; break; | |||
| case 42: singleUTF8 = "🂣"; break; | |||
| case 43: singleUTF8 = "🂤"; break; | |||
| case 44: singleUTF8 = "🂥"; break; | |||
| case 45: singleUTF8 = "🂦"; break; | |||
| case 46: singleUTF8 = "🂧"; break; | |||
| case 47: singleUTF8 = "🂨"; break; | |||
| case 48: singleUTF8 = "🂩"; break; | |||
| case 49: singleUTF8 = "🂪"; break; | |||
| case 50: singleUTF8 = "🂫"; break; | |||
| case 51: singleUTF8 = "🂭"; break; | |||
| case 52: singleUTF8 = "🂮"; break; | |||
| } | |||
| }; | |||
| @@ -122,7 +127,8 @@ std::string Card::text() { | |||
| return numbers[number] + " of " + suitName; | |||
| } | |||
| Card card[52] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, | |||
| 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, | |||
| 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, | |||
| 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; | |||
| Card card[53] = { 0, | |||
| 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, | |||
| 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, | |||
| 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, | |||
| 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52}; | |||
| @@ -98,7 +98,7 @@ Configuration::Configuration(int argc, char **argv) { | |||
| if (optarg != NULL) { | |||
| data["flat_bet"] = optarg; | |||
| } else { | |||
| data["flat_bet"] = "1"; | |||
| data["flat_bet"] = "yes"; | |||
| } | |||
| break; | |||
| case '?': | |||
| @@ -25,11 +25,6 @@ | |||
| #include <thread> | |||
| #include <chrono> | |||
| #include <algorithm> | |||
| #include <functional> | |||
| #include <cctype> | |||
| #include <locale> | |||
| #ifdef HAVE_LIBREADLINE | |||
| #include <readline/readline.h> | |||
| #include <readline/history.h> | |||
| @@ -43,27 +38,6 @@ | |||
| std::vector<std::string> commands; | |||
| // trim from start (in place) | |||
| static inline void ltrim(std::string &s) { | |||
| s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { | |||
| return !std::isspace(ch); | |||
| })); | |||
| } | |||
| // trim from end (in place) | |||
| static inline void rtrim(std::string &s) { | |||
| s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { | |||
| return !std::isspace(ch); | |||
| }).base(), s.end()); | |||
| } | |||
| // trim from both ends (in place) | |||
| static inline void trim(std::string &s) { | |||
| ltrim(s); | |||
| rtrim(s); | |||
| } | |||
| Tty::Tty(Configuration &conf) { | |||
| Libreblackjack::shortversion(); | |||
| @@ -71,6 +45,7 @@ Tty::Tty(Configuration &conf) { | |||
| conf.set(&flat_bet, {"flat_bet", "flatbet"}); | |||
| conf.set(&no_insurance, {"no_insurance", "dont_insure"}); | |||
| conf.set(&delay, {"delay"}); | |||
| if (commands.size() == 0) { | |||
| // commands.push_back("help"); | |||
| @@ -105,7 +80,7 @@ Tty::Tty(Configuration &conf) { | |||
| return; | |||
| } | |||
| void Tty::info(Info msg, int intData) { | |||
| void Tty::info(Info msg, int p) { | |||
| std::string s; | |||
| // TODO: choose utf8 or other representation | |||
| @@ -113,12 +88,12 @@ void Tty::info(Info msg, int intData) { | |||
| switch (msg) { | |||
| case Info::InvalidBet: | |||
| if (intData < 0) { | |||
| if (p < 0) { | |||
| // s = "bet_negative"; | |||
| s = "Your bet is negative (" + std::to_string(intData) + ")"; | |||
| } else if (intData > 0) { | |||
| s = "Your bet is negative (" + std::to_string(p) + ")"; | |||
| } else if (p > 0) { | |||
| // s = "bet_maximum"; | |||
| s = "Your bet is larger than the maximum allowed (" + std::to_string(intData) + ")"; | |||
| s = "Your bet is larger than the maximum allowed (" + std::to_string(p) + ")"; | |||
| } else { | |||
| // s = "bet_zero"; | |||
| s = "Your bet is zero"; | |||
| @@ -128,7 +103,7 @@ void Tty::info(Info msg, int intData) { | |||
| case Info::NewHand: | |||
| // s = "new_hand"; | |||
| std::cout << std::endl; | |||
| s = "Starting new hand, bankroll " + std::to_string(intData); | |||
| s = "Starting new hand, bankroll " + std::to_string(p); | |||
| dealerHand.cards.clear(); | |||
| break; | |||
| @@ -141,41 +116,41 @@ void Tty::info(Info msg, int intData) { | |||
| switch (currentHand->cards.size()) { | |||
| case 1: | |||
| // s = "card_player_first"; | |||
| s = "Player's first card is " + card[intData].utf8(); | |||
| s = "Player's first card is " + card[p].utf8(); | |||
| break; | |||
| case 2: | |||
| // s = "card_player_second"; | |||
| s = "Player's second card is " + card[intData].utf8(); | |||
| s = "Player's second card is " + card[p].utf8(); | |||
| break; | |||
| default: | |||
| // s = "card_player"; | |||
| s = "Player's card is " + card[intData].utf8(); | |||
| s = "Player's card is " + card[p].utf8(); | |||
| break; | |||
| } | |||
| break; | |||
| case Info::CardDealer: | |||
| if (intData != -1) { | |||
| if (p != -1) { | |||
| switch (dealerHand.cards.size()) { | |||
| case 0: | |||
| // s = "card_dealer_up"; | |||
| s = "Dealer's up card is " + card[intData].utf8(); | |||
| s = "Dealer's up card is " + card[p].utf8(); | |||
| break; | |||
| default: | |||
| // s = "card_dealer"; | |||
| s = "Dealer's card is " + card[intData].utf8(); | |||
| s = "Dealer's card is " + card[p].utf8(); | |||
| break; | |||
| } | |||
| } else { | |||
| s = "Dealer's hole card is dealt"; | |||
| } | |||
| dealerHand.cards.push_back(intData); | |||
| dealerHand.cards.push_back(p); | |||
| break; | |||
| case Info::CardDealerRevealsHole: | |||
| // s = "card_dealer_hole"; | |||
| s = "Dealer's hole card was " + card[intData].utf8(); | |||
| *(++(dealerHand.cards.begin())) = intData; | |||
| s = "Dealer's hole card was " + card[p].utf8(); | |||
| *(++(dealerHand.cards.begin())) = p; | |||
| // renderTable(); | |||
| break; | |||
| @@ -194,6 +169,12 @@ void Tty::info(Info msg, int intData) { | |||
| s = "Player also has Blackjack"; | |||
| renderTable(); | |||
| break; | |||
| case Info::PlayerNextHand: | |||
| // s = "player_pushes"; | |||
| s = "Playing next hand #" + std::to_string(p); | |||
| renderTable(); | |||
| break; | |||
| case Info::PlayerPushes: | |||
| // s = "player_pushes"; | |||
| @@ -213,7 +194,7 @@ void Tty::info(Info msg, int intData) { | |||
| break; | |||
| case Info::PlayerWins: | |||
| // s = "player_wins"; | |||
| s = "Player wins " + std::to_string(intData); | |||
| s = "Player wins " + std::to_string(p); | |||
| renderTable(); | |||
| break; | |||
| @@ -389,12 +370,12 @@ void Tty::renderTable(void) { | |||
| std::cout << " -- Dealer's hand: --------" << std::endl; | |||
| renderHand(&dealerHand); | |||
| std::cout << " Total: " << dealerHand.total() << std::endl; | |||
| std::cout << " Total: " << ((dealerHand.total() < 0)?"soft ":"") << std::abs(dealerHand.total()) << std::endl; | |||
| std::cout << " -- Player's hand --------" << std::endl; | |||
| for (auto hand : hands) { | |||
| renderHand(&hand); | |||
| std::cout << " Total: " << hand.total() << std::endl; | |||
| std::cout << " Total: " << ((hand.total() < 0)?"soft ":"") << std::abs(hand.total()) << std::endl; | |||
| } | |||
| return; | |||
| @@ -22,6 +22,11 @@ | |||
| #ifndef TTY_H | |||
| #define TTY_H | |||
| #include <algorithm> | |||
| #include <functional> | |||
| #include <cctype> | |||
| #include <locale> | |||
| #include "blackjack.h" | |||
| extern std::vector<std::string> commands; | |||
| @@ -66,6 +71,18 @@ class Tty : public Player { | |||
| std::string cyan; | |||
| std::string white; | |||
| std::string reset; | |||
| inline void ltrim(std::string &s) { | |||
| s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); | |||
| }; | |||
| inline void rtrim(std::string &s) { | |||
| s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); | |||
| }; | |||
| inline void trim(std::string &s) { ltrim(s); rtrim(s); }; | |||
| }; | |||
| #endif | |||
| @@ -31,7 +31,7 @@ void shortversion(void) { | |||
| std::cout << "LibreBlackjack "; | |||
| #ifdef LIBREBLACKJACK_VCS_BRANCH | |||
| std::cout << LIBREBLACKJACK_VCS_VERSION << ((LIBREBLACKJACK_VCS_CLEAN == 0) ? "" : "+Δ") << ((strcmp(LIBREBLACKJACK_VCS_BRANCH, "master") == 0) ? LIBREBLACKJACK_VCS_BRANCH : "") << std::endl; | |||
| std::cout << LIBREBLACKJACK_VCS_VERSION << ((LIBREBLACKJACK_VCS_CLEAN == 0) ? "" : "+Δ") << ((strcmp(LIBREBLACKJACK_VCS_BRANCH, "master")) ? LIBREBLACKJACK_VCS_BRANCH : "") << std::endl; | |||
| #else | |||
| std::cout << PACKAGE_VERSION << std::endl; | |||
| #endif | |||