Poker winning moment.

This commit is contained in:
2022-11-27 07:55:57 -08:00
parent c1d6885970
commit b8272f800c
19 changed files with 572 additions and 54 deletions

View File

@ -7,6 +7,7 @@
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Card.cpp
PokerPot.cpp
PokerPlayer.cpp
PokerGame.cpp
PokerWinning.cpp

View File

@ -17,7 +17,6 @@ void Card::fillDeck(std::vector<struct Card> *deck) {
int32_t Card::contains(std::vector<struct Card> *deck, struct Card c) {
assertNotNull(deck);
assertTrue(deck->size() > 0);
auto it = deck->begin();
while(it != deck->end()) {

View File

@ -12,7 +12,9 @@ namespace Dawn {
CARD_CLUBS = 0,
CARD_DIAMONDS = 1,
CARD_HEARTS = 2,
CARD_SPADES = 3
CARD_SPADES = 3,
CARD_SUIT_INVALUD = 0xFF
};
enum CardValue {
@ -28,7 +30,9 @@ namespace Dawn {
CARD_JACK = 9,
CARD_QUEEN = 10,
CARD_KING = 11,
CARD_ACE = 12
CARD_ACE = 12,
CARD_VALUE_INVALD = 0xFF
};
/** Count of cards in each suit */
@ -106,12 +110,13 @@ namespace Dawn {
Card(CardSuit suit, CardValue num) :
cardValue((suit * CARD_COUNT_PER_SUIT) + num)
{
assertTrue(suit < CARD_SUIT_COUNT);
assertTrue(num < CARD_COUNT_PER_SUIT);
if(suit == CARD_SUIT_INVALUD || num == CARD_VALUE_INVALD) {
this->cardValue = 0xFF;
}
}
Card(uint8_t cv) : cardValue(cv) {
assertTrue(cv < CARD_DECK_SIZE);
// assertTrue(cv < CARD_DECK_SIZE);
}
/**

View File

@ -107,7 +107,7 @@ void PokerGame::setDealer(uint8_t dealer) {
for(i = 0; i < this->players.size(); i++) {
k = (dealer + i) % this->players.size();
player = this->players[k];
if(player->isOut) ;
if(player->isOut) continue;
if(!foundDealer) {
this->dealerIndex = k;
foundDealer = true;

View File

@ -256,6 +256,8 @@ struct PokerWinning PokerPlayer::getWinning() {
enum CardSuit suit;
std::vector<struct Card> pairs;
winning.player = this;
// Get the full poker hand (should be a 7 card hand, but MAY not be)
for(i = 0; i < this->pokerGame->community.size(); i++) {
winning.full.push_back(this->pokerGame->community[i]);

111
src/dawn/poker/PokerPot.cpp Normal file
View File

@ -0,0 +1,111 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerPot.hpp"
#include "PokerGame.hpp"
#include "PokerPlayer.hpp"
using namespace Dawn;
void PokerPotWinning::award() {
auto it = this->winners.begin();
while(it != this->winners.end()) {
if(it == this->winners.begin()) {
(*it)->addChips(this->chipsOverflow);
} else {
(*it)->addChips(this->chipsEach);
}
++it;
}
}
struct PokerPotWinning PokerPot::getWinners(PokerGame *game) {
struct PokerPotWinning winning;
winning.pot = this;
// Calculate the winnings first.
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
if(player->isOut || player->isFolded) {
++it;
continue;
}
winning.participants.push_back(player);
winning.winnings[player] = player->getWinning();
++it;
}
// Compare participating players
auto it2 = winning.participants.begin();
while(it2 != winning.participants.end()) {
auto playerLeft = *it2;
auto winnerLeft = &winning.winnings[playerLeft];
bool_t isWinner = true;
enum CardValue highNumber = CARD_VALUE_INVALD;
enum CardValue number = CARD_VALUE_INVALD;
struct Card highCard(0xFF);
struct Card card(0xFF);
auto it3 = winning.participants.begin();
while(it3 != winning.participants.end()) {
if(it2 == it3) {
++it3;
continue;
}
auto playerRight = *it3;
auto winnerRight = &winning.winnings[playerRight];
// Am I the better hand / Is it the better hand?
if(winnerLeft->type < winnerRight->type) {
++it3;
continue;
}
if(winnerLeft->type > winnerRight->type) {
isWinner = false;
break;
}
// Equal, compare hands.
card = PokerWinning::compare(winnerLeft, winnerRight);
if(card.cardValue == 0xFF) {
isWinner = false;
break;
}
// Determine high card.
number = card.getValue();
if(
highNumber == CARD_VALUE_INVALD ||
number == CARD_ACE ||
number > highNumber
) {
highCard = card;
highNumber = number;
}
++it3;
}
if(!isWinner) {
++it2;
continue;
}
winnerLeft->kicker = highCard;
winning.winners.push_back(playerLeft);
++it2;
}
winning.chipsEach = this->chips / winning.winners.size();
winning.chipsOverflow = this->chips - (
winning.chipsEach * winning.winners.size()
);
return winning;
}

View File

@ -4,14 +4,31 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "PokerWinning.hpp"
namespace Dawn {
class PokerPlayer;
class PokerGame;
struct PokerPot;
struct PokerPotWinning {
public:
std::map<PokerPlayer*,struct PokerWinning> winnings;
std::vector<PokerPlayer*> winners;
std::vector<PokerPlayer*> participants;
struct PokerPot *pot;
int32_t chipsEach;
int32_t chipsOverflow;
void award();
};
struct PokerPot {
int32_t chips;
int32_t call;
std::vector<PokerPlayer*> players;
public:
int32_t chips;
int32_t call;
std::vector<PokerPlayer*> players;
struct PokerPotWinning getWinners(PokerGame *game);
};
}

View File

@ -4,6 +4,7 @@
// https://opensource.org/licenses/MIT
#include "PokerWinning.hpp"
#include "PokerPlayer.hpp"
using namespace Dawn;
@ -32,6 +33,98 @@ float_t PokerWinning::getWinningTypeConfidence(enum PokerWinningType type) {
}
}
struct Card PokerWinning::compare(
struct PokerWinning *left,
struct PokerWinning *right
) {
assertNotNull(left);
assertNotNull(right);
uint8_t i;
enum CardValue number = CARD_VALUE_INVALD;
enum CardValue highNumberLeft = CARD_VALUE_INVALD;
enum CardValue highNumberRight = CARD_VALUE_INVALD;
struct Card card(0xFF), highCardLeft(0xFF), highCardRight(0xFF);
int32_t index;
uint8_t countCardsSame;
countCardsSame = 0;
for(i = 0; i < left->set.size(); i++) {
card = left->set[i];
number = card.getValue();
// Quick check
if(highNumberLeft != CARD_VALUE_INVALD && number < highNumberLeft) continue;
// Check if this number is within the other hand or not
index = Card::containsNumber(&right->set, number);
if(index != -1) {
// This number IS within the other hand, let's check that the EXACT card
// is a match/isn't a match.
index = Card::contains(&right->set, card);
// Exact card match
if(index != -1) {
countCardsSame++;
continue;
}
// Not exact card match.. ?
}
if(
highNumberLeft == CARD_VALUE_INVALD ||
number == CARD_ACE ||
highNumberLeft < number
) {
highNumberLeft = number;
highCardLeft = card;
}
}
for(i = 0; i < right->set.size(); i++) {
card = right->set[i];
number = card.getValue();
if(highNumberRight != CARD_VALUE_INVALD && number < highNumberRight) {
continue;
}
index = Card::containsNumber(&left->set, number);
if(index != -1) {
index = Card::contains(&left->set, card);
if(index != -1) continue;
}
if(
highNumberRight == CARD_VALUE_INVALD ||
number == CARD_ACE || highNumberRight < number
) {
highNumberRight = number;
highCardRight = card;
}
}
if(countCardsSame == left->set.size()) {
for(i = 0; i < left->set.size(); i++) {
card = left->set[i];
number = card.getValue();
if(
highNumberLeft == CARD_VALUE_INVALD ||
number == CARD_ACE ||
highNumberLeft < number
) {
highNumberLeft = number;
highCardLeft = card;
}
}
return highCardLeft;
}
if(highCardLeft.cardValue == CARD_VALUE_INVALD) return 0xFF;
if(highNumberLeft < highNumberRight) return 0xFF;
return highCardLeft;// Greater or Equal to.
}
void PokerWinning::fillRemaining() {
uint8_t i, highest, current;
struct Card highestCard(0x00);

View File

@ -22,6 +22,8 @@
#define POKER_WINNING_SET_SIZE 5
namespace Dawn {
class PokerPlayer;
enum PokerWinningType {
POKER_WINNING_TYPE_NULL,
POKER_WINNING_TYPE_ROYAL_FLUSH,
@ -38,17 +40,47 @@ namespace Dawn {
struct PokerWinning {
public:
enum PokerWinningType type;
std::vector<struct Card> full;
std::vector<struct Card> set;
struct Card kicker;
PokerWinning() : kicker(0x00) {
}
/**
* Get the confidence of the bet for a given winning type.
*
* @param type Winning type type.
* @return The confidence.
*/
static float_t getWinningTypeConfidence(enum PokerWinningType type);
/**
* Compares two winning sets. The returned card is the kicker if the LEFT
* side is the winner. If LEFT is not a winner then 0xFF will be returned.
*
* @param left Left winning set.
* @param right Right winning set.
* @return The kicker card from left's hand or 0xFF if not the winner.
*/
static struct Card compare(
struct PokerWinning *left,
struct PokerWinning *right
);
/** Winning Type */
enum PokerWinningType type;
/** The full set of both the dealer and player's hand */
std::vector<struct Card> full;
/** Holds the winning set */
std::vector<struct Card> set;
/** If there was a kicker card it will be here */
struct Card kicker;
/* The player this winning state belongs to */
PokerPlayer *player;
PokerWinning() : kicker(0xFF) {}
/**
* Fills the remaining cards for a given poker player winning hand.
* Essentially this will just take the highest cards and slot them into
* the array. This also sorts the cards.
*
* @param winning Pointer to the poker winning to fill out.
*/
void fillRemaining();
};
}

View File

@ -11,6 +11,12 @@
namespace Dawn {
class PokerAIBetEvent : public PokerGameEvent {
protected:
IVisualNovelEvent * eventFold = nullptr;
IVisualNovelEvent * eventBet = nullptr;
IVisualNovelEvent * eventCall = nullptr;
IVisualNovelEvent * eventCheck = nullptr;
IVisualNovelEvent * eventAllIn = nullptr;
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
@ -19,6 +25,36 @@ namespace Dawn {
auto player = this->pokerGame->players[better];
this->turn = player->getAITurn();
this->turn.action();
switch(this->turn.type) {
case POKER_TURN_TYPE_FOLD:
this->then(this->eventFold);
this->eventFold = nullptr;
break;
case POKER_TURN_TYPE_BET:
this->then(this->eventBet);
this->eventBet = nullptr;
break;
case POKER_TURN_TYPE_CALL:
this->then(this->eventCall);
this->eventCall = nullptr;
break;
case POKER_TURN_TYPE_CHECK:
this->then(this->eventCheck);
this->eventCheck = nullptr;
break;
case POKER_TURN_TYPE_ALL_IN:
this->then(this->eventAllIn);
this->eventAllIn = nullptr;
break;
default:
assertUnreachable();
}
}
bool_t onUpdate() override {
@ -34,5 +70,73 @@ namespace Dawn {
PokerAIBetEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
/**
* Event that is triggered when the action was a folded event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenFolded(T *event) {
assertNotNull(event);
this->eventFold = event;
return event;
}
/**
* Event that is triggered when the action was a bet event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenBetting(T *event) {
assertNotNull(event);
this->eventBet = event;
return event;
}
/**
* Event that is triggered when the action was a call event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenCalling(T *event) {
assertNotNull(event);
this->eventCall = event;
return event;
}
/**
* Event that is triggered when the action was a check event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenChecking(T *event) {
assertNotNull(event);
this->eventCheck = event;
return event;
}
/**
* Event that is triggered when the action was an all-in event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenAllIn(T *event) {
assertNotNull(event);
this->eventAllIn = event;
return event;
}
~PokerAIBetEvent() {
if(this->eventFold != nullptr) delete this->eventFold;
if(this->eventBet != nullptr) delete this->eventBet;
if(this->eventCall != nullptr) delete this->eventCall;
if(this->eventCheck != nullptr) delete this->eventCheck;
if(this->eventAllIn != nullptr) delete this->eventAllIn;
}
};
}

View File

@ -0,0 +1,29 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerNewBettingRoundEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "New Betting Round" << std::endl;
this->pokerGame->newBettingRound();
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerNewBettingRoundEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -0,0 +1,28 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerWinnerEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "Poker Winning" << std::endl;
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerWinnerEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -17,4 +17,5 @@ target_include_directories(${DAWN_TARGET_NAME}
# Subdirs
add_subdirectory(game)
add_subdirectory(ui)
add_subdirectory(ui)
add_subdirectory(visualnovel)

View File

@ -12,6 +12,7 @@
#include "visualnovel/events/VisualNovelTextboxEvent.hpp"
#include "poker/PokerGame.hpp"
#include "visualnovel/events/PokerBetLoopEvent.hpp"
#include "visualnovel/events/PokerInitialEvent.hpp"
#include "visualnovel/events/SimpleLoopEvent.hpp"
namespace Dawn {
@ -47,13 +48,7 @@ namespace Dawn {
->setEvent(new VisualNovelTextboxEvent(vnManager, "Starting Game"))
->then(new PokerNewGameEvent(vnManager))
->then(new VisualNovelTextboxEvent(vnManager, "Game Started"))
->then(new PokerNewRoundEvent(vnManager))
->then(new VisualNovelTextboxEvent(vnManager, "Round Started"))
->then(new PokerTakeBlindsEvent(vnManager))
->then(new VisualNovelTextboxEvent(vnManager, "Blinds Taken"))
->then(new PokerDealEvent(vnManager))
->then(new VisualNovelTextboxEvent(vnManager, "Cards Dealt"))
->then(new PokerBetLoopEvent(vnManager))
->then(new PokerInitialEvent(vnManager))
;
return scene;

View File

@ -0,0 +1,7 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
add_subdirectory(events)

View File

@ -0,0 +1,10 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
PokerBetLoopEvent.cpp
)

View File

@ -0,0 +1,64 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerBetLoopEvent.hpp"
#include "PokerInitialEvent.hpp"
using namespace Dawn;
void PokerBetLoopEvent::onStart(IVisualNovelEvent *prev) {
PokerGameEvent::onStart(prev);
std::cout << "Bet Loop, bet" << std::endl;
auto evt2 = new PokerDetermineBetterEvent(this->manager);
auto betting = this->then(evt2);
betting
->whenEveryoneFolded(new VisualNovelTextboxEvent(this->manager, "Everyone Folded"))
->then(new PokerWinnerEvent(this->manager))
->then(new PokerInitialEvent(this->manager))
;
betting
->whenBettingFinished(new VisualNovelTextboxEvent(this->manager, "Betting Finished"))
->then(new PokerWinnerEvent(this->manager))
->then(new PokerInitialEvent(this->manager))
;
betting
->whenTurn(new PokerTurnEvent(this->manager))
->then(new VisualNovelTextboxEvent(this->manager, "Turn Time"))
->then(new PokerNewBettingRoundEvent(this->manager))
->then(new PokerBetLoopEvent(this->manager))
;
betting
->whenHumanBet(new VisualNovelTextboxEvent(this->manager, "Human Bet"))
->then(new PokerBetLoopEvent(this->manager))
;
// AI Betting
auto aiBet = betting
->whenAiBet(new VisualNovelTextboxEvent(this->manager, "AI Bet"))
->then(new PokerAIBetEvent(this->manager))
;
aiBet
->whenFolded(new VisualNovelTextboxEvent(this->manager, "Folded"))
->then(new PokerBetLoopEvent(this->manager))
;
aiBet
->whenAllIn(new VisualNovelTextboxEvent(this->manager, "All In"))
->then(new PokerBetLoopEvent(this->manager))
;
aiBet
->whenBetting(new VisualNovelTextboxEvent(this->manager, "Betting"))
->then(new PokerBetLoopEvent(this->manager))
;
aiBet
->whenCalling(new VisualNovelTextboxEvent(this->manager, "Calling"))
->then(new PokerBetLoopEvent(this->manager))
;
aiBet
->whenChecking(new VisualNovelTextboxEvent(this->manager, "Checking"))
->then(new PokerBetLoopEvent(this->manager))
;
}

View File

@ -11,38 +11,15 @@
#include "poker/visualnovel/PokerTurnEvent.hpp"
#include "poker/visualnovel/PokerDetermineBetterEvent.hpp"
#include "poker/visualnovel/PokerAIBetEvent.hpp"
#include "poker/visualnovel/PokerNewBettingRoundEvent.hpp"
#include "poker/visualnovel/PokerWinnerEvent.hpp"
#define POKER_DEAL_EVENT_CARD_COUNT 2
namespace Dawn {
class PokerBetLoopEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "Bet Loop, bet" << std::endl;
auto evt2 = new PokerDetermineBetterEvent(this->manager);
auto betting = this->then(evt2);
betting
->whenEveryoneFolded(new VisualNovelTextboxEvent(this->manager, "Everyone Folded"))
;
betting
->whenBettingFinished(new VisualNovelTextboxEvent(this->manager, "Betting Finished"))
;
betting
->whenTurn(new VisualNovelTextboxEvent(this->manager, "Turn Time"))
;
betting
->whenAiBet(new PokerAIBetEvent(this->manager))
->then(new VisualNovelTextboxEvent(this->manager, "AI Bet"))
->then(new PokerBetLoopEvent(this->manager))
;
betting
->whenHumanBet(new VisualNovelTextboxEvent(this->manager, "Human Bet"))
->then(new PokerBetLoopEvent(this->manager))
;
}
void onStart(IVisualNovelEvent *previous) override;
bool_t onUpdate() override {
return false;

View File

@ -0,0 +1,43 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "poker/visualnovel/PokerNewRoundEvent.hpp"
#include "poker/visualnovel/PokerDealEvent.hpp"
#include "poker/visualnovel/PokerTakeBlindsEvent.hpp"
#include "PokerBetLoopEvent.hpp"
#include "visualnovel/events/VisualNovelTextboxEvent.hpp"
#define POKER_DEAL_EVENT_CARD_COUNT 2
namespace Dawn {
class PokerInitialEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
this
->then(new PokerNewRoundEvent(this->manager))
->then(new VisualNovelTextboxEvent(this->manager, "Round Started"))
->then(new PokerTakeBlindsEvent(this->manager))
->then(new VisualNovelTextboxEvent(this->manager, "Blinds Taken"))
->then(new PokerDealEvent(this->manager))
->then(new VisualNovelTextboxEvent(this->manager, "Cards Dealt"))
->then(new PokerBetLoopEvent(this->manager))
;
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerInitialEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}