Browse Source

fixes & improvements

master
gtheler 5 years ago
parent
commit
f19d5c757b
9 changed files with 153 additions and 153 deletions
  1. +5
    -6
      TODO
  2. +3
    -4
      blackjack.conf
  3. +8
    -3
      src/base.h
  4. +55
    -80
      src/blackjack.cpp
  5. +1
    -0
      src/main.cpp
  6. +1
    -1
      src/stdinout.cpp
  7. +1
    -1
      src/stdinout.h
  8. +78
    -57
      src/tty.cpp
  9. +1
    -1
      src/tty.h

+ 5
- 6
TODO View File

* version and copyright
* messages implemented on the player's side
* arranged shoes
* handle no readline
* to_string() para floats
* max_splits through conf (default 3)
* DAS
* shoes (and arranged shoes)
* name of the game the dealer deals * name of the game the dealer deals
* name of the games the player can play * name of the games the player can play
* delays?
* dealers * dealers
* ENHC * ENHC
* blackjack switch * blackjack switch
* multithreading * multithreading
* use of const and restrict * use of const and restrict

+ 3
- 4
blackjack.conf View File

# flat_bet = 1
# max_bet = 2
arranged_cards = 9,8,13,12
delay = 0
; flat_bet = 1
; arranged_cards = 3,8,3,13,4,13,11,7
; delay = 0



+ 8
- 3
src/base.h View File

PlayerBlackjack, PlayerBlackjack,
PlayerWins, PlayerWins,
NoBlackjacks, NoBlackjacks,
PlayerBustsAllHands,
// PlayerBustsAllHands,
DealerBusts, DealerBusts,
Help, Help,
Bye, Bye,
Spades = 3 Spades = 3
}; };


enum class Color {
Black,
Red
};



