diff --git a/src/game/poker/actions/round.c b/src/game/poker/actions/round.c index 2391c20c..10a7610b 100644 --- a/src/game/poker/actions/round.c +++ b/src/game/poker/actions/round.c @@ -32,7 +32,7 @@ void _pokerGameActionRoundOnEnd(queue_t *queue,queueaction_t *action,uint8_t i){ pokerDiscussionQueue(&data); // Deal - cardShuffle(&game->poker.deck, CARD_DECK_SIZE); + cardShuffle(game->poker.deck, CARD_DECK_SIZE); pokerPlayerDealAll(&game->poker, POKER_PLAYER_HAND_SIZE_MAX); // Speak diff --git a/src/poker/bet.c b/src/poker/bet.c new file mode 100644 index 00000000..4fcbb58e --- /dev/null +++ b/src/poker/bet.c @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "bet.h" + +uint8_t pokerPlayerGetRemainingBetter(poker_t *poker) { + uint8_t i, j; + + for(i = 0; i < poker->playerCount; i++) { + j = (i + poker->playerBigBlind + 1) % poker->playerCount; + if(pokerPlayerDoesNeedToBetThisRound(poker, j)) return j; + } + return 0xFF; +} + + +uint8_t pokerPlayerGetNextBetter(poker_t *poker, uint8_t current) { + uint8_t i, j; + + for(i = 0; i < poker->playerCount; i++) { + j = (i + current + 1) % poker->playerCount; + if(pokerPlayerDoesNeedToBetThisRound(poker, j)) return j; + } + return 0xFF; +} + +int32_t pokerPlayerGetCallBet(poker_t *poker, pokerplayer_t *player) { + return pokerGetCallValue(poker) - player->currentBet; +} + +uint8_t pokerPlayerGetRemainingBetterCount(poker_t *poker) { + uint8_t i, count; + count = 0; + for(i = 0; i < poker->playerCount; i++) { + if(!pokerPlayerDoesNeedToBetThisRound(poker, i)) continue; + count++; + } + return count; +} + +void pokerPlayerBetPot( + poker_t *poker, pokerpot_t *pot, uint8_t playerIndex, int32_t chips +) { + pokerplayer_t *player; + player = poker->players + playerIndex; + player->chips -= chips; + player->currentBet += chips; + pot->chips += chips; + player->state |= POKER_PLAYER_STATE_HAS_BET_THIS_ROUND; + pokerPotAddPlayer(pot, playerIndex); +} + +void pokerPlayerBet(poker_t *poker, uint8_t playerIndex, int32_t chips) { + pokerPlayerBetPot( + poker, poker->pots + (poker->potCount - 1), playerIndex, chips + ); +} diff --git a/src/poker/bet.h b/src/poker/bet.h index 10a40b10..1ed12018 100644 --- a/src/poker/bet.h +++ b/src/poker/bet.h @@ -69,4 +69,3 @@ void pokerPlayerBetPot( * @param chips The amount of chips the player is betting. */ void pokerPlayerBet(poker_t *poker, uint8_t playerIndex, int32_t chips); -f \ No newline at end of file diff --git a/src/poker/dealer.c b/src/poker/dealer.c new file mode 100644 index 00000000..572de52d --- /dev/null +++ b/src/poker/dealer.c @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "dealer.h" + +void pokerNewDealer(poker_t *poker) { + pokerSetDealer(poker, poker->playerDealer + 1); +} + +void pokerSetDealer(poker_t *poker, uint8_t dealer) { + uint8_t i, k; + pokerplayer_t *player; + bool foundDealer; + bool foundSmall; + + foundDealer = false; + foundSmall = false; + poker->playerDealer = dealer; + + for(i = 0; i < poker->playerCount; i++) { + k = (dealer + i) % poker->playerCount; + player = poker->players + k; + if(player->state & POKER_PLAYER_STATE_OUT) continue; + if(!foundDealer) { + poker->playerDealer = k; + foundDealer = true; + } else if(!foundSmall) { + poker->playerSmallBlind = k; + foundSmall = true; + } else { + poker->playerBigBlind = k; + break; + } + } +} + +void pokerTurn(poker_t *poker, uint8_t count) { + uint8_t i; + for(i = 0; i < count; i++) { + cardDeal( + poker->deck, &poker->deckSize, poker->community, &poker->communitySize + ); + } +} + +void pokerBurn(poker_t *poker, uint8_t count) { + uint8_t i; + for(i = 0; i < count; i++) { + cardDeal(poker->deck, &poker->deckSize, poker->grave, &poker->graveSize); + } +} + +void pokerPlayerDeal(poker_t *poker, pokerplayer_t *player, uint8_t count) { + uint8_t i; + for(i = 0; i < count; i++) { + cardDeal(poker->deck, &poker->deckSize, player->cards, &player->cardCount); + } +} + +void pokerPlayerDealAll(poker_t *poker, uint8_t count) { + uint8_t i, j; + pokerplayer_t *player; + + for(j = 0; j < count; j++) { + for(i = 0; i < poker->playerCount; i++) { + player = poker->players + i; + + // Can't deal to a player who is folded or out + if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) { + continue; + } + + pokerPlayerDeal(poker, player, 1); + } + } +} diff --git a/src/poker/dealer.h b/src/poker/dealer.h new file mode 100644 index 00000000..d80de48d --- /dev/null +++ b/src/poker/dealer.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "fuck.h" +#include "poker.h" + +/** + * Cycles to the next dealer. This will also select the new small and big blind + * players. + * + * @param poker Poker game to select a new dealer for. + */ +void pokerNewDealer(poker_t *poker); + +/** + * Set a next dealer. This will also select the new small and big blind players. + * This will automatically skip out players if you provide one. + * + * @param poker Poker game to select a new dealer for. + * @param dealer Dealer to become the new dealer. + */ +void pokerSetDealer(poker_t *poker, uint8_t dealer); + +/** + * Turns over cards from the deck onto the table (from the deck into the dealer + * hand) + * + * @param poker Poker game instance. + * @param count Count of cards to deal. + */ +void pokerTurn(poker_t *poker, uint8_t count); + +/** + * Burns a set of cards off the top of the deck into the graveyard. + * + * @param poker Poker game instance. + * @param count Count of cards to burn. + */ +void pokerBurn(poker_t *poker, uint8_t count); + +/** + * Deal a card to a player. + * + * @param poker Poker game instance. + * @param player Poker player to deal to. + * @param count Count of cards to deal to the player. + */ +void pokerPlayerDeal(poker_t *poker, pokerplayer_t *player, uint8_t count); + +/** + * Deal card(s) to every active player. + * + * @param poker Poker game instance. + * @param count Count of cards to deal. + */ +void pokerPlayerDealAll(poker_t *poker, uint8_t count); diff --git a/src/poker/fuck.h b/src/poker/fuck.h new file mode 100644 index 00000000..b26f909c --- /dev/null +++ b/src/poker/fuck.h @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../util/flags.h" +#include "card.h" + +/** Maximum number of players that the game state can support */ +#define POKER_PLAYER_COUNT_MAX 5 + +/** Maximum cards a players' hand can hold */ +#define POKER_PLAYER_HAND_SIZE_MAX 2 + +/** Player States */ +#define POKER_PLAYER_STATE_FOLDED flagDefine(0) +#define POKER_PLAYER_STATE_OUT flagDefine(1) +#define POKER_PLAYER_STATE_HAS_BET_THIS_ROUND flagDefine(2) +#define POKER_PLAYER_STATE_SHOWING flagDefine(3) + +/** Maximum number of cards that the grave can hold */ +#define POKER_GRAVE_SIZE_MAX CARD_DECK_SIZE + +/** Maximum number of community cards */ +#define POKER_COMMUNITY_SIZE_MAX 5 + +/** Maximum number of pots in the poker game. */ +#define POKER_POT_COUNT_MAX POKER_PLAYER_COUNT_MAX + +/** Maximum number of cards a winning state can hold. */ +#define POKER_WINNING_FULL_SIZE (\ + POKER_PLAYER_HAND_SIZE_MAX + POKER_COMMUNITY_SIZE_MAX\ +) + +/** How many cards in the winning set */ +#define POKER_WINNING_SET_SIZE 5 + +/** Winning Types */ +#define POKER_WINNING_TYPE_NULL 0x00 +#define POKER_WINNING_TYPE_ROYAL_FLUSH 0x01 +#define POKER_WINNING_TYPE_STRAIGHT_FLUSH 0x02 +#define POKER_WINNING_TYPE_FOUR_OF_A_KIND 0x03 +#define POKER_WINNING_TYPE_FULL_HOUSE 0x04 +#define POKER_WINNING_TYPE_FLUSH 0x05 +#define POKER_WINNING_TYPE_STRAIGHT 0x06 +#define POKER_WINNING_TYPE_THREE_OF_A_KIND 0x07 +#define POKER_WINNING_TYPE_TWO_PAIR 0x08 +#define POKER_WINNING_TYPE_PAIR 0x09 +#define POKER_WINNING_TYPE_HIGH_CARD 0x0A + +#define POKER_WINNING_CONFIDENCE_ROYAL_FLUSH 1.0f +#define POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH 0.99f +#define POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND 0.9f +#define POKER_WINNING_CONFIDENCE_FULL_HOUSE 0.85f +#define POKER_WINNING_CONFIDENCE_FLUSH 0.8f +#define POKER_WINNING_CONFIDENCE_STRAIGHT 0.7f +#define POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND 0.5f +#define POKER_WINNING_CONFIDENCE_TWO_PAIR 0.4f +#define POKER_WINNING_CONFIDENCE_PAIR 0.2f +#define POKER_WINNING_CONFIDENCE_HIGH_CARD 0.1f + +#define POKER_WORLD_DEALER_INDEX 0x02 +#define POKER_WORLD_HUMAN_INDEX 0x02 + +/** Turn Types */ +#define POKER_TURN_TYPE_OUT 0x00 +#define POKER_TURN_TYPE_FOLD 0x01 +#define POKER_TURN_TYPE_BET 0x02 +#define POKER_TURN_TYPE_CALL 0x03 +#define POKER_TURN_TYPE_ALL_IN 0x04 +#define POKER_TURN_TYPE_CALL_ALL_IN 0x04 +#define POKER_TURN_TYPE_CHECK 0x05 + +#define POKER_TURN_MAX_RAISES 0x02 + +/** How many chips each player has by defautl */ +#define POKER_BET_PLAYER_CHIPS_DEFAULT 1200 +/** The default blind cost for the big blind. */ +#define POKER_BET_BLIND_BIG_DEFAULT 600 +/** The default blind cost for the small blind. (Defaults half big blind) */ +#define POKER_BET_BLIND_SMALL_DEFAULT (POKER_BET_BLIND_BIG_DEFAULT/2) + +/** How many cards are dealt for the flop, turn and river */ +#define POKER_FLOP_CARD_COUNT 3 +#define POKER_TURN_CARD_COUNT 1 +#define POKER_RIVER_CARD_COUNT 1 + +/** How many cards the dealer should burn before dealing the flop */ +#define POKER_FLOP_BURN_COUNT 1 + +typedef struct { + /** Count of chips the player has */ + int32_t chips; + + /** Cards in the players' hand */ + card_t cards[POKER_PLAYER_HAND_SIZE_MAX]; + uint8_t cardCount; + + /** Players current state */ + uint8_t state; + + /** Current bet that the player has done. */ + int32_t currentBet; + + uint8_t timesRaised; +} pokerplayer_t; + +/** Holds information about a player's winning state */ +typedef struct { + /** The full set of both the dealer and player's hand */ + card_t full[POKER_WINNING_FULL_SIZE]; + uint8_t fullSize; + + /** Holds the winning set */ + card_t set[POKER_WINNING_SET_SIZE]; + uint8_t setSize; + + /** Winning Type */ + uint8_t type; + + /** If there was a kicker card it will be here, otherwise -1 for no kicker */ + card_t kicker; +} pokerplayerwinning_t; + +typedef struct { + /** Current pot of chips */ + int32_t chips; + /** Players who are participating in the pot */ + uint8_t players[POKER_PLAYER_COUNT_MAX]; + uint8_t playerCount; +} pokerpot_t; + +typedef struct { + /** What type of action the turn is */ + uint8_t type; + /** How many chips they did in their turn (if applicable) */ + int32_t chips; + /** How confident the AI is about their turn. 0 = none, 1 = full */ + float confidence; +} pokerturn_t; + +typedef struct { + /** Current Card Deck (Dealer) */ + card_t deck[CARD_DECK_SIZE]; + uint8_t deckSize; + + /** Card grave (where spent cards go when burned */ + card_t grave[POKER_GRAVE_SIZE_MAX]; + uint8_t graveSize; + + /** Dealer Hand */ + card_t community[POKER_COMMUNITY_SIZE_MAX]; + uint8_t communitySize; + + /** List of pots in the game (side pots) */ + pokerpot_t pots[POKER_POT_COUNT_MAX]; + uint8_t potCount; + + /** Players within the game */ + pokerplayer_t players[POKER_PLAYER_COUNT_MAX]; + uint8_t playerCount; + + /** Index of the dealer player */ + uint8_t playerDealer; + /** Index of the small blind player */ + uint8_t playerSmallBlind; + /** Index of the big blind player */ + uint8_t playerBigBlind; + + /** Which player is the current active better ? */ + uint8_t better; + + /** Size of the small blind */ + int32_t blindSmall; + /** Size of the big blind */ + int32_t blindBig; +} poker_t; \ No newline at end of file diff --git a/src/poker/player.c b/src/poker/player.c new file mode 100644 index 00000000..f0b1fd18 --- /dev/null +++ b/src/poker/player.c @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "player.h" + +uint8_t pokerPlayerAdd(poker_t *poker) { + pokerplayer_t *player; + uint8_t i = poker->playerCount++; + + player = poker->players + i; + player->cardCount = 0; + player->chips = 0; + player->currentBet = 0; + player->state = POKER_PLAYER_STATE_OUT; + player->timesRaised = 0; + + return i; +} + +void pokerPlayerChipsAdd(pokerplayer_t *player, int32_t chips) { + player->chips += chips; + if(player->chips > 0) { + player->state = flagOff(player->state, POKER_PLAYER_STATE_OUT); + } +} + +bool pokerPlayerDoesNeedToBetThisRound(poker_t *poker, uint8_t playerIndex) { + pokerplayer_t *player; + player = poker->players + playerIndex; + if(player->state & POKER_PLAYER_STATE_FOLDED) return false; + if(player->chips <= 0) return false; + if(!(player->state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND)) return true; + if(player->currentBet < pokerGetCallValue(poker)) return true; + return false; +} + +bool pokerPlayerCanCheck(poker_t *poker, pokerplayer_t *player) { + return pokerGetCallValue(poker) <= player->currentBet; +} \ No newline at end of file diff --git a/src/poker/player.h b/src/poker/player.h new file mode 100644 index 00000000..aaae1070 --- /dev/null +++ b/src/poker/player.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "fuck.h" +#include "poker.h" + +/** + * Add a player to the game. The player starts in an out state (with no chips). + * + * @param poker Poker game instance to add to. + * @return Player index. + */ +uint8_t pokerPlayerAdd(poker_t *poker); + +/** + * Add chips to a player. This will also update their state if they were out + * to ensure that they are no longer. + * + * @param player Player to add chips to. + * @param chips Amount of chips to add. + */ +void pokerPlayerChipsAdd(pokerplayer_t *player, int32_t chips); + +/** + * Checks a players' state and decided whether or not the players still needs to + * bet for the current round. This will check both whether the player has bet in + * the current round yet, and whether or not they have met the call value of the + * active pot. + * + * @param poker Poker game instance. + * @param playerIndex Player index to check. + * @return + */ +bool pokerPlayerDoesNeedToBetThisRound(poker_t *poker, uint8_t playerIndex); + +/** + * Determine whether or not a player CAN check, given the current max bet and + * the players current bet. + * + * @param poker Poker game instance. + * @param player Player to check. + * @return True if the player can check, false if they need to call first. + */ +bool pokerPlayerCanCheck(poker_t *poker, pokerplayer_t *player); \ No newline at end of file diff --git a/src/poker/poker.c b/src/poker/poker.c index 2077d9bf..32bf5ec3 100644 --- a/src/poker/poker.c +++ b/src/poker/poker.c @@ -69,37 +69,6 @@ void pokerResetBettingRound(poker_t *poker) { poker->better = pokerPlayerGetRemainingBetter(poker); } -void pokerNewDealer(poker_t *poker) { - pokerSetDealer(poker, poker->playerDealer + 1); -} - -void pokerSetDealer(poker_t *poker, uint8_t dealer) { - uint8_t i, k; - pokerplayer_t *player; - bool foundDealer; - bool foundSmall; - - foundDealer = false; - foundSmall = false; - poker->playerDealer = dealer; - - for(i = 0; i < poker->playerCount; i++) { - k = (dealer + i) % poker->playerCount; - player = poker->players + k; - if(player->state & POKER_PLAYER_STATE_OUT) continue; - if(!foundDealer) { - poker->playerDealer = k; - foundDealer = true; - } else if(!foundSmall) { - poker->playerSmallBlind = k; - foundSmall = true; - } else { - poker->playerBigBlind = k; - break; - } - } -} - void pokerTakeBlinds(poker_t *poker, int32_t small, int32_t big) { pokerPlayerBet(poker, poker->playerSmallBlind, small); pokerPlayerBet(poker, poker->playerBigBlind, big); @@ -117,124 +86,10 @@ int32_t pokerGetCallValue(poker_t *poker) { } // Pot functions -uint8_t pokerPotAdd(poker_t *poker) { - pokerpot_t *pot; - uint8_t i = poker->potCount++; - pot = poker->pots + i; - pot->chips = 0; - pot->playerCount = 0; - return i; -} - -bool pokerPotHasPlayer(pokerpot_t *pot, uint8_t playerIndex) { - return arrayContains( - sizeof(uint8_t), pot->players, pot->playerCount, &playerIndex - ); -} - -void pokerPotAddPlayer(pokerpot_t *pot, uint8_t playerIndex) { - if(pokerPotHasPlayer(pot, playerIndex)) return; - pot->players[pot->playerCount++] = playerIndex; -} // Dealer Functions -void pokerTurn(poker_t *poker, uint8_t count) { - uint8_t i; - for(i = 0; i < count; i++) { - cardDeal( - poker->deck, &poker->deckSize, poker->community, &poker->communitySize - ); - } -} - -void pokerBurn(poker_t *poker, uint8_t count) { - uint8_t i; - for(i = 0; i < count; i++) { - cardDeal(poker->deck, &poker->deckSize, poker->grave, &poker->graveSize); - } -} - // Player Functions -uint8_t pokerPlayerAdd(poker_t *poker) { - pokerplayer_t *player; - uint8_t i = poker->playerCount++; - - player = poker->players + i; - player->cardCount = 0; - player->chips = 0; - player->currentBet = 0; - player->state = POKER_PLAYER_STATE_OUT; - player->timesRaised = 0; - - return i; -} - -void pokerPlayerDeal(poker_t *poker, pokerplayer_t *player, uint8_t count) { - uint8_t i; - for(i = 0; i < count; i++) { - cardDeal(poker->deck, &poker->deckSize, player->cards, &player->cardCount); - } -} - -void pokerPlayerChipsAdd(pokerplayer_t *player, int32_t chips) { - player->chips += chips; - if(player->chips > 0) { - player->state = flagOff(player->state, POKER_PLAYER_STATE_OUT); - } -} - -void pokerPlayerDealAll(poker_t *poker, uint8_t count) { - uint8_t i, j; - pokerplayer_t *player; - - for(j = 0; j < count; j++) { - for(i = 0; i < poker->playerCount; i++) { - player = poker->players + i; - - // Can't deal to a player who is folded or out - if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) { - continue; - } - - pokerPlayerDeal(poker, player, 1); - } - } -} - -bool pokerPlayerDoesNeedToBetThisRound(poker_t *poker, uint8_t playerIndex) { - pokerplayer_t *player; - player = poker->players + playerIndex; - if(player->state & POKER_PLAYER_STATE_FOLDED) return false; - if(player->chips <= 0) return false; - if(!(player->state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND)) return true; - if(player->currentBet < pokerGetCallValue(poker)) return true; - return false; -} - -uint8_t pokerPlayerGetRemainingBetter(poker_t *poker) { - uint8_t i, j; - - for(i = 0; i < poker->playerCount; i++) { - j = (i + poker->playerBigBlind + 1) % poker->playerCount; - if(pokerPlayerDoesNeedToBetThisRound(poker, j)) return j; - } - return 0xFF; -} - -uint8_t pokerPlayerGetNextBetter(poker_t *poker, uint8_t current) { - uint8_t i, j; - - for(i = 0; i < poker->playerCount; i++) { - j = (i + current + 1) % poker->playerCount; - if(pokerPlayerDoesNeedToBetThisRound(poker, j)) return j; - } - return 0xFF; -} - -int32_t pokerPlayerGetCallBet(poker_t *poker, pokerplayer_t *player) { - return pokerGetCallValue(poker) - player->currentBet; -} uint8_t pokerInRoundGetCount(poker_t *poker) { uint8_t i, count; @@ -252,252 +107,7 @@ uint8_t pokerInRoundGetCount(poker_t *poker) { return count; } -uint8_t pokerPlayerGetRemainingBetterCount(poker_t *poker) { - uint8_t i, count; - count = 0; - for(i = 0; i < poker->playerCount; i++) { - if(!pokerPlayerDoesNeedToBetThisRound(poker, i)) continue; - count++; - } - return count; -} - // Betting -void pokerPlayerBetPot( - poker_t *poker, pokerpot_t *pot, uint8_t playerIndex, int32_t chips -) { - pokerplayer_t *player; - player = poker->players + playerIndex; - player->chips -= chips; - player->currentBet += chips; - pot->chips += chips; - player->state |= POKER_PLAYER_STATE_HAS_BET_THIS_ROUND; - pokerPotAddPlayer(pot, playerIndex); -} - -void pokerPlayerBet(poker_t *poker, uint8_t playerIndex, int32_t chips) { - pokerPlayerBetPot( - poker, poker->pots + (poker->potCount - 1), playerIndex, chips - ); -} - -bool pokerPlayerCanCheck(poker_t *poker, pokerplayer_t *player) { - return pokerGetCallValue(poker) <= player->currentBet; -} - -pokerturn_t pokerTurnFold(poker_t *poker, uint8_t player) { - return (pokerturn_t){ - .chips = 0, - .confidence = 1, - .type = POKER_TURN_TYPE_FOLD - }; -} - -pokerturn_t pokerTurnBet(poker_t *poker, uint8_t playerIndex, int32_t chips) { - pokerturn_t turn; - pokerplayer_t *player; - int32_t i; - player = poker->players + playerIndex; - turn.confidence = 1; - - if(chips == 0) { - turn.type = POKER_TURN_TYPE_CHECK; - turn.chips = 0; - } else if(player->chips <= chips) { - turn.chips = player->chips; - turn.type = POKER_TURN_TYPE_ALL_IN; - } else { - turn.chips = chips; - turn.type = POKER_TURN_TYPE_BET; - i = pokerGetCallValue(poker); - - if(chips == (i - player->currentBet)) { - turn.type = POKER_TURN_TYPE_CALL; - } - } - - return turn; -} - -pokerturn_t pokerTurnGetForPlayer(poker_t *poker, uint8_t playerIndex) { - int32_t random, maxBet, bluffBet, callBet; - float confidence, expectedGain, potOdds; - bool isBluff; - int32_t amount; - pokerplayer_t *player; - pokerplayerwinning_t winning; - uint8_t i, cardNumber0, cardNumber1, suitNumber0, suitNumber1; - pokerturn_t turn; - - player = poker->players + playerIndex; - - // Can the player do anything? - if(player->state & (POKER_PLAYER_STATE_FOLDED | POKER_PLAYER_STATE_OUT)) { - turn.type = POKER_TURN_TYPE_OUT; - return turn; - } - - // The following logic is heavily inspired by; - // https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c - // But with some changes and smarts added by me. The original source code will - // essentially just run a crap tun of simulated games and get the times that - // they are expected to win from those games, but I'm just going to use the - // odds of the winning hand. - - // Is this preflop? - if(poker->communitySize == 0 && player->cardCount >= 2) { - // Get the hand weight - cardNumber0 = cardGetNumber(player->cards[0]); - cardNumber1 = cardGetNumber(player->cards[1]); - suitNumber0 = cardGetSuit(player->cards[0]); - suitNumber1 = cardGetSuit(player->cards[1]); - - // Get delta between cards - i = mathAbs(cardNumber0 - cardNumber1); - - // Get card weight - confidence = (float)cardNumber0 + (float)cardNumber1; - if(cardNumber0 == cardNumber1) {// Pairs - confidence += 6; - } else if(suitNumber0 == suitNumber1) {// Same suit - confidence += 4; - } - - // Get difference from cards for guessing flush - if(i > 4) { - confidence -= 4; - } else if(i > 2) { - confidence -= i; - } - - // Get the confidence delta 0-1 - confidence = confidence / 30.0f; - } else { - // Simulate my hand being the winning hand, use that as the confidence - pokerWinnerGetForPlayer(poker, player, &winning); - confidence = pokerWinnerGetTypeConfidence(winning.type); - } - - // Now we know how confident the AI is, let's put a chip value to that weight - // How many chips to call? - callBet = pokerPlayerGetCallBet(poker, player); - - // Do they need chips to call, or is it possible to check? - if(callBet > 0) { - potOdds = (float)callBet / ( - (float)callBet + (float)pokerPlayerGetPotChipsSum(poker, playerIndex) - ); - } else { - potOdds = 1.0f / (float)pokerPlayerGetRemainingBetterCount(poker); - } - - // Now determine the expected ROI - expectedGain = confidence / potOdds; - - // Now get a random 0-100 - random = randInt32() % 100; - - // Determine the max bet that the AI is willing to make - maxBet = (int32_t)((float)player->chips / 1.75f) - (random / 2); - maxBet -= callBet; - - // Determine what's a good bluff bet. - bluffBet = random * maxBet / 100 / 2; - - // Now prep the output - isBluff = false; - amount = 0; - - // Now the actual AI can happen. This is basically a weight to confidence - // ratio. The higher the gains and the confidence then the more likely the AI - // is to betting. There are also bluff chances within here. - if(expectedGain < 0.8 && confidence < 0.8) { - if(random < 95) { - amount = 0; - } else { - amount = bluffBet; - isBluff = true; - } - } else if ((expectedGain < 1.0 && confidence < 0.85) || confidence < 0.1) { - if (random < 80) { - amount = 0; - } else if(random < 5) { - amount = callBet; - isBluff = true; - } else { - amount = bluffBet; - isBluff = true; - } - } else if ((expectedGain < 1.3 && confidence < 0.9) || confidence < 0.5) { - if (random < 60 || confidence < 0.5) { - amount = callBet; - } else { - amount = maxBet; - } - } else if (confidence < 0.95 || poker->communitySize < 0x04) { - if(random < 20) { - amount = callBet; - } else { - amount = maxBet; - } - } else { - amount = (player->chips - callBet) * 9 / 10; - } - - // If this is the first round... make it a lot less likely I'll bet - if(poker->communitySize == 0x00 && amount > callBet) { - if(random > 5) amount = callBet; - } - - // Did we actually bet? - if(amount > 0) { - printf("AI is betting %i chips, bluff: %i\n", amount, isBluff); - - // Let's not get caught in a raising loop with AI. - if(player->timesRaised >= POKER_TURN_MAX_RAISES) { - amount = callBet; - } - - amount = mathMax(amount, callBet); - turn = pokerTurnBet(poker, playerIndex, amount); - turn.confidence = confidence; - } else if(pokerPlayerCanCheck(poker, player)) { - turn = pokerTurnBet(poker, playerIndex, 0); - turn.confidence = 1; - } else { - turn = pokerTurnFold(poker, playerIndex); - turn.confidence = 1 - confidence; - } - - return turn; -} - -void pokerTurnAction(poker_t *poker, uint8_t playerIndex, pokerturn_t turn) { - pokerplayer_t *player; - player = poker->players + playerIndex; - - switch(turn.type) { - case POKER_TURN_TYPE_BET: - case POKER_TURN_TYPE_CALL: - case POKER_TURN_TYPE_ALL_IN: - pokerPlayerBet(poker, playerIndex, turn.chips); - player->timesRaised++; - break; - - case POKER_TURN_TYPE_CHECK: - pokerPlayerBet(poker, playerIndex, 0); - player->timesRaised = 0; - break; - - case POKER_TURN_TYPE_FOLD: - player->state |= POKER_PLAYER_STATE_FOLDED; - player->timesRaised = 0; - break; - } - - player->state |= POKER_PLAYER_STATE_HAS_BET_THIS_ROUND; -} - int32_t pokerPlayerGetPotChipsSum(poker_t *poker, uint8_t playerIndex) { int32_t count; uint8_t i; @@ -529,396 +139,3 @@ void pokerHandGetFull( cards[i+poker->communitySize] = player->cards[i]; } } - -void pokerWinnerFillRemaining(pokerplayerwinning_t *winning) { - uint8_t i, highest, current; - card_t highestCard, currentCard; - - // Set the kicker - winning->kicker = 0xFF; - - // Fill the remaining cards - while(winning->setSize < POKER_WINNING_SET_SIZE) { - highest = 0xFF; - - for(i = 0; i < winning->fullSize; i++) { - currentCard = winning->full[i]; - if(cardContains(winning->set, winning->setSize, currentCard) != -1) { - continue; - } - - if(highest == 0xFF) { - highestCard = currentCard; - highest = cardGetNumber(highestCard); - } else { - current = cardGetNumber(currentCard); - if(current != CARD_ACE && current < highest) continue; - highestCard = currentCard; - highest = current; - } - } - - if(highest == 0xFF) break; - winning->set[winning->setSize++] = highestCard; - } - cardHandSort(winning->set, winning->setSize); -} - -void pokerWinnerGetForPlayer( - poker_t *poker, pokerplayer_t *player, pokerplayerwinning_t *winning -) { - uint8_t i, j, l; - int32_t index; - card_t card; - uint8_t number, suit, pairCount; - int32_t pairs[CARD_SUIT_COUNT]; - - // Get the full poker hand (should be a 7 card hand, but MAY not be) - winning->fullSize = poker->communitySize + player->cardCount; - pokerHandGetFull(poker, player, winning->full); - cardHandSort(winning->full, winning->fullSize); - - // Reset the winning status. - winning->setSize = 0; - - //////////////////////// Now look for the winning set //////////////////////// - - // Royal / Straight Flush - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - if(number < CARD_FIVE) continue; - - suit = cardGetSuit(card); - winning->setSize = 1; - - // Now look for the matching cards (Reverse order to order from A to 10) - for(j = 1; j <= 4; j++) { - l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. - index = cardContains(winning->full, winning->fullSize, cardGet(l, suit)); - if(index == -1) break; - winning->set[j] = winning->full[index]; - winning->setSize++; - } - - // Check if has all necessary cards. - if(winning->setSize < POKER_WINNING_SET_SIZE) continue; - - // Add self to array - winning->set[0] = winning->full[i]; - winning->type = ( - number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH : - POKER_WINNING_TYPE_STRAIGHT_FLUSH - ); - pokerWinnerFillRemaining(winning); - return; - } - - // Four of a kind. - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - if(pairCount < CARD_SUIT_COUNT) continue; - - winning->setSize = pairCount; - for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; - winning->type = POKER_WINNING_TYPE_FOUR_OF_A_KIND; - pokerWinnerFillRemaining(winning); - return; - } - - // Full House - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - // Check we haven't already added this card. - card = winning->full[i]; - if(cardContains(winning->set, winning->setSize, card) != -1) continue; - - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - - // Did we find either two pair or three pair? - if(pairCount != 2 && pairCount != 3) continue; - if(winning->setSize == 3) pairCount = 2;//Clamp to 5 max. - - // Copy found pairs. - for(j = 0; j < pairCount; j++) { - winning->set[winning->setSize + j] = winning->full[pairs[j]]; - } - winning->setSize += pairCount; - - // Winned? - if(winning->setSize != POKER_WINNING_SET_SIZE) continue; - winning->type = POKER_WINNING_TYPE_FULL_HOUSE; - pokerWinnerFillRemaining(winning); - return; - } - - // Flush (5 same suit) - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - suit = cardGetSuit(card); - winning->setSize = 1; - for(j = i+1; j < winning->fullSize; j++) { - if(cardGetSuit(winning->full[j]) != suit) continue; - winning->set[winning->setSize++] = winning->full[j]; - if(winning->setSize == POKER_WINNING_SET_SIZE) break; - } - if(winning->setSize < POKER_WINNING_SET_SIZE) continue; - winning->set[0] = winning->full[i]; - winning->type = POKER_WINNING_TYPE_FLUSH; - pokerWinnerFillRemaining(winning); - return; - } - - // Straight (sequence any suit) - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - if(number < CARD_FIVE) continue; - winning->setSize = 1; - - for(j = 1; j <= 4; j++) { - l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. - index = cardContainsNumber(winning->full, winning->fullSize, l); - if(index == -1) break; - winning->set[j] = winning->full[index]; - winning->setSize++; - } - - // Check if has all necessary cards. - if(winning->setSize < POKER_WINNING_SET_SIZE) continue; - winning->set[0] = winning->full[i]; - winning->type = POKER_WINNING_TYPE_STRAIGHT; - pokerWinnerFillRemaining(winning); - return; - } - - // Three of a kind - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - if(pairCount != 3) continue; - - winning->setSize = pairCount; - for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; - winning->type = POKER_WINNING_TYPE_THREE_OF_A_KIND; - pokerWinnerFillRemaining(winning); - return; - } - - // Two Pair - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i];// Check we haven't already added this card. - if(cardContains(winning->set, winning->setSize, card) != -1) { - continue; - } - - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - if(pairCount != 2) continue; - - for(j = 0; j < pairCount; j++) { - winning->set[winning->setSize+j] = winning->full[pairs[j]]; - } - winning->setSize += pairCount; - if(winning->setSize != 4) continue; - - winning->type = POKER_WINNING_TYPE_TWO_PAIR; - pokerWinnerFillRemaining(winning); - return; - } - - // Pair - if(winning->setSize == 2) { - winning->type = POKER_WINNING_TYPE_PAIR; - pokerWinnerFillRemaining(winning); - return; - } - - // High card - winning->setSize = 0; - pokerWinnerFillRemaining(winning); - winning->type = POKER_WINNING_TYPE_HIGH_CARD; - - return; -} - -card_t pokerWinnerCompare( - pokerplayerwinning_t *left, pokerplayerwinning_t *right -) { - uint8_t i, number; - card_t card; - int32_t index; - uint8_t countCardsSame; - - card_t highCardLeft, highCardRight; - uint8_t highNumberLeft, highNumberRight; - - highNumberLeft = 0xFF; - highNumberRight = 0xFF; - countCardsSame = 0; - - - for(i = 0; i < left->setSize; i++) { - card = left->set[i]; - number = cardGetNumber(card); - if(highNumberLeft != 0xFF && number < highNumberLeft) continue;// Quick check - - // Check if this number is within the other hand or not - index = cardContainsNumber(right->set, right->setSize, 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 = cardContains(right->set, right->setSize, card); - - // Exact card match - if(index != -1) { - countCardsSame++; - continue; - } - // Not exact card match.. ? - } - - if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { - highNumberLeft = number; - highCardLeft = card; - } - } - - for(i = 0; i < right->setSize; i++) { - card = right->set[i]; - number = cardGetNumber(card); - if(highNumberRight != 0xFF && number < highNumberRight) continue; - - index = cardContainsNumber(left->set, left->setSize, number); - if(index != -1) { - index = cardContains(left->set, left->setSize, card); - if(index != -1) continue; - } - - if(highNumberRight == 0xFF||number == CARD_ACE||highNumberRight < number) { - highNumberRight = number; - highCardRight = card; - } - } - - - if(countCardsSame == left->setSize) { - for(i = 0; i < left->setSize; i++) { - card = left->set[i]; - number = cardGetNumber(card); - if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { - highNumberLeft = number; - highCardLeft = card; - } - } - return highCardLeft; - } - - if(highCardLeft == 0xFF) return 0xFF; - if(highNumberLeft < highNumberRight) return 0xFF; - return highCardLeft;//Greater or Equal to. -} - -void pokerWinnerDetermineForPot( - poker_t *poker, - pokerpot_t *pot, - pokerplayerwinning_t winners[POKER_PLAYER_COUNT_MAX], - uint8_t winnerPlayers[POKER_PLAYER_COUNT_MAX], - uint8_t *winnerCount, - uint8_t participants[POKER_PLAYER_COUNT_MAX], - uint8_t *participantCount -) { - uint8_t i, j, countPlayers, countWinners, number, highNumber; - pokerplayerwinning_t *left, *right; - pokerplayer_t *player; - card_t card, highCard; - bool isWinner; - - countPlayers = 0; - countWinners = 0; - highCard = 0xFF; - - // Get participating players and their hands. - for(i = 0; i < pot->playerCount; i++) { - player = poker->players + pot->players[i]; - if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) { - continue; - } - - participants[countPlayers] = pot->players[i]; - pokerWinnerGetForPlayer(poker, player, winners + countPlayers++); - } - - // Compare participating players - for(i = 0; i < countPlayers; i++) { - left = winners + i; - isWinner = true; - highNumber = 0xFF; - - for(j = 0; j < countPlayers; j++) { - if(i == j) continue; - right = winners + j; - - // Am I the better hand / Is it the better hand? - if(left->type < right->type) continue; - if(left->type > right->type) { - isWinner = false; - break; - } - - // Equal, compare hands. - card = pokerWinnerCompare(left, right); - if(card == 0xFF) { - isWinner = false; - break; - } - - // Determine high card. - number = cardGetNumber(card); - if(highNumber == 0xFF || number == CARD_ACE || number > highNumber) { - highCard = card; - highNumber = number; - } - } - - if(!isWinner) continue; - left->kicker = highCard; - winnerPlayers[countWinners++] = participants[i]; - } - - *participantCount = countPlayers; - *winnerCount = countWinners; -} - -float pokerWinnerGetTypeConfidence(uint8_t type) { - switch(type) { - case POKER_WINNING_TYPE_ROYAL_FLUSH: - return POKER_WINNING_CONFIDENCE_ROYAL_FLUSH; - case POKER_WINNING_TYPE_STRAIGHT_FLUSH: - return POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH; - case POKER_WINNING_TYPE_FOUR_OF_A_KIND: - return POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND; - case POKER_WINNING_TYPE_FULL_HOUSE: - return POKER_WINNING_CONFIDENCE_FULL_HOUSE; - case POKER_WINNING_TYPE_FLUSH: - return POKER_WINNING_CONFIDENCE_FLUSH; - case POKER_WINNING_TYPE_STRAIGHT: - return POKER_WINNING_CONFIDENCE_STRAIGHT; - case POKER_WINNING_TYPE_THREE_OF_A_KIND: - return POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND; - case POKER_WINNING_TYPE_TWO_PAIR: - return POKER_WINNING_CONFIDENCE_TWO_PAIR; - case POKER_WINNING_TYPE_PAIR: - return POKER_WINNING_CONFIDENCE_PAIR; - default: - return POKER_WINNING_CONFIDENCE_HIGH_CARD; - } -} \ No newline at end of file diff --git a/src/poker/poker.h b/src/poker/poker.h index 0a64559e..6a23f949 100644 --- a/src/poker/poker.h +++ b/src/poker/poker.h @@ -11,175 +11,14 @@ #include "../util/array.h" #include "../util/math.h" #include "card.h" +#include "dealer.h" +#include "fuck.h" +#include "player.h" +#include "pot.h" +#include "turn.h" +#include "winner.h" -/** Maximum number of players that the game state can support */ -#define POKER_PLAYER_COUNT_MAX 5 -/** Maximum cards a players' hand can hold */ -#define POKER_PLAYER_HAND_SIZE_MAX 2 - -/** Player States */ -#define POKER_PLAYER_STATE_FOLDED flagDefine(0) -#define POKER_PLAYER_STATE_OUT flagDefine(1) -#define POKER_PLAYER_STATE_HAS_BET_THIS_ROUND flagDefine(2) -#define POKER_PLAYER_STATE_SHOWING flagDefine(3) - -/** Maximum number of cards that the grave can hold */ -#define POKER_GRAVE_SIZE_MAX CARD_DECK_SIZE - -/** Maximum number of community cards */ -#define POKER_COMMUNITY_SIZE_MAX 5 - -/** Maximum number of pots in the poker game. */ -#define POKER_POT_COUNT_MAX POKER_PLAYER_COUNT_MAX - -/** Maximum number of cards a winning state can hold. */ -#define POKER_WINNING_FULL_SIZE (\ - POKER_PLAYER_HAND_SIZE_MAX + POKER_COMMUNITY_SIZE_MAX\ -) - -/** How many cards in the winning set */ -#define POKER_WINNING_SET_SIZE 5 - -/** Winning Types */ -#define POKER_WINNING_TYPE_NULL 0x00 -#define POKER_WINNING_TYPE_ROYAL_FLUSH 0x01 -#define POKER_WINNING_TYPE_STRAIGHT_FLUSH 0x02 -#define POKER_WINNING_TYPE_FOUR_OF_A_KIND 0x03 -#define POKER_WINNING_TYPE_FULL_HOUSE 0x04 -#define POKER_WINNING_TYPE_FLUSH 0x05 -#define POKER_WINNING_TYPE_STRAIGHT 0x06 -#define POKER_WINNING_TYPE_THREE_OF_A_KIND 0x07 -#define POKER_WINNING_TYPE_TWO_PAIR 0x08 -#define POKER_WINNING_TYPE_PAIR 0x09 -#define POKER_WINNING_TYPE_HIGH_CARD 0x0A - -#define POKER_WINNING_CONFIDENCE_ROYAL_FLUSH 1.0f -#define POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH 0.99f -#define POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND 0.9f -#define POKER_WINNING_CONFIDENCE_FULL_HOUSE 0.85f -#define POKER_WINNING_CONFIDENCE_FLUSH 0.8f -#define POKER_WINNING_CONFIDENCE_STRAIGHT 0.7f -#define POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND 0.5f -#define POKER_WINNING_CONFIDENCE_TWO_PAIR 0.4f -#define POKER_WINNING_CONFIDENCE_PAIR 0.2f -#define POKER_WINNING_CONFIDENCE_HIGH_CARD 0.1f - -#define POKER_WORLD_DEALER_INDEX 0x02 -#define POKER_WORLD_HUMAN_INDEX 0x02 - -/** Turn Types */ -#define POKER_TURN_TYPE_OUT 0x00 -#define POKER_TURN_TYPE_FOLD 0x01 -#define POKER_TURN_TYPE_BET 0x02 -#define POKER_TURN_TYPE_CALL 0x03 -#define POKER_TURN_TYPE_ALL_IN 0x04 -#define POKER_TURN_TYPE_CALL_ALL_IN 0x04 -#define POKER_TURN_TYPE_CHECK 0x05 - -#define POKER_TURN_MAX_RAISES 0x02 - -/** How many chips each player has by defautl */ -#define POKER_BET_PLAYER_CHIPS_DEFAULT 1200 -/** The default blind cost for the big blind. */ -#define POKER_BET_BLIND_BIG_DEFAULT 600 -/** The default blind cost for the small blind. (Defaults half big blind) */ -#define POKER_BET_BLIND_SMALL_DEFAULT (POKER_BET_BLIND_BIG_DEFAULT/2) - -/** How many cards are dealt for the flop, turn and river */ -#define POKER_FLOP_CARD_COUNT 3 -#define POKER_TURN_CARD_COUNT 1 -#define POKER_RIVER_CARD_COUNT 1 -/** How many cards the dealer should burn before dealing the flop */ -#define POKER_FLOP_BURN_COUNT 1 - -typedef struct { - /** Count of chips the player has */ - int32_t chips; - - /** Cards in the players' hand */ - card_t cards[POKER_PLAYER_HAND_SIZE_MAX]; - uint8_t cardCount; - - /** Players current state */ - uint8_t state; - - /** Current bet that the player has done. */ - int32_t currentBet; - - uint8_t timesRaised; -} pokerplayer_t; - -/** Holds information about a player's winning state */ -typedef struct { - /** The full set of both the dealer and player's hand */ - card_t full[POKER_WINNING_FULL_SIZE]; - uint8_t fullSize; - - /** Holds the winning set */ - card_t set[POKER_WINNING_SET_SIZE]; - uint8_t setSize; - - /** Winning Type */ - uint8_t type; - - /** If there was a kicker card it will be here, otherwise -1 for no kicker */ - card_t kicker; -} pokerplayerwinning_t; - -typedef struct { - /** Current pot of chips */ - int32_t chips; - /** Players who are participating in the pot */ - uint8_t players[POKER_PLAYER_COUNT_MAX]; - uint8_t playerCount; -} pokerpot_t; - -typedef struct { - /** What type of action the turn is */ - uint8_t type; - /** How many chips they did in their turn (if applicable) */ - int32_t chips; - /** How confident the AI is about their turn. 0 = none, 1 = full */ - float confidence; -} pokerturn_t; - -typedef struct { - /** Current Card Deck (Dealer) */ - card_t deck[CARD_DECK_SIZE]; - uint8_t deckSize; - - /** Card grave (where spent cards go when burned */ - card_t grave[POKER_GRAVE_SIZE_MAX]; - uint8_t graveSize; - - /** Dealer Hand */ - card_t community[POKER_COMMUNITY_SIZE_MAX]; - uint8_t communitySize; - - /** List of pots in the game (side pots) */ - pokerpot_t pots[POKER_POT_COUNT_MAX]; - uint8_t potCount; - - /** Players within the game */ - pokerplayer_t players[POKER_PLAYER_COUNT_MAX]; - uint8_t playerCount; - - /** Index of the dealer player */ - uint8_t playerDealer; - /** Index of the small blind player */ - uint8_t playerSmallBlind; - /** Index of the big blind player */ - uint8_t playerBigBlind; - - /** Which player is the current active better ? */ - uint8_t better; - - /** Size of the small blind */ - int32_t blindSmall; - /** Size of the big blind */ - int32_t blindBig; -} poker_t; - +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Fucked if I know /** * Initialize the poker game instance. @@ -211,23 +50,6 @@ void pokerResetRound(poker_t *poker); */ void pokerResetBettingRound(poker_t *poker); -/** - * Cycles to the next dealer. This will also select the new small and big blind - * players. - * - * @param poker Poker game to select a new dealer for. - */ -void pokerNewDealer(poker_t *poker); - -/** - * Set a next dealer. This will also select the new small and big blind players. - * This will automatically skip out players if you provide one. - * - * @param poker Poker game to select a new dealer for. - * @param dealer Dealer to become the new dealer. - */ -void pokerSetDealer(poker_t *poker, uint8_t dealer); - /** * Take the blinds from the blind players. * @@ -245,129 +67,6 @@ void pokerTakeBlinds(poker_t *poker, int32_t small, int32_t big); */ int32_t pokerGetCallValue(poker_t *poker); -/** - * Adds a new pot to the poker game instance. - * - * @param poker Poker game instance to add a pot to. - * @return The index of the pot within the array of pots. - */ -uint8_t pokerPotAdd(poker_t *poker); - -/** - * Check whether or not the given player is part of the given pot. - * - * @param pot Pot to check. - * @param playerIndex Player index to see if within the pot or not. - * @return True if in the pot, otherwise false. - */ -bool pokerPotHasPlayer(pokerpot_t *pot, uint8_t playerIndex); - -/** - * Add a player to a pot. This will not let you add the same player to the pot - * twice. - * - * @param pot Pot to add to. - * @param playerIndex Players' index to add to the pot. - */ -void pokerPotAddPlayer(pokerpot_t *pot, uint8_t playerIndex); - - -/** - * Turns over cards from the deck onto the table (from the deck into the dealer - * hand) - * - * @param poker Poker game instance. - * @param count Count of cards to deal. - */ -void pokerTurn(poker_t *poker, uint8_t count); - - -/** - * Burns a set of cards off the top of the deck into the graveyard. - * - * @param poker Poker game instance. - * @param count Count of cards to burn. - */ -void pokerBurn(poker_t *poker, uint8_t count); - -/** - * Add a player to the game. The player starts in an out state (with no chips). - * - * @param poker Poker game instance to add to. - * @return Player index. - */ -uint8_t pokerPlayerAdd(poker_t *poker); - - -/** - * Deal a card to a player. - * - * @param poker Poker game instance. - * @param player Poker player to deal to. - * @param count Count of cards to deal to the player. - */ -void pokerPlayerDeal(poker_t *poker, pokerplayer_t *player, uint8_t count); - -/** - * Add chips to a player. This will also update their state if they were out - * to ensure that they are no longer. - * - * @param player Player to add chips to. - * @param chips Amount of chips to add. - */ -void pokerPlayerChipsAdd(pokerplayer_t *player, int32_t chips); - -/** - * Deal card(s) to every active player. - * - * @param poker Poker game instance. - * @param count Count of cards to deal. - */ -void pokerPlayerDealAll(poker_t *poker, uint8_t count); - -/** - * Checks a players' state and decided whether or not the players still needs to - * bet for the current round. This will check both whether the player has bet in - * the current round yet, and whether or not they have met the call value of the - * active pot. - * - * @param poker Poker game instance. - * @param playerIndex Player index to check. - * @return - */ -bool pokerPlayerDoesNeedToBetThisRound(poker_t *poker, uint8_t playerIndex); - - -/** - * Returns the index of the first player that remains to bet for the current - * round. - * - * @param poker Poker game instance. - * @return The player index of the remaining player, otherwise 0xFF. - */ -uint8_t pokerPlayerGetRemainingBetter(poker_t *poker); - -/** - * Returns the index of the first player that remains to bet for the current - * round. This is based on whatever current better player index you provide. - * - * @param poker Poker game instance. - * @param current Current better player index. - * @return The player index of the next remaining player, otherwise 0xFF. - */ -uint8_t pokerPlayerGetNextBetter(poker_t *poker, uint8_t current); - -/** - * Get the bet necessary for a specific player to make a call. This takes the - * players current bet and the bet necessary to call into the pot and will - * return the difference. - * - * @param poker Poker game instance. - * @param player Player instance to get the call value for. - * @return The count of chips needed to call into the current active pot. - */ -int32_t pokerPlayerGetCallBet(poker_t *poker, pokerplayer_t *player); - /** * Gets the count of players still currently left in the round. * @@ -376,83 +75,6 @@ int32_t pokerPlayerGetCallBet(poker_t *poker, pokerplayer_t *player); */ uint8_t pokerInRoundGetCount(poker_t *poker); -/** - * Returns the count of players remaining to bet. - * - * @param poker Poker game instance. - * @return Count of players left to bet. - */ -uint8_t pokerPlayerGetRemainingBetterCount(poker_t *poker); - -/** - * Let a player bet chips into the pot. - * - * @param poker Poker game instance. - * @param pot Poker pot to bet in to. - * @param playerIndex The players' index that is betting. - * @param chips The amount of chips the player is betting. - */ -void pokerPlayerBetPot( - poker_t *poker, pokerpot_t *pot, uint8_t playerIndex, int32_t chips -); - -/** - * Let a player bet chips into the current pot. - * - * @param bet Poker game instance. - * @param playerIndex The players' index that is betting. - * @param chips The amount of chips the player is betting. - */ -void pokerPlayerBet(poker_t *poker, uint8_t playerIndex, int32_t chips); - - -/** - * Determine whether or not a player CAN check, given the current max bet and - * the players current bet. - * - * @param poker Poker game instance. - * @param player Player to check. - * @return True if the player can check, false if they need to call first. - */ -bool pokerPlayerCanCheck(poker_t *poker, pokerplayer_t *player); - -/** - * Return a turn action for the given player to fold. - * - * @param poker Poker game instance. - * @param player Player index. - * @return A turn for a fold action. - */ -pokerturn_t pokerTurnFold(poker_t *poker, uint8_t player); - -/** - * Perform a turn action for betting as a player. - * - * @param poker Poker game instance. - * @param playerIndex Player index who is betting. - * @param chips Chips to raise by. - * @return A turn for a bet action. - */ -pokerturn_t pokerTurnBet(poker_t *poker, uint8_t playerIndex, int32_t chips); - -/** - * Returns the AI result for a turn done by a non human player. - * - * @param poker Poker game instance to use. - * @param playerIndex Player index to get the turn for. - * @return Some information about the move the player is trying to perform. - */ -pokerturn_t pokerTurnGetForPlayer(poker_t *poker, uint8_t playerIndex); - -/** - * Perform the turn's action. - * - * @param poker Poker game instance. - * @param playerIndex Player index. - * @param turn Turn to action. - */ -void pokerTurnAction(poker_t *poker, uint8_t playerIndex, pokerturn_t turn); - /** * Returns the sum of chips in the pot(s) that the specified player is in. This * does not consider the pot, player or hand, just the pure sum of chips. @@ -475,65 +97,15 @@ void pokerHandGetFull( poker_t *poker, pokerplayer_t *player, card_t cards[POKER_WINNING_FULL_SIZE] ); -/** - * 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 pokerWinnerFillRemaining(pokerplayerwinning_t *winning); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Dealer -/** - * Calculates and returns the winning state for a given player - * - * @param poker Poker game instance. - * @param player Player to get the state for. - * @param winning Output winning state struct to push data in to. - */ -void pokerWinnerGetForPlayer( - poker_t *poker, pokerplayer_t *player, pokerplayerwinning_t *winning -); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Betting -/** - * 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 left is not the winner. - */ -card_t pokerWinnerCompare( - pokerplayerwinning_t *left, pokerplayerwinning_t *right -); +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Pots -/** - * Determines the winning player for the given pot. The pot has participating - * players, and from those participating players some will be winners. The - * participants are only the players within the pot who haven't folded or out. - * - * @param poker Poker game instance. - * @param pot Pot to get the winners for. - * @param winners Array to store the winner state in. - * @param winnerPlayers Array to store player indexes of each of the winners. - * @param winnerCount Pointer to store the count of winners in. - * @param participants Array to store player indexes of players who participate. - * @param participantCount Pointer to store the count of participants in. - */ -void pokerWinnerDetermineForPot( - poker_t *poker, - pokerpot_t *pot, - pokerplayerwinning_t winners[POKER_PLAYER_COUNT_MAX], - uint8_t winnerPlayers[POKER_PLAYER_COUNT_MAX], - uint8_t *winnerCount, - uint8_t participants[POKER_PLAYER_COUNT_MAX], - uint8_t *participantCount -); -/** - * Get the confidence of the bet for a given winning type. - * - * @param type Winning type type. - * @return The confidence. - */ -float pokerWinnerGetTypeConfidence(uint8_t type); \ No newline at end of file +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Clearly fucking player related + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Turn + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Winning \ No newline at end of file diff --git a/src/poker/pot.c b/src/poker/pot.c new file mode 100644 index 00000000..79498b00 --- /dev/null +++ b/src/poker/pot.c @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "pot.h" + +uint8_t pokerPotAdd(poker_t *poker) { + pokerpot_t *pot; + uint8_t i = poker->potCount++; + pot = poker->pots + i; + pot->chips = 0; + pot->playerCount = 0; + return i; +} + +bool pokerPotHasPlayer(pokerpot_t *pot, uint8_t playerIndex) { + return arrayContains( + sizeof(uint8_t), pot->players, pot->playerCount, &playerIndex + ); +} + +void pokerPotAddPlayer(pokerpot_t *pot, uint8_t playerIndex) { + if(pokerPotHasPlayer(pot, playerIndex)) return; + pot->players[pot->playerCount++] = playerIndex; +} diff --git a/src/poker/pot.h b/src/poker/pot.h new file mode 100644 index 00000000..8f0ecfd8 --- /dev/null +++ b/src/poker/pot.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../util/array.h" +#include "poker.h" + +/** + * Adds a new pot to the poker game instance. + * + * @param poker Poker game instance to add a pot to. + * @return The index of the pot within the array of pots. + */ +uint8_t pokerPotAdd(poker_t *poker); + +/** + * Check whether or not the given player is part of the given pot. + * + * @param pot Pot to check. + * @param playerIndex Player index to see if within the pot or not. + * @return True if in the pot, otherwise false. + */ +bool pokerPotHasPlayer(pokerpot_t *pot, uint8_t playerIndex); + +/** + * Add a player to a pot. This will not let you add the same player to the pot + * twice. + * + * @param pot Pot to add to. + * @param playerIndex Players' index to add to the pot. + */ +void pokerPotAddPlayer(pokerpot_t *pot, uint8_t playerIndex); \ No newline at end of file diff --git a/src/poker/turn.c b/src/poker/turn.c new file mode 100644 index 00000000..a9edc30f --- /dev/null +++ b/src/poker/turn.c @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "turn.h" + +pokerturn_t pokerTurnFold(poker_t *poker, uint8_t player) { + return (pokerturn_t){ + .chips = 0, + .confidence = 1, + .type = POKER_TURN_TYPE_FOLD + }; +} + +pokerturn_t pokerTurnBet(poker_t *poker, uint8_t playerIndex, int32_t chips) { + pokerturn_t turn; + pokerplayer_t *player; + int32_t i; + player = poker->players + playerIndex; + turn.confidence = 1; + + if(chips == 0) { + turn.type = POKER_TURN_TYPE_CHECK; + turn.chips = 0; + } else if(player->chips <= chips) { + turn.chips = player->chips; + turn.type = POKER_TURN_TYPE_ALL_IN; + } else { + turn.chips = chips; + turn.type = POKER_TURN_TYPE_BET; + i = pokerGetCallValue(poker); + + if(chips == (i - player->currentBet)) { + turn.type = POKER_TURN_TYPE_CALL; + } + } + + return turn; +} + +pokerturn_t pokerTurnGetForPlayer(poker_t *poker, uint8_t playerIndex) { + int32_t random, maxBet, bluffBet, callBet; + float confidence, expectedGain, potOdds; + bool isBluff; + int32_t amount; + pokerplayer_t *player; + pokerplayerwinning_t winning; + uint8_t i, cardNumber0, cardNumber1, suitNumber0, suitNumber1; + pokerturn_t turn; + + player = poker->players + playerIndex; + + // Can the player do anything? + if(player->state & (POKER_PLAYER_STATE_FOLDED | POKER_PLAYER_STATE_OUT)) { + turn.type = POKER_TURN_TYPE_OUT; + return turn; + } + + // The following logic is heavily inspired by; + // https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c + // But with some changes and smarts added by me. The original source code will + // essentially just run a crap tun of simulated games and get the times that + // they are expected to win from those games, but I'm just going to use the + // odds of the winning hand. + + // Is this preflop? + if(poker->communitySize == 0 && player->cardCount >= 2) { + // Get the hand weight + cardNumber0 = cardGetNumber(player->cards[0]); + cardNumber1 = cardGetNumber(player->cards[1]); + suitNumber0 = cardGetSuit(player->cards[0]); + suitNumber1 = cardGetSuit(player->cards[1]); + + // Get delta between cards + i = mathAbs(cardNumber0 - cardNumber1); + + // Get card weight + confidence = (float)cardNumber0 + (float)cardNumber1; + if(cardNumber0 == cardNumber1) {// Pairs + confidence += 6; + } else if(suitNumber0 == suitNumber1) {// Same suit + confidence += 4; + } + + // Get difference from cards for guessing flush + if(i > 4) { + confidence -= 4; + } else if(i > 2) { + confidence -= i; + } + + // Get the confidence delta 0-1 + confidence = confidence / 30.0f; + } else { + // Simulate my hand being the winning hand, use that as the confidence + pokerWinnerGetForPlayer(poker, player, &winning); + confidence = pokerWinnerGetTypeConfidence(winning.type); + } + + // Now we know how confident the AI is, let's put a chip value to that weight + // How many chips to call? + callBet = pokerPlayerGetCallBet(poker, player); + + // Do they need chips to call, or is it possible to check? + if(callBet > 0) { + potOdds = (float)callBet / ( + (float)callBet + (float)pokerPlayerGetPotChipsSum(poker, playerIndex) + ); + } else { + potOdds = 1.0f / (float)pokerPlayerGetRemainingBetterCount(poker); + } + + // Now determine the expected ROI + expectedGain = confidence / potOdds; + + // Now get a random 0-100 + random = randInt32() % 100; + + // Determine the max bet that the AI is willing to make + maxBet = (int32_t)((float)player->chips / 1.75f) - (random / 2); + maxBet -= callBet; + + // Determine what's a good bluff bet. + bluffBet = random * maxBet / 100 / 2; + + // Now prep the output + isBluff = false; + amount = 0; + + // Now the actual AI can happen. This is basically a weight to confidence + // ratio. The higher the gains and the confidence then the more likely the AI + // is to betting. There are also bluff chances within here. + if(expectedGain < 0.8 && confidence < 0.8) { + if(random < 95) { + amount = 0; + } else { + amount = bluffBet; + isBluff = true; + } + } else if ((expectedGain < 1.0 && confidence < 0.85) || confidence < 0.1) { + if (random < 80) { + amount = 0; + } else if(random < 5) { + amount = callBet; + isBluff = true; + } else { + amount = bluffBet; + isBluff = true; + } + } else if ((expectedGain < 1.3 && confidence < 0.9) || confidence < 0.5) { + if (random < 60 || confidence < 0.5) { + amount = callBet; + } else { + amount = maxBet; + } + } else if (confidence < 0.95 || poker->communitySize < 0x04) { + if(random < 20) { + amount = callBet; + } else { + amount = maxBet; + } + } else { + amount = (player->chips - callBet) * 9 / 10; + } + + // If this is the first round... make it a lot less likely I'll bet + if(poker->communitySize == 0x00 && amount > callBet) { + if(random > 5) amount = callBet; + } + + // Did we actually bet? + if(amount > 0) { + printf("AI is betting %i chips, bluff: %i\n", amount, isBluff); + + // Let's not get caught in a raising loop with AI. + if(player->timesRaised >= POKER_TURN_MAX_RAISES) { + amount = callBet; + } + + amount = mathMax(amount, callBet); + turn = pokerTurnBet(poker, playerIndex, amount); + turn.confidence = confidence; + } else if(pokerPlayerCanCheck(poker, player)) { + turn = pokerTurnBet(poker, playerIndex, 0); + turn.confidence = 1; + } else { + turn = pokerTurnFold(poker, playerIndex); + turn.confidence = 1 - confidence; + } + + return turn; +} + +void pokerTurnAction(poker_t *poker, uint8_t playerIndex, pokerturn_t turn) { + pokerplayer_t *player; + player = poker->players + playerIndex; + + switch(turn.type) { + case POKER_TURN_TYPE_BET: + case POKER_TURN_TYPE_CALL: + case POKER_TURN_TYPE_ALL_IN: + pokerPlayerBet(poker, playerIndex, turn.chips); + player->timesRaised++; + break; + + case POKER_TURN_TYPE_CHECK: + pokerPlayerBet(poker, playerIndex, 0); + player->timesRaised = 0; + break; + + case POKER_TURN_TYPE_FOLD: + player->state |= POKER_PLAYER_STATE_FOLDED; + player->timesRaised = 0; + break; + } + + player->state |= POKER_PLAYER_STATE_HAS_BET_THIS_ROUND; +} \ No newline at end of file diff --git a/src/poker/turn.h b/src/poker/turn.h new file mode 100644 index 00000000..4846c273 --- /dev/null +++ b/src/poker/turn.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "fuck.h" +#include "poker.h" + +/** + * Return a turn action for the given player to fold. + * + * @param poker Poker game instance. + * @param player Player index. + * @return A turn for a fold action. + */ +pokerturn_t pokerTurnFold(poker_t *poker, uint8_t player); + +/** + * Perform a turn action for betting as a player. + * + * @param poker Poker game instance. + * @param playerIndex Player index who is betting. + * @param chips Chips to raise by. + * @return A turn for a bet action. + */ +pokerturn_t pokerTurnBet(poker_t *poker, uint8_t playerIndex, int32_t chips); + +/** + * Returns the AI result for a turn done by a non human player. + * + * @param poker Poker game instance to use. + * @param playerIndex Player index to get the turn for. + * @return Some information about the move the player is trying to perform. + */ +pokerturn_t pokerTurnGetForPlayer(poker_t *poker, uint8_t playerIndex); + +/** + * Perform the turn's action. + * + * @param poker Poker game instance. + * @param playerIndex Player index. + * @param turn Turn to action. + */ +void pokerTurnAction(poker_t *poker, uint8_t playerIndex, pokerturn_t turn); \ No newline at end of file diff --git a/src/poker/winner.c b/src/poker/winner.c new file mode 100644 index 00000000..5224c835 --- /dev/null +++ b/src/poker/winner.c @@ -0,0 +1,401 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "winner.h" + +void pokerWinnerFillRemaining(pokerplayerwinning_t *winning) { + uint8_t i, highest, current; + card_t highestCard, currentCard; + + // Set the kicker + winning->kicker = 0xFF; + + // Fill the remaining cards + while(winning->setSize < POKER_WINNING_SET_SIZE) { + highest = 0xFF; + + for(i = 0; i < winning->fullSize; i++) { + currentCard = winning->full[i]; + if(cardContains(winning->set, winning->setSize, currentCard) != -1) { + continue; + } + + if(highest == 0xFF) { + highestCard = currentCard; + highest = cardGetNumber(highestCard); + } else { + current = cardGetNumber(currentCard); + if(current != CARD_ACE && current < highest) continue; + highestCard = currentCard; + highest = current; + } + } + + if(highest == 0xFF) break; + winning->set[winning->setSize++] = highestCard; + } + cardHandSort(winning->set, winning->setSize); +} + +void pokerWinnerGetForPlayer( + poker_t *poker, pokerplayer_t *player, pokerplayerwinning_t *winning +) { + uint8_t i, j, l; + int32_t index; + card_t card; + uint8_t number, suit, pairCount; + int32_t pairs[CARD_SUIT_COUNT]; + + // Get the full poker hand (should be a 7 card hand, but MAY not be) + winning->fullSize = poker->communitySize + player->cardCount; + pokerHandGetFull(poker, player, winning->full); + cardHandSort(winning->full, winning->fullSize); + + // Reset the winning status. + winning->setSize = 0; + + //////////////////////// Now look for the winning set //////////////////////// + + // Royal / Straight Flush + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + if(number < CARD_FIVE) continue; + + suit = cardGetSuit(card); + winning->setSize = 1; + + // Now look for the matching cards (Reverse order to order from A to 10) + for(j = 1; j <= 4; j++) { + l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. + index = cardContains(winning->full, winning->fullSize, cardGet(l, suit)); + if(index == -1) break; + winning->set[j] = winning->full[index]; + winning->setSize++; + } + + // Check if has all necessary cards. + if(winning->setSize < POKER_WINNING_SET_SIZE) continue; + + // Add self to array + winning->set[0] = winning->full[i]; + winning->type = ( + number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH : + POKER_WINNING_TYPE_STRAIGHT_FLUSH + ); + pokerWinnerFillRemaining(winning); + return; + } + + // Four of a kind. + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + if(pairCount < CARD_SUIT_COUNT) continue; + + winning->setSize = pairCount; + for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; + winning->type = POKER_WINNING_TYPE_FOUR_OF_A_KIND; + pokerWinnerFillRemaining(winning); + return; + } + + // Full House + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + // Check we haven't already added this card. + card = winning->full[i]; + if(cardContains(winning->set, winning->setSize, card) != -1) continue; + + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + + // Did we find either two pair or three pair? + if(pairCount != 2 && pairCount != 3) continue; + if(winning->setSize == 3) pairCount = 2;//Clamp to 5 max. + + // Copy found pairs. + for(j = 0; j < pairCount; j++) { + winning->set[winning->setSize + j] = winning->full[pairs[j]]; + } + winning->setSize += pairCount; + + // Winned? + if(winning->setSize != POKER_WINNING_SET_SIZE) continue; + winning->type = POKER_WINNING_TYPE_FULL_HOUSE; + pokerWinnerFillRemaining(winning); + return; + } + + // Flush (5 same suit) + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + suit = cardGetSuit(card); + winning->setSize = 1; + for(j = i+1; j < winning->fullSize; j++) { + if(cardGetSuit(winning->full[j]) != suit) continue; + winning->set[winning->setSize++] = winning->full[j]; + if(winning->setSize == POKER_WINNING_SET_SIZE) break; + } + if(winning->setSize < POKER_WINNING_SET_SIZE) continue; + winning->set[0] = winning->full[i]; + winning->type = POKER_WINNING_TYPE_FLUSH; + pokerWinnerFillRemaining(winning); + return; + } + + // Straight (sequence any suit) + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + if(number < CARD_FIVE) continue; + winning->setSize = 1; + + for(j = 1; j <= 4; j++) { + l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. + index = cardContainsNumber(winning->full, winning->fullSize, l); + if(index == -1) break; + winning->set[j] = winning->full[index]; + winning->setSize++; + } + + // Check if has all necessary cards. + if(winning->setSize < POKER_WINNING_SET_SIZE) continue; + winning->set[0] = winning->full[i]; + winning->type = POKER_WINNING_TYPE_STRAIGHT; + pokerWinnerFillRemaining(winning); + return; + } + + // Three of a kind + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + if(pairCount != 3) continue; + + winning->setSize = pairCount; + for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; + winning->type = POKER_WINNING_TYPE_THREE_OF_A_KIND; + pokerWinnerFillRemaining(winning); + return; + } + + // Two Pair + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i];// Check we haven't already added this card. + if(cardContains(winning->set, winning->setSize, card) != -1) { + continue; + } + + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + if(pairCount != 2) continue; + + for(j = 0; j < pairCount; j++) { + winning->set[winning->setSize+j] = winning->full[pairs[j]]; + } + winning->setSize += pairCount; + if(winning->setSize != 4) continue; + + winning->type = POKER_WINNING_TYPE_TWO_PAIR; + pokerWinnerFillRemaining(winning); + return; + } + + // Pair + if(winning->setSize == 2) { + winning->type = POKER_WINNING_TYPE_PAIR; + pokerWinnerFillRemaining(winning); + return; + } + + // High card + winning->setSize = 0; + pokerWinnerFillRemaining(winning); + winning->type = POKER_WINNING_TYPE_HIGH_CARD; + + return; +} + +card_t pokerWinnerCompare( + pokerplayerwinning_t *left, pokerplayerwinning_t *right +) { + uint8_t i, number; + card_t card; + int32_t index; + uint8_t countCardsSame; + + card_t highCardLeft, highCardRight; + uint8_t highNumberLeft, highNumberRight; + + highNumberLeft = 0xFF; + highNumberRight = 0xFF; + countCardsSame = 0; + + + for(i = 0; i < left->setSize; i++) { + card = left->set[i]; + number = cardGetNumber(card); + if(highNumberLeft != 0xFF && number < highNumberLeft) continue;//Quick check + + // Check if this number is within the other hand or not + index = cardContainsNumber(right->set, right->setSize, 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 = cardContains(right->set, right->setSize, card); + + // Exact card match + if(index != -1) { + countCardsSame++; + continue; + } + // Not exact card match.. ? + } + + if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { + highNumberLeft = number; + highCardLeft = card; + } + } + + for(i = 0; i < right->setSize; i++) { + card = right->set[i]; + number = cardGetNumber(card); + if(highNumberRight != 0xFF && number < highNumberRight) continue; + + index = cardContainsNumber(left->set, left->setSize, number); + if(index != -1) { + index = cardContains(left->set, left->setSize, card); + if(index != -1) continue; + } + + if(highNumberRight == 0xFF||number == CARD_ACE||highNumberRight < number) { + highNumberRight = number; + highCardRight = card; + } + } + + + if(countCardsSame == left->setSize) { + for(i = 0; i < left->setSize; i++) { + card = left->set[i]; + number = cardGetNumber(card); + if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { + highNumberLeft = number; + highCardLeft = card; + } + } + return highCardLeft; + } + + if(highCardLeft == 0xFF) return 0xFF; + if(highNumberLeft < highNumberRight) return 0xFF; + return highCardLeft;//Greater or Equal to. +} + +void pokerWinnerDetermineForPot( + poker_t *poker, + pokerpot_t *pot, + pokerplayerwinning_t winners[POKER_PLAYER_COUNT_MAX], + uint8_t winnerPlayers[POKER_PLAYER_COUNT_MAX], + uint8_t *winnerCount, + uint8_t participants[POKER_PLAYER_COUNT_MAX], + uint8_t *participantCount +) { + uint8_t i, j, countPlayers, countWinners, number, highNumber; + pokerplayerwinning_t *left, *right; + pokerplayer_t *player; + card_t card, highCard; + bool isWinner; + + countPlayers = 0; + countWinners = 0; + highCard = 0xFF; + + // Get participating players and their hands. + for(i = 0; i < pot->playerCount; i++) { + player = poker->players + pot->players[i]; + if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) { + continue; + } + + participants[countPlayers] = pot->players[i]; + pokerWinnerGetForPlayer(poker, player, winners + countPlayers++); + } + + // Compare participating players + for(i = 0; i < countPlayers; i++) { + left = winners + i; + isWinner = true; + highNumber = 0xFF; + + for(j = 0; j < countPlayers; j++) { + if(i == j) continue; + right = winners + j; + + // Am I the better hand / Is it the better hand? + if(left->type < right->type) continue; + if(left->type > right->type) { + isWinner = false; + break; + } + + // Equal, compare hands. + card = pokerWinnerCompare(left, right); + if(card == 0xFF) { + isWinner = false; + break; + } + + // Determine high card. + number = cardGetNumber(card); + if(highNumber == 0xFF || number == CARD_ACE || number > highNumber) { + highCard = card; + highNumber = number; + } + } + + if(!isWinner) continue; + left->kicker = highCard; + winnerPlayers[countWinners++] = participants[i]; + } + + *participantCount = countPlayers; + *winnerCount = countWinners; +} + +float pokerWinnerGetTypeConfidence(uint8_t type) { + switch(type) { + case POKER_WINNING_TYPE_ROYAL_FLUSH: + return POKER_WINNING_CONFIDENCE_ROYAL_FLUSH; + case POKER_WINNING_TYPE_STRAIGHT_FLUSH: + return POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH; + case POKER_WINNING_TYPE_FOUR_OF_A_KIND: + return POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND; + case POKER_WINNING_TYPE_FULL_HOUSE: + return POKER_WINNING_CONFIDENCE_FULL_HOUSE; + case POKER_WINNING_TYPE_FLUSH: + return POKER_WINNING_CONFIDENCE_FLUSH; + case POKER_WINNING_TYPE_STRAIGHT: + return POKER_WINNING_CONFIDENCE_STRAIGHT; + case POKER_WINNING_TYPE_THREE_OF_A_KIND: + return POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND; + case POKER_WINNING_TYPE_TWO_PAIR: + return POKER_WINNING_CONFIDENCE_TWO_PAIR; + case POKER_WINNING_TYPE_PAIR: + return POKER_WINNING_CONFIDENCE_PAIR; + default: + return POKER_WINNING_CONFIDENCE_HIGH_CARD; + } +} \ No newline at end of file diff --git a/src/poker/winner.h b/src/poker/winner.h new file mode 100644 index 00000000..f8195adc --- /dev/null +++ b/src/poker/winner.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "card.h" +#include "fuck.h" +#include "../libs.h" + +/** + * 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 pokerWinnerFillRemaining(pokerplayerwinning_t *winning); + +/** + * Calculates and returns the winning state for a given player + * + * @param poker Poker game instance. + * @param player Player to get the state for. + * @param winning Output winning state struct to push data in to. + */ +void pokerWinnerGetForPlayer( + poker_t *poker, pokerplayer_t *player, pokerplayerwinning_t *winning +); + +/** + * 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 left is not the winner. + */ +card_t pokerWinnerCompare( + pokerplayerwinning_t *left, pokerplayerwinning_t *right +); + +/** + * Determines the winning player for the given pot. The pot has participating + * players, and from those participating players some will be winners. The + * participants are only the players within the pot who haven't folded or out. + * + * @param poker Poker game instance. + * @param pot Pot to get the winners for. + * @param winners Array to store the winner state in. + * @param winnerPlayers Array to store player indexes of each of the winners. + * @param winnerCount Pointer to store the count of winners in. + * @param participants Array to store player indexes of players who participate. + * @param participantCount Pointer to store the count of participants in. + */ +void pokerWinnerDetermineForPot( + poker_t *poker, + pokerpot_t *pot, + pokerplayerwinning_t winners[POKER_PLAYER_COUNT_MAX], + uint8_t winnerPlayers[POKER_PLAYER_COUNT_MAX], + uint8_t *winnerCount, + uint8_t participants[POKER_PLAYER_COUNT_MAX], + uint8_t *participantCount +); + +/** + * Get the confidence of the bet for a given winning type. + * + * @param type Winning type type. + * @return The confidence. + */ +float pokerWinnerGetTypeConfidence(uint8_t type); \ No newline at end of file