Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored random + memory #11

Merged
merged 14 commits into from
Dec 27, 2023
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3")

set(INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(SRC_DIR ${PROJECT_SOURCE_DIR}/src)
set(TEST_DIR ${PROJECT_SOURCE_DIR}/tests)
set(PLAYER_DIR ${SRC_DIR}/players)
set(CHECK_PLAYER ${PLAYER_DIR}/check_player/check_player.cpp)
set(RAND_PLAYER ${PLAYER_DIR}/rand_player/rand_player.cpp)

include(FetchContent)

Expand Down
2 changes: 1 addition & 1 deletion format.sh
Original file line number Diff line number Diff line change
@@ -1 +1 @@
clang-format -i src/*.cpp include/*.h tests/*.cpp src/players/*/*.cpp src/players/*/*.h --style file:.clang-format
clang-format -i src/*.cpp include/*.h tests/*.cpp tests/*.h src/players/*/*.cpp src/players/*/*.h --style file:.clang-format
15 changes: 12 additions & 3 deletions include/config.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#pragma once
#include <iostream>

#include "logger.h"

const constexpr u_int8_t MAX_PLAYERS = 10;

class Config {
public:
u_int64_t startingChips; // Starting chips for each player
Expand All @@ -10,11 +14,16 @@ class Config {
u_int8_t numPlayers; // Number of players in the game

// Constructor
Config(u_int16_t rounds, u_int8_t players, u_int64_t chips, u_int64_t small, u_int64_t addBlind) noexcept
: startingChips(chips), smallBlind(small), addBlindPerDealer0(addBlind), numRounds(rounds), numPlayers(players) {}
Config(u_int16_t rounds, u_int8_t players, u_int64_t chips, u_int64_t small, u_int64_t addBlind)
: startingChips(chips), smallBlind(small), addBlindPerDealer0(addBlind), numRounds(rounds), numPlayers(players) {
if (this->numPlayers < 2 || this->numPlayers > MAX_PLAYERS) {
PLOG_FATAL << "Invalid number of players: " << this->numPlayers << " (min: 2, max: " << MAX_PLAYERS << ")";
throw std::invalid_argument("Invalid number of players");
}
}
};

class BaseConfig : public Config {
public:
BaseConfig() noexcept : Config(10, 4, 1000, 10, 1) {} // Default values
BaseConfig() : Config(10, 4, 1000, 10, 1) {} // Default values
};
35 changes: 18 additions & 17 deletions include/data_structs.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
#include <iostream>
#include <vector>

#include "config.h"
#include "deck.h"

enum class Actions {
Expand Down Expand Up @@ -38,28 +38,35 @@ struct Action {
u_int64_t bet = 0;
};

enum class OutEnum {
ROUND_CONTINUE,
GAME_WON,
ROUND_WON,
};

// contains the data for a single bet round (preflop, flop, turn, river)
struct BetRoundData {
u_int8_t playerPos; // position of the player that is currently playing
u_int64_t currentBet; // current bet
std::vector<u_int64_t> playerBets; // bets of the players in the current round
u_int64_t playerBets[MAX_PLAYERS]; // bets of the players in the current round
};

// contains the data for a single round (until the pot is won)
struct RoundData {
u_int64_t smallBlind; // small blind
u_int64_t bigBlind; // big blind
u_int64_t addBlind; // add blind amount every time the dealer is again at position 0
u_int8_t dealerPos; // position of the dealer
u_int64_t pot; // current pot
std::vector<bool> playerFolded; // true if player folded
std::vector<Card> communityCards; // community cards
u_int64_t smallBlind; // small blind
u_int64_t bigBlind; // big blind
u_int64_t addBlind; // add blind amount every time the dealer is again at position 0
u_int8_t dealerPos; // position of the dealer
u_int64_t pot; // current pot
bool playerFolded[MAX_PLAYERS]; // true if player folded
Card communityCards[5]; // community cards
OutEnum result; // whats the state of the round (continue, round won, game won)
};

// contains the data for a single game (until only one player is left)
struct GameData {
std::vector<bool> playerOut; // true if player is out of the game
std::vector<u_int64_t> playerChips; // chips of the players
bool playerOut[MAX_PLAYERS]; // true if player is out of the game
u_int64_t playerChips[MAX_PLAYERS]; // chips of the players
};

// contains all Data for a game set (multiple games)
Expand Down Expand Up @@ -103,10 +110,4 @@ struct Data {
u_int64_t getCallAdd() const noexcept { return this->betRoundData.currentBet - this->betRoundData.playerBets[this->betRoundData.playerPos]; }

u_int64_t getRaiseAdd(const u_int64_t bet) const noexcept { return bet - this->betRoundData.playerBets[this->betRoundData.playerPos]; }
};

enum class OutEnum {
ROUND_CONTINUE,
GAME_WON,
ROUND_WON,
};
27 changes: 16 additions & 11 deletions include/deck.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#pragma once

#include <random>
#include <iostream>

struct Card {
unsigned char rank; // 2-14
unsigned char suit; // 0-3 (0 = Diamonds, 1 = Hearts, 2 = Spades, 3 = Clubs)
u_int8_t rank; // 2-14
u_int8_t suit; // 0-3 (0 = Diamonds, 1 = Hearts, 2 = Spades, 3 = Clubs)

std::string toString() const;

friend bool operator==(const Card& lhs, const Card& rhs) noexcept { return lhs.rank == rhs.rank && lhs.suit == rhs.suit; };
friend bool operator!=(const Card& lhs, const Card& rhs) noexcept { return !(lhs == rhs); };
};

class Deck {
Expand All @@ -18,15 +19,19 @@ class Deck {
Card draw();
std::string toString(const std::string sep = "\n") const;

static Card getRandomCardExcept(const std::vector<Card>& cards, const int8_t suit = -1, const std::vector<u_int8_t> ranks = {}) noexcept;
static Card getRandomCardExceptCardsWith(const std::vector<Card>& exceptionCards, const int8_t suit = -1, const int8_t rank = -1) noexcept;
static Card getRandomCardExcept(const Card cards[], const u_int8_t cardsLen, const int8_t suit = -1, const u_int8_t ranks[] = {}, const u_int8_t rankLen = 0) noexcept;
static Card getRandomCardExceptCardsWith(const Card exceptionCards[], const u_int8_t cardsLen, const int8_t suit = -1, const int8_t rank = -1) noexcept;

~Deck() { delete[] cards; };
friend bool operator==(const Deck& lhs, const Deck& rhs) noexcept {
if (lhs.len != rhs.len) return false;
for (u_int8_t i = 0; i < lhs.len; i++) {
if (lhs.cards[i] != rhs.cards[i]) return false;
}
return true;
};
friend bool operator!=(const Deck& lhs, const Deck& rhs) noexcept { return !(lhs == rhs); };

private:
Card* cards;
unsigned char len;
std::random_device dev;
std::mt19937 rng{dev()};
std::uniform_int_distribution<std::mt19937::result_type> dist{0, INT32_MAX}; // distribution in max u_int32 range
Card cards[52];
u_int8_t len = 52;
};
43 changes: 32 additions & 11 deletions include/game.h
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
#pragma once
#include <memory>

#include "config.h"
#include "player.h"

class Game {
friend class GameTest;

public:
Game(const Config& config) noexcept : m_config(config) {}
Game(const Config& config) noexcept : m_config(config), players(new std::unique_ptr<Player>[config.numPlayers]) {}

void run();

void run() const;
~Game() { delete[] this->players; }

private:
std::string getPlayerInfo() const noexcept;

void initPlayerOrder() noexcept;

// starts a round by shuffling the deck, setting the dealer and the blinds
OutEnum startRound(Player* players[], Data& data, Deck& deck, const bool firstRound) const noexcept;
OutEnum startRound(const bool firstRound);

// sets the blinds for the round by betting the small and big blind automatically
OutEnum setBlinds(Player* players[], Data& data) const noexcept;
OutEnum setBlinds() noexcept;

// sets up the data for a bet round (preflop, flop, turn, river)
void setupBetRound(Data& data) const noexcept;
void setupBetRound() noexcept;

// runs a single bet round (preflop, flop, turn, river)
OutEnum betRound(Player* players[], Data& data) const;
OutEnum betRound();

// runs a single non out player turn
OutEnum playerTurn(Player* players[], Data& data, short* firstChecker) const;
OutEnum playerTurn(short& firstChecker);

// the current player bets amount and the next player is selected
// note that amount is the total amount that the player bets (e.g. if the player has to call 200 but he already bet 100 => amount is still 200)
bool bet(Data& data, const u_int64_t amount) const noexcept;
bool bet(const u_int64_t amount) noexcept;

// selects the current player as out and returns true if only one player is left in this game, selects the next player
OutEnum playerOut(Player* players[], Data& data) const noexcept;
OutEnum playerOut() noexcept;

// selects the current player as folded and returns true if only one player is left in this round, selects the next player
OutEnum playerFolded(Data& data) const noexcept;
OutEnum playerFolded() noexcept;

OutEnum getOutEnum() const noexcept;

void preflop();

void flop();

void turn();

OutEnum getOutEnum(const Data& data) const noexcept;
void river();

Config m_config;
std::unique_ptr<Player>* players;
Data data;
Deck deck;
};
7 changes: 4 additions & 3 deletions include/hand_strengths.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ class HandStrengths {
u_int32_t rankStrength;

HandStrengths(HandKinds handkind, u_int32_t rankStrength) noexcept : handkind(handkind), rankStrength(rankStrength){};
HandStrengths() noexcept : handkind(HandKinds::NO_HAND), rankStrength(0){};

static std::vector<HandStrengths> getHandStrengths(Player* players[], const Data& data) noexcept;
static HandStrengths getHandStrength(const std::pair<Card, Card>& hand, const std::vector<Card>& community) noexcept;
static u_int32_t getRankStrength(const std::vector<Card> sortedCards, const u_int8_t num) noexcept;
static void getHandStrengths(const std::unique_ptr<Player> players[], const Data& data, HandStrengths result[]) noexcept;
static HandStrengths getHandStrength(const std::pair<Card, Card>& hand, const Card community[]) noexcept;
static u_int32_t getRankStrength(const Card sortedCards[], const u_int8_t num) noexcept;

friend bool operator>(const HandStrengths& lhs, const HandStrengths& rhs) noexcept;
friend bool operator==(const HandStrengths& lhs, const HandStrengths& rhs) noexcept;
Expand Down
6 changes: 0 additions & 6 deletions include/utils.h

This file was deleted.

12 changes: 2 additions & 10 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@


set(PLAYER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/players)
set(CHECK_PLAYER ${PLAYER_DIR}/check_player/check_player.cpp)
set(RAND_PLAYER ${PLAYER_DIR}/rand_player/rand_player.cpp)

# Add the executable target
add_executable(PokerWorkshop main.cpp deck.cpp game.cpp player.cpp utils.cpp hand_strengths.cpp ${CHECK_PLAYER} ${RAND_PLAYER})
add_executable(PokerWorkshop main.cpp deck.cpp game.cpp player.cpp hand_strengths.cpp ${CHECK_PLAYER} ${RAND_PLAYER})
# Include headers
target_include_directories(PokerWorkshop PUBLIC ${INCLUDE_DIR})
target_include_directories(PokerWorkshop PUBLIC ${PLAYER_DIR})

target_include_directories(PokerWorkshop PUBLIC ${INCLUDE_DIR} ${PLAYER_DIR})

# Link with plog library
target_link_libraries(PokerWorkshop plog)
45 changes: 14 additions & 31 deletions src/deck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,9 @@ std::string Card::toString() const {

Deck::Deck() noexcept {
// generate poker deck
this->len = 52;
this->cards = new Card[this->len];
unsigned char i = 0;
for (unsigned char suit = 0; suit < 4; suit++) {
for (unsigned char rank = 2; rank < 15; rank++) {
u_int8_t i = 0;
for (u_int8_t suit = 0; suit < 4; suit++) {
for (u_int8_t rank = 2; rank < 15; rank++) {
this->cards[i].rank = rank;
this->cards[i].suit = suit;
i++;
Expand All @@ -70,14 +68,7 @@ Deck::Deck() noexcept {

void Deck::shuffle() noexcept {
// shuffle deck
for (unsigned char i = this->len - 1; i > 0; --i) {
unsigned char j = this->dist(this->rng) % (i + 1); // Generate a random index within the unshuffled part of the array

// Swap characters at indices i and j
Card temp = this->cards[i];
this->cards[i] = this->cards[j];
this->cards[j] = temp;
}
std::random_shuffle(&this->cards[0], &this->cards[52]);
}

Card Deck::draw() {
Expand All @@ -93,45 +84,37 @@ Card Deck::draw() {
std::string Deck::toString(const std::string sep) const {
// print deck
std::string str = "";
for (unsigned char i = 0; i < this->len; i++) {
for (u_int8_t i = 0; i < this->len; i++) {
str += this->cards[i].toString() + sep;
}
return str;
}

Card Deck::getRandomCardExcept(const std::vector<Card>& cards, const int8_t suit, const std::vector<u_int8_t> ranks) noexcept {
// get random card from deck except cards in vector
Card Deck::getRandomCardExcept(const Card cards[], const u_int8_t cardsLen, const int8_t suit, const u_int8_t ranks[], const u_int8_t rankLen) noexcept {
// get random card from deck except cards in array
// or with suit if suit != -1
// or with ranks if ranks.size() > 0

std::random_device dev;
std::mt19937 rng{dev()};
std::uniform_int_distribution<std::mt19937::result_type> dist{0, INT32_MAX}; // distribution in max u_int32 range

while (true) {
Card card{.rank = (u_int8_t)((dist(rng) % 13) + 2), .suit = (u_int8_t)(dist(rng) % 4)};
if (std::find(cards.begin(), cards.end(), card) == cards.end() && (suit == -1 || card.suit != suit) && (ranks.size() == 0 || std::find(ranks.begin(), ranks.end(), card.rank) == ranks.end())) {
Card card{.rank = (u_int8_t)((std::rand() % 13) + 2), .suit = (u_int8_t)(std::rand() % 4)};
if (std::find(cards, &cards[cardsLen], card) == &cards[cardsLen] && (suit == -1 || card.suit != suit) && (!rankLen || std::find(ranks, &ranks[rankLen], card.rank) == &ranks[rankLen])) {
return card;
}
}
}

Card Deck::getRandomCardExceptCardsWith(const std::vector<Card>& exceptionCards, const int8_t suit, const int8_t rank) noexcept {
Card Deck::getRandomCardExceptCardsWith(const Card exceptionCards[], const u_int8_t cardsLen, const int8_t suit, const int8_t rank) noexcept {
// get random card from deck with suit if suit != -1 and rank if rank != -1

std::random_device dev;
std::mt19937 rng{dev()};
std::uniform_int_distribution<std::mt19937::result_type> dist{0, INT32_MAX}; // distribution in max u_int32 range
Card card;
do {
if (suit == -1 && rank == -1) // get random card
card = Card{.rank = (u_int8_t)((dist(rng) % 13) + 2), .suit = (u_int8_t)(dist(rng) % 4)};
card = Card{.rank = (u_int8_t)((std::rand() % 13) + 2), .suit = (u_int8_t)(std::rand() % 4)};
else if (suit == -1) // get random card with rank
card = Card{.rank = (u_int8_t)rank, .suit = (u_int8_t)(dist(rng) % 4)};
card = Card{.rank = (u_int8_t)rank, .suit = (u_int8_t)(std::rand() % 4)};
else if (rank == -1) // get random card with suit
card = Card{.rank = (u_int8_t)((dist(rng) % 13) + 2), .suit = (u_int8_t)suit};
card = Card{.rank = (u_int8_t)((std::rand() % 13) + 2), .suit = (u_int8_t)suit};
else // get card with suit and rank
card = Card{.rank = (u_int8_t)rank, .suit = (u_int8_t)suit};
} while (std::find(exceptionCards.begin(), exceptionCards.end(), card) != exceptionCards.end());
} while (std::find(exceptionCards, &exceptionCards[cardsLen], card) != &exceptionCards[cardsLen]);
return card;
}
Loading