class Card { class Card {
public: public:


class Hand { class Hand {
public: public:
std::list<int> cards;
std::list<unsigned int> cards;


// inline on purpose // inline on purpose
int total() { int total() {
Player(const Player &&) = delete; Player(const Player &&) = delete;


virtual int play() = 0; virtual int play() = 0;
virtual void info(Info = Info::None, int = 0) = 0;
virtual void info(Info = Info::None, int = 0, int = 0) = 0;
PlayerActionRequired actionRequired = PlayerActionRequired::None; PlayerActionRequired actionRequired = PlayerActionRequired::None;
PlayerActionTaken actionTaken = PlayerActionTaken::None; PlayerActionTaken actionTaken = PlayerActionTaken::None;

+ 55
- 80
src/blackjack.cpp View File



} }


player->info(Info::NewHand, player->bankroll);
player->info(Info::NewHand, n_hand, 1e3*player->bankroll);
return; return;
break; break;
// step 6. deal the dealer's hole card // step 6. deal the dealer's hole card
holeCard = drawCard(&hand); holeCard = drawCard(&hand);
player->info(Info::CardDealer, -1);
player->info(Info::CardDealer);


// step 7.a. if the upcard is an ace ask for insurance // step 7.a. if the upcard is an ace ask for insurance
if (card[upCard].value == 11) { if (card[upCard].value == 11) {
player->blackjacksDealer++; player->blackjacksDealer++;


if (player->currentHand->insured) { if (player->currentHand->insured) {
player->info(Info::PlayerWinsInsurance, player->currentHand->bet);
player->info(Info::PlayerWinsInsurance, 1e3*player->currentHand->bet);
player->current_result += player->currentHand->bet; player->current_result += player->currentHand->bet;
player->bankroll += player->currentHand->bet; player->bankroll += player->currentHand->bet;
player->winsInsured++; player->winsInsured++;


if (playerBlackack) { if (playerBlackack) {
player->info(Info::PlayerBlackjackAlso); player->info(Info::PlayerBlackjackAlso);
player->info(Info::PlayerPushes);
player->info(Info::PlayerPushes, 1e3*player->currentHand->bet);
player->blackjacksPlayer++; player->blackjacksPlayer++;
player->pushes++; player->pushes++;
} else { } else {
player->info(Info::PlayerLosses);
player->info(Info::PlayerLosses, 1e3*player->currentHand->bet);
player->current_result -= player->currentHand->bet; player->current_result -= player->currentHand->bet;
player->bankroll -= player->currentHand->bet; player->bankroll -= player->currentHand->bet;
if (player->bankroll < player->worst_bankroll) { if (player->bankroll < player->worst_bankroll) {
player->bankroll += blackjack_pays * player->currentHand->bet; player->bankroll += blackjack_pays * player->currentHand->bet;
player->blackjacksPlayer++; player->blackjacksPlayer++;
player->info(Info::PlayerWins, blackjack_pays * player->currentHand->bet);
player->info(Info::PlayerWins, 1e3 * blackjack_pays*player->currentHand->bet);
player->wins++; player->wins++;
player->winsBlackjack++; player->winsBlackjack++;


} }


if (player->bustedAllHands) { if (player->bustedAllHands) {

player->info(Info::PlayerBustsAllHands);
player->info(Info::CardDealerRevealsHole, holeCard); player->info(Info::CardDealerRevealsHole, holeCard);
// TODO: no tengo que sacarle todo el dinero?
player->actionRequired = PlayerActionRequired::None; player->actionRequired = PlayerActionRequired::None;
nextAction = DealerAction::StartNewHand; nextAction = DealerAction::StartNewHand;
player->info(Info::CardDealerRevealsHole, holeCard); player->info(Info::CardDealerRevealsHole, holeCard);


// hit if count is less than 17 (or equalt to soft 17 if hit_soft_17 is true)
// hit while count is less than 17 (or equal to soft 17 if hit_soft_17 is true)
dealerTotal = hand.total(); dealerTotal = hand.total();
while ((std::abs(dealerTotal) < 17 || (hit_soft_17 && dealerTotal == -17)) && hand.busted() == 0) { while ((std::abs(dealerTotal) < 17 || (hit_soft_17 && dealerTotal == -17)) && hand.busted() == 0) {
unsigned int dealerCard = drawCard(&hand); unsigned int dealerCard = drawCard(&hand);
player->info(Info::CardDealer, dealerCard); player->info(Info::CardDealer, dealerCard);
dealerTotal = hand.total();
}


dealerTotal = std::abs(hand.total());

if (hand.busted()) {
player->info(Info::DealerBusts);
player->bustsDealer++;
for (auto playerHand : player->hands) {
if (playerHand.busted() == false) {
player->info(Info::PlayerWins, playerHand.bet);
if (hand.busted()) {
player->info(Info::DealerBusts, dealerTotal);
player->bustsDealer++;
for (auto playerHand : player->hands) {
if (playerHand.busted() == false) {
player->info(Info::PlayerWins, 1e3*playerHand.bet);
player->current_result += playerHand.bet;
player->bankroll += playerHand.bet;
player->wins++;
player->winsDoubled += playerHand.doubled;
}
}
} else {
for (auto playerHand : player->hands) {
if (playerHand.busted() == false) { // busted hands have already been solved
playerTotal = std::abs(playerHand.total());
if (dealerTotal > playerTotal) {
player->info(Info::PlayerLosses, 1e3*playerHand.bet, playerTotal);
player->bankroll -= playerHand.bet;
if (player->bankroll < player->worst_bankroll) {
player->worst_bankroll = player->bankroll;
}
player->losses++;
} else if (dealerTotal == playerTotal) {
player->info(Info::PlayerPushes, 1e3*playerHand.bet);
player->pushes++;
} else {
player->info(Info::PlayerWins, 1e3*playerHand.bet, playerTotal);
player->current_result += playerHand.bet; player->current_result += playerHand.bet;
player->bankroll += playerHand.bet; player->bankroll += playerHand.bet;
player->wins++; player->wins++;

if (playerHand.doubled) { if (playerHand.doubled) {
player->winsDoubled++; player->winsDoubled++;
} else { } else {
} }
} }
} }
} else {
for (auto playerHand : player->hands) {
if (playerHand.busted() == false) { // busted hands have already been solved
playerTotal = std::abs(playerHand.total());
if (dealerTotal > playerTotal) {
player->info(Info::PlayerLosses, playerHand.bet);
player->bankroll -= playerHand.bet;
if (player->bankroll < player->worst_bankroll) {
player->worst_bankroll = player->bankroll;
}
player->losses++;
} else if (dealerTotal == playerTotal) {
player->info(Info::PlayerPushes, playerHand.bet);
player->pushes++;
} else {
player->info(Info::PlayerWins, playerHand.bet);
player->current_result += playerHand.bet;
player->bankroll += playerHand.bet;
player->wins++;

if (playerHand.doubled) {
player->winsDoubled++;
} else {
player->wins++;
}
}
}
}
} }
} }
switch (player->actionTaken) { switch (player->actionTaken) {


// TODO: maybe we have to call a basic method with common commands?
// we first check common commands // we first check common commands
///ig+quit+name quit ///ig+quit+name quit
///ig+quit+desc Finish the game ///ig+quit+desc Finish the game
player->info(Info::Help); player->info(Info::Help);
return 0; return 0;
break; break;
case PlayerActionTaken::Count:
std::cout << "count " << (*player->currentHand).total() <<std::endl;
return 0;
break;
case PlayerActionTaken::UpcardValue:
std::cout << "upcard " << card[upCard].utf8() <<std::endl;
return 0;
break;
case PlayerActionTaken::Bankroll:
std::cout << "bankroll " << player->bankroll <<std::endl;
return 0;
break;
case PlayerActionTaken::None: case PlayerActionTaken::None:
return 0; return 0;
break; break;
///ip+double+detail This command can be abbreviated as `d`. ///ip+double+detail This command can be abbreviated as `d`.
case PlayerActionTaken::Double: case PlayerActionTaken::Double:
if (player->currentHand->cards.size() == 2) { if (player->currentHand->cards.size() == 2) {
std::cout << "double_down" << std::endl;


// TODO: check bankroll // TODO: check bankroll
player->total_money_waged += player->currentHand->bet;
player->total_money_waged += player->currentHand->bet;
player->currentHand->bet *= 2; player->currentHand->bet *= 2;
player->currentHand->doubled = true;
player->currentHand->doubled = true;
player->handsDoubled++; player->handsDoubled++;


playerCard = drawCard(&(*player->currentHand)); playerCard = drawCard(&(*player->currentHand));
unsigned int playerTotal = player->currentHand->total(); unsigned int playerTotal = player->currentHand->total();
std::cout << "card_player" << card[playerCard].utf8() << std::endl;
std::cout << "player_total " << playerTotal << std::endl;
player->info(Info::CardPlayer, playerCard);


if (player->currentHand->busted()) { if (player->currentHand->busted()) {
std::cout << "player_busted" << std::endl;
player->info(Info::PlayerLosses, 1e3*player->currentHand->bet, playerTotal);
player->current_result -= player->currentHand->bet; player->current_result -= player->currentHand->bet;
player->bankroll -= player->currentHand->bet; player->bankroll -= player->currentHand->bet;
player->bustsPlayer++; player->bustsPlayer++;
secondCard = *(++player->currentHand->cards.begin()); secondCard = *(++player->currentHand->cards.begin());
// up to three splits (i.e. four hands) // up to three splits (i.e. four hands)
// TODO: choose
if (player->currentSplits < 3 && player->currentHand->cards.size() == 2 &&
card[firstCard].value == card[secondCard].value) {
// TODO: choose through conf
// TODO: check bankroll to see if player can split
if (player->currentSplits < 3 && player->currentHand->cards.size() == 2 && card[firstCard].value == card[secondCard].value) {
// mark that we split to put ids in the hands and to limi the number of spltis // mark that we split to put ids in the hands and to limi the number of spltis
player->currentSplits++; player->currentSplits++;


// create a new hand // create a new hand
PlayerHand newHand; PlayerHand newHand;
newHand.id = player->currentHand->id + 1; newHand.id = player->currentHand->id + 1;
// TODO: check bankroll
newHand.bet = player->currentHand->bet; newHand.bet = player->currentHand->bet;
player->total_money_waged += player->currentHand->bet; player->total_money_waged += player->currentHand->bet;
///ip+hit+detail ///ip+hit+detail
///ip+hit+detail This command can be abbreviated as `h`. ///ip+hit+detail This command can be abbreviated as `h`.
playerCard = drawCard(&(*player->currentHand)); playerCard = drawCard(&(*player->currentHand));
std::cout << "card_player " << card[playerCard].utf8() << std::endl;
player->info(Info::CardPlayer, playerCard);


if (player->currentHand->busted()) { if (player->currentHand->busted()) {
std::cout << "busted_player " << player->currentHand->total() << std::endl;
player->info(Info::PlayerLosses, 1e3*player->currentHand->bet);
player->current_result -= player->currentHand->bet; player->current_result -= player->currentHand->bet;
player->bankroll -= player->currentHand->bet; player->bankroll -= player->currentHand->bet;
player->bustsPlayer++; player->bustsPlayer++;

+ 1
- 0
src/main.cpp View File

while (!dealer->finished()) { while (!dealer->finished()) {
dealer->deal(player); dealer->deal(player);
if (player->actionRequired != PlayerActionRequired::None) { if (player->actionRequired != PlayerActionRequired::None) {
unknownCommands = 0;
do { do {
if (unknownCommands++ > conf.max_incorrect_commands) { if (unknownCommands++ > conf.max_incorrect_commands) {
std::cerr << "Too many unknown commands." << std::endl; std::cerr << "Too many unknown commands." << std::endl;

+ 1
- 1
src/stdinout.cpp View File

StdInOut::StdInOut(void) { StdInOut::StdInOut(void) {
} }


void StdInOut::info(Info msg, int data) {
void StdInOut::info(Info msg, int p1, int p2) {
return; return;
} }



+ 1
- 1
src/stdinout.h View File

~StdInOut() { }; ~StdInOut() { };
int play(void) override; int play(void) override;
void info(Info = Info::None, int = 0) override;
void info(Info = Info::None, int = 0, int = 0) override;
private: private:
std::string input_buffer; std::string input_buffer;

+ 78
- 57
src/tty.cpp View File

return; return;
} }


void Tty::info(Info msg, int p) {
void Tty::info(Info msg, int p1, int p2) {
std::string s; std::string s;
bool render = false;
// TODO: choose utf8 or other representation // TODO: choose utf8 or other representation
switch (msg) { switch (msg) {


case Info::InvalidBet: case Info::InvalidBet:
if (p < 0) {
if (p1 < 0) {
// s = "bet_negative"; // s = "bet_negative";
s = "Your bet is negative (" + std::to_string(p) + ")";
} else if (p > 0) {
s = "Your bet is negative (" + std::to_string(p1) + ")";
} else if (p1 > 0) {
// s = "bet_maximum"; // s = "bet_maximum";
s = "Your bet is larger than the maximum allowed (" + std::to_string(p) + ")";
s = "Your bet is larger than the maximum allowed (" + std::to_string(p1) + ")";
} else { } else {
// s = "bet_zero"; // s = "bet_zero";
s = "Your bet is zero"; s = "Your bet is zero";
case Info::NewHand: case Info::NewHand:
// s = "new_hand"; // s = "new_hand";
std::cout << std::endl; std::cout << std::endl;
s = "Starting new hand, bankroll " + std::to_string(p);
s = "Starting new hand #" + std::to_string(p1) + " with bankroll " + std::to_string(1e-3*p2);
dealerHand.cards.clear(); dealerHand.cards.clear();
break; break;
case Info::Shuffle: case Info::Shuffle:
// TODO: ask the user to cut
// s = "shuffle"; // s = "shuffle";
s = "Deck needs to be shuffled."; s = "Deck needs to be shuffled.";
break; break;
switch (currentHand->cards.size()) { switch (currentHand->cards.size()) {
case 1: case 1:
// s = "card_player_first"; // s = "card_player_first";
s = "Player's first card is " + card[p].utf8();
s = "Player's first card is " + card[p1].utf8();
break; break;
case 2: case 2:
// s = "card_player_second"; // s = "card_player_second";
s = "Player's second card is " + card[p].utf8();
s = "Player's second card is " + card[p1].utf8();
break; break;
default: default:
// s = "card_player"; // s = "card_player";
s = "Player's card is " + card[p].utf8();
s = "Player's card is " + card[p1].utf8();
break; break;
} }
break; break;
case Info::CardDealer: case Info::CardDealer:
if (p != -1) {
if (p1 > 0) {
switch (dealerHand.cards.size()) { switch (dealerHand.cards.size()) {
case 0: case 0:
// s = "card_dealer_up"; // s = "card_dealer_up";
s = "Dealer's up card is " + card[p].utf8();
s = "Dealer's up card is " + card[p1].utf8();
break; break;
default: default:
// s = "card_dealer"; // s = "card_dealer";
s = "Dealer's card is " + card[p].utf8();
s = "Dealer's card is " + card[p1].utf8();
break; break;
} }
} else { } else {
s = "Dealer's hole card is dealt"; s = "Dealer's hole card is dealt";
} }
dealerHand.cards.push_back(p);
dealerHand.cards.push_back(p1);
break; break;
case Info::CardDealerRevealsHole: case Info::CardDealerRevealsHole:
// s = "card_dealer_hole"; // s = "card_dealer_hole";
s = "Dealer's hole card was " + card[p].utf8();
*(++(dealerHand.cards.begin())) = p;
// renderTable();
s = "Dealer's hole card was " + card[p1].utf8();
*(++(dealerHand.cards.begin())) = p1;
break; break;
case Info::DealerBlackjack: case Info::DealerBlackjack:
case Info::PlayerBlackjackAlso: case Info::PlayerBlackjackAlso:
// s = "player_blackjack_also"; // s = "player_blackjack_also";
s = "Player also has Blackjack"; s = "Player also has Blackjack";
renderTable();
render = true;
break; break;


case Info::PlayerNextHand: case Info::PlayerNextHand:
// s = "player_pushes";
s = "Playing next hand #" + std::to_string(p);
renderTable();
// s = "player_next_hand";
s = "Playing next hand #" + std::to_string(p1);
render = true;
break; break;
case Info::PlayerPushes: case Info::PlayerPushes:
// s = "player_pushes"; // s = "player_pushes";
s = "Player pushes";
renderTable();
s = "Player pushes " + std::to_string(1e-3*p1) + ((p2 > 0) ? (" with " + std::to_string(p2)) : "");
render = true;
break; break;
case Info::PlayerLosses: case Info::PlayerLosses:
// s = "player_losses"; // s = "player_losses";
s = "Player losses";
renderTable();
s = "Player losses " + std::to_string(1e-3*p1) + ((p2 > 0) ? (" with " + std::to_string(p2)) : "");
render = true;
break; break;
case Info::PlayerBlackjack: case Info::PlayerBlackjack:
// s = "blackjack_player"; // s = "blackjack_player";
s = "Player has Blackjack"; s = "Player has Blackjack";
renderTable();
render = true;
break; break;
case Info::PlayerWins: case Info::PlayerWins:
// s = "player_wins"; // s = "player_wins";
s = "Player wins " + std::to_string(p);
renderTable();
s = "Player wins " + std::to_string(1e-3*p1) + ((p2 > 0) ? (" with " + std::to_string(p2)) : "");
render = true;
break; break;
case Info::NoBlackjacks: case Info::NoBlackjacks:
// s = "no_blackjacks"; // s = "no_blackjacks";
s = "No blackjacks"; s = "No blackjacks";
break; break;

/*
case Info::PlayerBustsAllHands: case Info::PlayerBustsAllHands:
// s = "player_busted_all_hands"; // s = "player_busted_all_hands";
if (hands.size() == 1) { if (hands.size() == 1) {
} }
renderTable(); renderTable();
break; break;
*/
case Info::DealerBusts: case Info::DealerBusts:
// s = "no_blackjacks"; // s = "no_blackjacks";
s = "Dealer busts!";
renderTable();
s = "Dealer busts with " + std::to_string(p1);
break; break;
case Info::Help: case Info::Help:
std::this_thread::sleep_for(std::chrono::milliseconds(delay)); std::this_thread::sleep_for(std::chrono::milliseconds(delay));
} }
std::cout << green << s << reset << std::endl; std::cout << green << s << reset << std::endl;

if (render) {
renderTable();
}
return; return;
} }


// TODO: better solution // TODO: better solution
std::string command = input_buffer; std::string command = input_buffer;
free(input_buffer);
trim(command); trim(command);
switch (actionRequired) { switch (actionRequired) {


case PlayerActionRequired::Bet: case PlayerActionRequired::Bet:
currentBet = std::stoi(input_buffer);
currentBet = std::stoi(command);
actionTaken = PlayerActionTaken::Bet; actionTaken = PlayerActionTaken::Bet;
break; break;


} }
} }
free(input_buffer);
} }
#else #else


void Tty::renderHand(Hand *hand) { void Tty::renderHand(Hand *hand) {


std::string ansiColor;
std::string ansiReset;
for (unsigned int i = 0; i < hand->cards.size(); i++) { for (unsigned int i = 0; i < hand->cards.size(); i++) {
std::cout << " _____ "; std::cout << " _____ ";
} }
std::cout << std::endl; std::cout << std::endl;
unsigned int i = 0;
for (auto it : hand->cards) {
if (it >= 0) {
std::cout << "|" << card[it].getNumberASCII() << ((card[it].number != 10)?" ":"") << " | ";
for (auto c : hand->cards) {
if (color && (card[c].suit == Suit::Diamonds || card[c].suit == Suit::Hearts)) {
ansiColor = red;
ansiReset = reset;
} else {
ansiColor = "";
ansiReset = "";
}
if (c > 0) {
std::cout << "|" << ansiColor << card[c].getNumberASCII() << ((card[c].number != 10)?" ":"") << ansiReset << " | ";
} else { } else {
std::cout << "|#####| "; std::cout << "|#####| ";
} }
i++;
} }
std::cout << std::endl; std::cout << std::endl;


i = 0;
for (auto it : hand->cards) {
if (it >= 0) {
for (auto c : hand->cards) {
if (c > 0) {
std::cout << "| | "; std::cout << "| | ";
} else { } else {
std::cout << "|#####| "; std::cout << "|#####| ";
} }
i++;
} }
std::cout << std::endl; std::cout << std::endl;
i = 0;
for (auto it : hand->cards) {
if (it >= 0) {
std::cout << "| " << card[it].getSuitUTF8() << " | ";
for (auto c : hand->cards) {
if (color && (card[c].suit == Suit::Diamonds || card[c].suit == Suit::Hearts)) {
ansiColor = red;
ansiReset = reset;
} else {
ansiColor = "";
ansiReset = "";
}
if (c > 0) {
std::cout << "| " << ansiColor << card[c].getSuitUTF8() << ansiReset << " | ";
} else { } else {
std::cout << "|#####| "; std::cout << "|#####| ";
} }
i++;
} }
std::cout << std::endl; std::cout << std::endl;
i = 0;
for (auto it : hand->cards) {
if (it >= 0) {
for (auto c : hand->cards) {
if (c > 0) {
std::cout << "| | "; std::cout << "| | ";
} else { } else {
std::cout << "|#####| "; std::cout << "|#####| ";
} }
i++;
} }
std::cout << std::endl; std::cout << std::endl;


i = 0;
for (auto it : hand->cards) {
if (it >= 0) {
std::cout << "|___" << ((card[it].number != 10)?"_":"") << card[it].getNumberASCII() << "| ";
for (auto c : hand->cards) {
if (color && (card[c].suit == Suit::Diamonds || card[c].suit == Suit::Hearts)) {
ansiColor = red;
ansiReset = reset;
} else {
ansiColor = "";
ansiReset = "";
}
if (c > 0) {
std::cout << "|___" << ansiColor << ((card[c].number != 10)?"_":"") << card[c].getNumberASCII() << ansiReset<< "| ";
} else { } else {
std::cout << "|#####| "; std::cout << "|#####| ";
} }
i++;
} }
std::cout << std::endl; std::cout << std::endl;

+ 1
- 1
src/tty.h View File

~Tty() { }; ~Tty() { };
int play() override; int play() override;
void info(Info = Info::None, int = 0) override;
void info(Info = Info::None, int = 0, int = 0) override;


// for readline's autocompletion // for readline's autocompletion
static char *rl_command_generator(const char *, int); static char *rl_command_generator(const char *, int);

Loading…
Cancel
Save