gtheler пре 5 година
родитељ
комит
5ecfebc912
9 измењених фајлова са 478 додато и 57 уклоњено
  1. +2
    -1
      Makefile.am
  2. +92
    -26
      src/base.h
  3. +216
    -11
      src/blackjack.cpp
  4. +15
    -2
      src/blackjack.h
  5. +12
    -8
      src/main.cpp
  6. +16
    -7
      src/stdinout.cpp
  7. +17
    -2
      src/stdinout.h
  8. +75
    -0
      src/tty.cpp
  9. +33
    -0
      src/tty.h

+ 2
- 1
Makefile.am Прегледај датотеку

@@ -18,4 +18,5 @@ blackjack_LDADD = $(all_libraries)
blackjack_SOURCES = \
src/main.cpp \
src/blackjack.cpp \
src/stdinout.cpp
src/stdinout.cpp \
src/tty.cpp

+ 92
- 26
src/base.h Прегледај датотеку

@@ -23,6 +23,11 @@
#ifndef BASE_H
#define BASE_H

#include <string>
#include <list>

// TODO: namespace

enum class DealerAction {
None,
StartNewHand,
@@ -38,14 +43,14 @@ enum class DealerAction {
Payout
};

enum class PlayerAction {
enum class PlayerActionRequired {
None,
Bet,
Insurance,
Play
};

enum class Command {
enum class PlayerActionTaken {
None,
// common
Quit,
@@ -57,14 +62,88 @@ enum class Command {
Table,
// particular
Bet,
Yes,
No,
Insure,
DontInsure,
Stand,
Double,
Split,
Hit,
};


#define CARD_ART_LINES 6
#define CARD_TYPES 5
#define CARD_SIZE 16

class Card {
public:
int tag;
int value;
private:
std::string token[CARD_TYPES];
std::string text;
std::string art[CARD_ART_LINES];
};

class Hand {
public:
bool insured;
bool soft;
bool blackjack;
bool busted;
bool holeCardShown; // maybe we need a separate class for dealer and for player?
int bet;
int count;
std::list<Card> cards;
private:
};

class Player {
public:
Player() = default;
~Player() = default;
// delete copy and move constructors
Player(Player&) = delete;
Player(const Player&) = delete;
Player(Player &&) = delete;
Player(const Player &&) = delete;

/*
PlayerAction getNextAction() {
return nextAction;
}
void setNextAction(PlayerAction a) {
nextAction = a;
}
*/
virtual int play() = 0;
PlayerActionRequired actionRequired = PlayerActionRequired::None;
PlayerActionTaken actionTaken = PlayerActionTaken::None;
bool hasSplit = false;
bool hasDoubled = false;

int flatBet = 0;
int currentBet = 0;
int n_hands = 0; // this is different from the dealer's due to splitting
double total_money_waged = 0;
double current_result = 0;
double mean = 0;
double M2 = 0;
double variance = 0;
std::list<Hand> hands;
std::list<Hand>::iterator currentHand;
};

class Dealer {
public:
Dealer() {};
@@ -75,14 +154,17 @@ class Dealer {
Dealer(Dealer &&) = delete;
Dealer(const Dealer &&) = delete;

virtual void deal() = 0;
// virtual void ask() = 0;
virtual int process(Command) = 0;
virtual void deal(Player *) = 0;
virtual int process(Player *) = 0;
void setNextAction(DealerAction a) {
next_action = a;
}

void getNextAction(DealerAction a) {
next_action = a;
}
bool getInputNeeded(void) {
return input_needed;
@@ -100,30 +182,14 @@ class Dealer {
return (done = d);
}
private:
bool done = false;
bool input_needed = false;
DealerAction next_action = DealerAction::None;
PlayerAction player_action = PlayerAction::None;
Command player_command = Command::None;
double insurance = 0;
int bet = 0;
};

class Player {
public:
Player() = default;
~Player() = default;
// delete copy and move constructors
Player(Player&) = delete;
Player(const Player&) = delete;
Player(Player &&) = delete;
Player(const Player &&) = delete;
// TODO: most of the games will have a single element, but maybe
// there are games where the dealer has more than one hand
std::list <Hand> hands;
virtual int play(Command *, int *) = 0;
};

#endif

+ 216
- 11
src/blackjack.cpp Прегледај датотеку

@@ -21,6 +21,7 @@
*/

#include <iostream>
#include <utility>

#include "blackjack.h"

@@ -32,26 +33,230 @@ Blackjack::~Blackjack() {
std::cout << "Bye bye! We'll play Blackjack again next time." << std::endl;
}

void Blackjack::deal() {
std::cout << "Here are your cards" << std::endl;
setInputNeeded(true);
void Blackjack::deal(Player *player) {
switch(next_action) {
// -------------------------------------------------------------------------
case DealerAction::StartNewHand:
// check if we are done
if (n_hands > 0 && n_hand >= n_hands) {
finished(true);
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) {
double delta = player->current_result - player->mean;
player->mean += delta / (double)(n_hand);
player->M2 += delta * (player->current_result - player->mean);
player->variance = player->M2 / (double)(n_hand);
}

infinite_decks_card_number_for_arranged_ones = 0;
n_hand++;
// clear dealer's hand, create a new one and add it to the list
hands.clear();
hands.push_back(std::move(Hand()));

// erase all the player's, create one, add and make it the current one
player->hands.clear();
player->hands.push_back(std::move(Hand()));
player->currentHand = player->hands.begin();
// state that the player did not win anything nor splitted nor doubled down
player->current_result = 0;
player->hasSplit = 0;
player->hasDoubled = 0;
if (lastPass) {
// tell people we are shuffling
// bjcall (blackjack.current_player->write (player, "shuffling"));
// shuffle the cards
// shuffle_shoe ();
// TODO: reset card counting systems
// burn as many cards as asked
for (int i = 0; i < number_of_burnt_cards; i++) {
// dealCard();
}
lastPass = false;
}
if (player->flatBet) {
player->currentHand->bet = player->flatBet;
setNextAction(DealerAction::DealPlayerFirstCard);
} else {
setNextAction(DealerAction::AskForBets);
}

// bjcall (blackjack.current_player->write (player, "new_hand"));
// TODO: think!
std::cout << "new_hand" << std::endl;
break;
// -------------------------------------------------------------------------
case DealerAction::AskForBets:
// step 1. ask for bets
// TODO: use an output buffer to re-ask in case no number comes back
std::cout << "bet?" << std::endl;
// TODO: setter
player->actionRequired = PlayerActionRequired::Bet;
setInputNeeded(true);
break;

case DealerAction::DealPlayerFirstCard:
// where's step 2?
// step 3. deal the first card to each player
player->n_hands++; // splits are counted as a single hand
player->total_money_waged += player->currentHand->bet;

Card card;
card.tag = 7;
player->currentHand->cards.push_back(card);
std::cout << "card_player_first " << player->currentHand->cards.begin()->tag << std::endl;
// bjcall (write_formatted_card (player, 0, "card_player_first", card));
/*
// step 4. show dealer's upcard
card = deal_card_to_hand (blackjack.dealer_hand);
bjcall (write_formatted_card (player, 0, "card_dealer_up", card));
if (stdout_opts.isatty)
{
print_card_art (card);
}

// step 5. deal the second card to each player
card = deal_card_to_hand (player->current_hand);
bjcall (write_formatted_card (player, 0, "card_player_second", card));
if (stdout_opts.isatty)
{
print_hand_art (player->current_hand);
}

// TODO: ENHC
blackjack.next_dealer_action = DEAL_DEALERS_HOLE_CARD;
break;
*/
break;
/*
case DealerAction::DealDealerHoleCard:
break;
case DealerAction::AskForInsurance:
break;
case DealerAction::CheckforBlackjacks:
break;
case DealerAction::PayOrTakeInsuranceBets:
break;
case DealerAction::AskForPlay:
std::cout << "Here are your cards" << std::endl;
setInputNeeded(true);
break;
case DealerAction::MoveOnToNextHand:
break;
case DealerAction::HitDealerHand:
break;
case DealerAction::Payout:
break;
case DealerAction::None:
break;
*/
}
}

// returns zero if it is a common command and we need to ask again
// returns positive if what was asked was answered
// returns negative if what was aked was not asnwered or the command does not apply
int Blackjack::process(Command command) {
int Blackjack::process(Player *player) {
switch (command) {
case Command::Hit:
std::cout << "ok, you hit" << std::endl;
switch (player->actionTaken) {

// TODO: maybe we have to call a basic method with common commands?
// we first check common commands
///ig+quit+name quit
///ig+quit+desc Finish the game
///ig+quit+detail Upon receiving this command, the game is finished
///ig+quit+detail immediately without even finishing the hand.
///ig+quit+detail All IPC resources are unlocked, removed and/or destroyed.
///ig+quit+detail The YAML report is written before exiting.
case PlayerActionTaken::Quit:
finished(true);
return 1;
break;
case Command::None:
std::cout << "I don't undertand you" << std::endl;
break;
///ig+help+name help
///ig+help+desc Ask for help
///ig+help+detail A succinct help message is written on the standard output.
///ig+help+detail This command makes sense only when issued by a human player.
// TODO
case PlayerActionTaken::Help:
std::cout << "help yourself" << std::endl;
return 0;
break;
break;
// TODO:
case PlayerActionTaken::Count:
break;
case PlayerActionTaken::UpcardValue:
break;
case PlayerActionTaken::Bankroll:
break;
case PlayerActionTaken::Hands:
break;
case PlayerActionTaken::Table:
break;
case PlayerActionTaken::None:
return 0;
break;
// if we made it this far, the command is particular
case PlayerActionTaken::Bet:
// TODO: bet = 0 -> wonging
if (player->currentBet == 0) {
std::cout << "bet_zero" << std::endl;
return 0;
} else if (player->currentBet < 0) {
std::cout << "bet_negative" << std::endl;
return 0;
} else if (max_bet != 0 && player->currentBet > max_bet) {
std::cout << "bet_maximum" << max_bet << std::endl;
return 0;
} else {
// ok, valid bet
player->currentHand->bet = player->currentBet;
setNextAction(DealerAction::DealPlayerFirstCard);
return 1;
}
break;

case PlayerActionTaken::Insure:
player->currentHand->insured = true;
return 1;
break;

case PlayerActionTaken::DontInsure:
player->currentHand->insured = false;
return 1;
break;
case PlayerActionTaken::Hit:
std::cout << "ok, you hit" << std::endl;
finished(true);
return 1;
break;
}
return 0;

+ 15
- 2
src/blackjack.h Прегледај датотеку

@@ -28,7 +28,20 @@ class Blackjack : public Dealer {
Blackjack();
~Blackjack();
void deal() override;
int process(Command) override;
void deal(Player *) override;
int process(Player *) override;
private:
bool lastPass = false;
int n_hands = 0;
int n_hand = 0;
int max_bet = 0;
int number_of_burnt_cards = 0;
int infinite_decks_card_number_for_arranged_ones = 0;
double insurance = 0;
};
#endif

+ 12
- 8
src/main.cpp Прегледај датотеку

@@ -21,9 +21,11 @@
*/

#include <iostream>
#include <unistd.h>

#include "base.h"
#include "blackjack.h"
#include "tty.h"
#include "stdinout.h"

int main(int argc, char **argv) {
@@ -34,21 +36,23 @@ int main(int argc, char **argv) {
// TODO: read the args/conf to know what kind of dealer and player we are having
// TODO: pass args/conf to the constructor
dealer = new Blackjack();
player = new StdInOut();
std::cout << "Let's play" << std::endl;
if (isatty(1)) {
player = new Tty();
} else {
player = new StdInOut();
}
// TODO: player strategy from file
dealer->setNextAction(DealerAction::StartNewHand);
Command command{Command::None};
while (!dealer->finished()) {
dealer->setInputNeeded(false);
dealer->deal();
dealer->deal(player);
if (dealer->getInputNeeded()) {
do {
// TODO: check for too many errors meaning dealer and player do not understand each other
player->play(&command, nullptr);
} while (dealer->process(command) <= 0);
player->play();
} while (dealer->process(player) <= 0);
}
}

+ 16
- 7
src/stdinout.cpp Прегледај датотеку

@@ -1,20 +1,29 @@
#include <iostream>

#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif


#include "blackjack.h"
#include "stdinout.h"

int StdInOut::play(Command *command, int *param) {
std::cout << "what do you want to do" << std::endl;
std::string input_buffer;
std::cin >> input_buffer;
StdInOut::StdInOut(void) {
}


int StdInOut::play() {
std::cin >> input_buffer;

// TODO: check EOF
/*
if (input_buffer == "hit" || input_buffer == "h") {
*command = Command::Hit;
} else {
*command = Command::None;
}
*/
return 0;
}

+ 17
- 2
src/stdinout.h Прегледај датотеку

@@ -4,9 +4,24 @@

class StdInOut : public Player {
public:
StdInOut() { };
StdInOut();
~StdInOut() { };
int play(Command *, int *) override;
int play(void) override;
private:
std::string input_buffer;

std::string black = "\x1B[0m";
std::string red = "\x1B[31m";
std::string green = "\x1B[32m";
std::string yellow = "\x1B[33m";
std::string blue = "\x1B[34m";
std::string magenta = "\x1B[35m";
std::string cyan = "\x1B[36m";
std::string white = "\x1B[37m";
std::string reset = "\033[0m";
};
#endif

+ 75
- 0
src/tty.cpp Прегледај датотеку

@@ -0,0 +1,75 @@
#include <iostream>
#include <cstring>

#ifdef HAVE_LIBREADLINE
#include <readline/readline.h>
#include <readline/history.h>
#endif

#include "blackjack.h"
#include "tty.h"

Tty::Tty(void) {
#ifdef HAVE_LIBREADLINE
prompt = cyan + " > " + reset;
#endif
}

int Tty::play() {
#ifdef HAVE_LIBREADLINE
if ((input_buffer = readline(prompt.c_str())) != nullptr) {
add_history(input_buffer);
}

// TODO: check EOF
// TODO: esto puede ir en algo comun para tty y stdout
switch (actionRequired) {
case PlayerActionRequired::Bet:
// TODO: both as parameters or both as class members
currentBet = atoi(input_buffer);
actionTaken = PlayerActionTaken::Bet;
break;
case PlayerActionRequired::Insurance:
if (strcmp(input_buffer, "y") == 0 || strcmp(input_buffer, "yes") == 0) {
actionTaken = PlayerActionTaken::Insure;
} else if (strcmp(input_buffer, "n") == 0 || strcmp(input_buffer, "no") == 0) {
actionTaken = PlayerActionTaken::DontInsure;
} else {
// TODO: chosse if we allow not(yes) == no
actionTaken = PlayerActionTaken::None;
}
break;
case PlayerActionRequired::Play:
if (strcmp(input_buffer, "h") == 0 || strcmp(input_buffer, "hit") == 0) {
actionTaken = PlayerActionTaken::Hit;
} else {
actionTaken = PlayerActionTaken::None;
}
break;
}
free(input_buffer);
#else

std::cout << prompt;
std::cin >> input_buffer;

// TODO: check EOF
// TODO: esto puede ir en algo comun para tty y stdout
if (input_buffer == "hit" || input_buffer == "h") {
*command = Command::Hit;
} else {
*command = Command::None;
}
#endif
return 0;
}

+ 33
- 0
src/tty.h Прегледај датотеку

@@ -0,0 +1,33 @@
#ifndef TTY_H
#define TTY_H
#include "blackjack.h"

class Tty : public Player {
public:
Tty();
~Tty() { };
int play() override;
private:
#ifdef HAVE_LIBREADLINE
char *input_buffer;
#else
std::string input_buffer;
#endif
std::string prompt;

std::string black = "\x1B[0m";
std::string red = "\x1B[31m";
std::string green = "\x1B[32m";
std::string yellow = "\x1B[33m";
std::string blue = "\x1B[34m";
std::string magenta = "\x1B[35m";
std::string cyan = "\x1B[36m";
std::string white = "\x1B[37m";
std::string reset = "\033[0m";
};
#endif

Loading…
Откажи
Сачувај