766 lines
23 KiB
C
766 lines
23 KiB
C
/**
|
|
* Copyright (c) 2022 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "poker.h"
|
|
|
|
pokerplayer_t POKER_PLAYERS[POKER_PLAYER_COUNT_MAX];
|
|
|
|
uint8_t POKER_DECK[CARD_DECK_SIZE];
|
|
uint8_t POKER_DECK_SIZE;
|
|
uint8_t POKER_COMMUNITY[POKER_COMMUNITY_SIZE_MAX];
|
|
uint8_t POKER_COMMUNITY_SIZE;
|
|
|
|
uint8_t POKER_PLAYER_DEALER;
|
|
uint8_t POKER_PLAYER_SMALL_BLIND;
|
|
uint8_t POKER_PLAYER_BIG_BLIND;
|
|
uint8_t POKER_PLAYER_BETTER;
|
|
|
|
uint16_t POKER_GAME_BLINDS_CURRENT;
|
|
|
|
pokerpot_t POKER_POTS[POKER_POT_COUNT_MAX];
|
|
uint8_t POKER_POT_CURRENT;
|
|
uint8_t POKER_POT_COUNT;
|
|
|
|
pokerplayerwinning_t POKER_WINNERS[POKER_PLAYER_COUNT_MAX];
|
|
uint8_t POKER_WINNER_PLAYERS[POKER_PLAYER_COUNT_MAX];
|
|
uint8_t POKER_WINNER_PARTICIPANTS[POKER_PLAYER_COUNT_MAX];
|
|
uint8_t POKER_WINNER_COUNT;
|
|
uint8_t POKER_WINNER_PARTICIPANT_COUNT;
|
|
|
|
void pokerInit() {
|
|
uint8_t i;
|
|
|
|
// Set up players
|
|
for(i = 0; i < POKER_PLAYER_COUNT_MAX; i++) {
|
|
POKER_PLAYERS[i].chips = 10000;
|
|
POKER_PLAYERS[i].state = 0;
|
|
}
|
|
|
|
// Set up the initial state.
|
|
// TODO: Should this be randomized?
|
|
POKER_PLAYER_DEALER = 0;
|
|
POKER_GAME_BLINDS_CURRENT = 10;
|
|
|
|
// Reset the round state (For the first round)
|
|
pokerNewRound();
|
|
}
|
|
|
|
void pokerNewRound() {
|
|
uint8_t i, j, k;
|
|
uint8_t found;
|
|
|
|
// Reset round state
|
|
POKER_COMMUNITY_SIZE = 0;
|
|
POKER_POT_COUNT = 0;
|
|
POKER_POT_CURRENT = 0;
|
|
|
|
// Reset the pots.
|
|
for(i = 0; i < POKER_POT_COUNT_MAX; i++) {
|
|
POKER_POTS[i].chips = 0;
|
|
POKER_POTS[i].call = 0;
|
|
for(j = 0; j < POKER_PLAYER_COUNT_MAX; j++) {
|
|
POKER_POTS[i].players[j] = 0;
|
|
}
|
|
}
|
|
|
|
// Fill deck
|
|
for(i = 0; i < CARD_DECK_SIZE; i++) POKER_DECK[i] = i;
|
|
POKER_DECK_SIZE = CARD_DECK_SIZE;
|
|
|
|
// Shuffle Deck
|
|
for(i = CARD_DECK_SIZE-1; i > 0; i--) {
|
|
k = POKER_DECK[i];
|
|
j = rand() % (i+1);
|
|
POKER_DECK[i] = POKER_DECK[j];
|
|
POKER_DECK[j] = k;
|
|
}
|
|
|
|
// Reset Players and decide new blinds.
|
|
found = 0;
|
|
POKER_PLAYER_DEALER++;
|
|
|
|
for(i = 0; i < POKER_PLAYER_COUNT_MAX; i++) {
|
|
POKER_PLAYERS[i].state &= ~(
|
|
POKER_PLAYER_STATE_FOLDED |
|
|
POKER_PLAYER_STATE_HAS_BET_THIS_ROUND
|
|
);
|
|
POKER_PLAYERS[i].timesRaised = 0;
|
|
|
|
// Have we found the dealer, small blind, and big blind players?
|
|
if(found != 3 && (POKER_PLAYERS[i].state & POKER_PLAYER_STATE_OUT) == 0){
|
|
k = (POKER_PLAYER_DEALER + i) % POKER_PLAYER_COUNT_MAX;
|
|
|
|
if(found == 0) {// Have we found the dealer?
|
|
POKER_PLAYER_DEALER = i;
|
|
found++;
|
|
} else if(found == 1) {// Have we found the small blind?
|
|
POKER_PLAYER_SMALL_BLIND = i;
|
|
found++;
|
|
} else if(found == 2) {// Have we found the big blind?
|
|
POKER_PLAYER_BIG_BLIND = i;
|
|
found++;
|
|
}
|
|
}
|
|
|
|
// Deal two cards to the player.
|
|
for(j = 0; j < POKER_PLAYER_HAND_SIZE_MAX; j++) {
|
|
POKER_PLAYERS[i].hand[j] = POKER_DECK[POKER_DECK_SIZE--];
|
|
}
|
|
}
|
|
|
|
// Take blinds
|
|
// TODO: I need to make sure the blind players even have the chips to blind.
|
|
pokerBet(POKER_PLAYER_SMALL_BLIND, POKER_GAME_BLINDS_CURRENT);
|
|
pokerBet(POKER_PLAYER_BIG_BLIND, (POKER_GAME_BLINDS_CURRENT*2));
|
|
|
|
// Set the initial better, we set this to the BIG BLIND player because we will
|
|
// cycle to the "next better" as soon as the game starts.
|
|
POKER_PLAYER_BETTER = POKER_PLAYER_BIG_BLIND;
|
|
}
|
|
|
|
inline void pokerBet(uint8_t player, uint16_t amount) {
|
|
// TODO: This may become a function because if a player doesn't have enough
|
|
// chips to bet to the active pot, then the pot needs to autosplit, take those
|
|
// who have bet into the pot up to the amount that the player betting can bet,
|
|
// and push them into a new pot.
|
|
// There also needs to be a limit on this, for example;
|
|
// player 0 has $1200, and bets $1000, they can't bet more than that ever.
|
|
// player 1 has $1000, and bets all of it. The remanin
|
|
// player 2 has $700, and bets all o it. A new $300 sidepot auto creates
|
|
// player 3 has $500 and bets all of it, Another sidepot with $200 is auto made.
|
|
POKER_PLAYERS[player].state |= POKER_PLAYER_STATE_HAS_BET_THIS_ROUND;
|
|
POKER_PLAYERS[player].chips -= amount;
|
|
POKER_POTS[POKER_POT_CURRENT].chips += amount;
|
|
POKER_POTS[POKER_POT_CURRENT].players[player] += amount;
|
|
POKER_POTS[POKER_POT_CURRENT].call = MATH_MAX(
|
|
amount, POKER_POTS[POKER_POT_CURRENT].players[player]
|
|
);
|
|
}
|
|
|
|
inline uint8_t pokerGetCallBet(uint8_t player) {
|
|
return (
|
|
POKER_POTS[POKER_POT_CURRENT].call -
|
|
POKER_POTS[POKER_POT_CURRENT].players[player]
|
|
);
|
|
}
|
|
|
|
void pokerAi(uint8_t player, pokerturn_t *turn) {
|
|
uint8_t i, cardNumber0, cardNumber1, suitNumber0, suitNumber1;
|
|
uint16_t callBet, maxBet, amount, bluffBet;
|
|
uint8_t random;// TODO: Determine type.
|
|
uint16_t confidence, expectedGain, potOdds;
|
|
pokerplayerwinning_t winning;
|
|
pokerplayer_t *plyr = POKER_PLAYERS + player;
|
|
|
|
// 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 hand being the winning hand.
|
|
|
|
// Is this preflop?
|
|
if(POKER_COMMUNITY_SIZE == 0) {
|
|
// Get the hand weight
|
|
cardNumber0 = cardGetNumber(plyr->hand[0]);
|
|
cardNumber1 = cardGetNumber(plyr->hand[1]);
|
|
suitNumber0 = cardGetSuit(plyr->hand[0]);
|
|
suitNumber1 = cardGetSuit(plyr->hand[1]);
|
|
|
|
// Get delta between cards
|
|
i = MATH_ABS(cardNumber0 - cardNumber1);
|
|
|
|
// Get card weight
|
|
confidence = cardNumber0 + 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;
|
|
}
|
|
|
|
// Confidence is now a value between 0-30 (inclusive). Multiply by 1000
|
|
confidence = (confidence * 1000) / 30;
|
|
// Now do over 30 to get the value represented as 0-1000
|
|
} else {
|
|
// Simulate my hand being the winning hand, use that as the confidence
|
|
// TODO: Repurpose old code lmao. Just take it from Dawn-C
|
|
BGB_printf("Holy shit you guys");
|
|
pokerWinnerGetForPlayer(player, &winning);
|
|
BGB_printf("Winning type %u", winning.type);
|
|
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 = pokerGetCallBet(player);
|
|
|
|
// Do they need chips to call, or is it possible to check?
|
|
if(callBet > 0) {
|
|
// Work out how many chips the player could possibly win. This is a number
|
|
// between 0 and player count * initial chips, at the time of writing that
|
|
// is around 50,000.
|
|
expectedGain = 0;
|
|
for(i = 0; i < POKER_POT_COUNT; i++) {
|
|
if(POKER_POTS[i].players[player] == 0) break;
|
|
expectedGain += POKER_POTS[i].chips;
|
|
}
|
|
|
|
// Now work out the pot odds, this is basically "how many times the callbet
|
|
// could I win if I win this hand". e.g. Desirable hands will have an
|
|
// expected gain much higher than the callbet.
|
|
potOdds = plyr->chips / 1000;
|
|
potOdds = MATH_MAX((callBet + expectedGain), 1) / MATH_MAX(potOdds, 1);
|
|
} else {
|
|
// If the call bet is zero then there's fairly equal odds, so let's just
|
|
// take the chances out of the remainig player count.
|
|
potOdds = 1000 / pokerGetRemainingBetterCount() * 2;// 0 - 1000
|
|
}
|
|
|
|
// Now determine the expected ROI
|
|
//TODO: I think these values are a bit odd.
|
|
expectedGain = (confidence*100) / (potOdds / 10);
|
|
|
|
// Now get a random number 0-100.
|
|
random = rand() % 100;
|
|
|
|
// Determine the max bet that the AI is willing to make. The max bet is
|
|
// basically how high the AI is willing to bet.
|
|
maxBet = plyr->chips;// TODO: Replace with below code and test, just running
|
|
// With this for now.
|
|
// maxBet = plyr->chips / MATH_MAX(random / 10, 1);
|
|
// maxBet -= callBet;
|
|
// BGB_printf("Rand %u, Max Bet %u", random, callBet);
|
|
|
|
// Determine what's a good bluff bet.
|
|
// TODO: not float
|
|
bluffBet = maxBet;
|
|
// bluffBet = ((random * 100) / maxBet) * plyr->chips;
|
|
|
|
// Now prep the output
|
|
turn->bluff = 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 < 800 && confidence < 800) {
|
|
if(random < 95) {
|
|
amount = 0;
|
|
} else {
|
|
amount = bluffBet;
|
|
turn->bluff = true;
|
|
}
|
|
} else if ((expectedGain < 1000 && confidence < 850) || confidence < 100) {
|
|
if (random < 80) {
|
|
amount = 0;
|
|
} else if(random < 5) {
|
|
amount = callBet;
|
|
turn->bluff = true;
|
|
} else {
|
|
amount = bluffBet;
|
|
turn->bluff = true;
|
|
}
|
|
} else if ((expectedGain < 1300 && confidence < 900) || confidence < 500) {
|
|
if (random < 60 || confidence < 500) {
|
|
amount = callBet;
|
|
} else {
|
|
amount = maxBet;
|
|
}
|
|
} else if (confidence < 950 || POKER_COMMUNITY_SIZE < 0x04) {
|
|
if(random < 20) {
|
|
amount = callBet;
|
|
} else {
|
|
amount = maxBet;
|
|
}
|
|
} else {
|
|
// TODO: check this
|
|
amount = (plyr->chips - callBet) * 9 / 10;
|
|
}
|
|
|
|
// If this is the first round... make it a lot less likely I'll bet
|
|
if(POKER_COMMUNITY_SIZE == 0x00 && amount > callBet) {
|
|
if(random > 5) amount = callBet;
|
|
}
|
|
|
|
|
|
// Did we actually bet?
|
|
if(amount > 0) {
|
|
// Let's not get caught in a raising loop with AI.
|
|
if(plyr->timesRaised >= POKER_TURN_MAX_RAISES) amount = callBet;
|
|
amount = MATH_MAX(amount, callBet);
|
|
amount = MATH_MIN(amount, plyr->chips);
|
|
turn->chips = amount;
|
|
turn->confidence = confidence;
|
|
|
|
if(amount == plyr->chips) {
|
|
turn->type = POKER_TURN_TYPE_ALL_IN;
|
|
} else if(amount == callBet) {
|
|
turn->type = POKER_TURN_TYPE_CALL;
|
|
} else {
|
|
turn->type = POKER_TURN_TYPE_BET;
|
|
}
|
|
|
|
} else if(pokerCanPlayerCheck(player)) {
|
|
turn->type = POKER_TURN_TYPE_CHECK;
|
|
turn->chips = 0;
|
|
turn->confidence = 1000;
|
|
} else {
|
|
turn->type = POKER_TURN_TYPE_FOLD;
|
|
turn->chips = 0;
|
|
turn->confidence = 1000 - confidence;
|
|
}
|
|
}
|
|
|
|
inline bool pokerCanPlayerCheck(uint8_t player) {
|
|
return (
|
|
POKER_POTS[POKER_POT_CURRENT].players[player] ==
|
|
POKER_POTS[POKER_POT_CURRENT].call
|
|
);
|
|
}
|
|
|
|
inline bool pokerDoesPlayerNeedToBet(uint8_t playerIndex) {
|
|
uint8_t i;
|
|
pokerplayer_t *player = POKER_PLAYERS + playerIndex;
|
|
|
|
// Can this player even participate?
|
|
if(
|
|
(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) != 0 ||
|
|
player->chips == 0
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// Has the player bet? If so are they in the current pot?
|
|
if((player->state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND) == 0) {
|
|
return true;
|
|
}
|
|
|
|
//TODO: Refer to pokerbet function, but basically I can't let the player
|
|
//bet if they have bet more money than the second richest player.
|
|
|
|
// Check each pot, if the pot is inactive or the player is CALLED/CHECKED
|
|
for(i = 0; i < POKER_POT_COUNT_MAX; i++) {
|
|
// Is this pot active?
|
|
if(POKER_POTS[i].chips == 0) break;
|
|
|
|
// Is the player called into this pot all the way?
|
|
if(POKER_POTS[i].players[playerIndex] == POKER_POTS[i].call) continue;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
inline uint8_t pokerGetRemainingBetterCount() {
|
|
uint8_t i, count;
|
|
count = 0;
|
|
for(i = 0 ; i < POKER_PLAYER_COUNT_MAX; i++) {
|
|
if(pokerDoesPlayerNeedToBet(i)) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void pokerWinnerFillRemaining(pokerplayerwinning_t *winning) {
|
|
uint8_t i, highest, current, 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) != 0xFF) {
|
|
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(uint8_t playerIndex,pokerplayerwinning_t *winning){
|
|
uint8_t i, j, l, card, number, suit, pairCount;
|
|
int16_t index;
|
|
int16_t pairs[CARD_SUIT_COUNT];
|
|
pokerplayer_t *player;
|
|
|
|
player = POKER_PLAYERS + playerIndex;
|
|
|
|
// Get the full poker hand (should be a 7 card hand, but MAY not be)
|
|
for(i = 0; i < POKER_COMMUNITY_SIZE; i++) {
|
|
winning->full[i] = POKER_COMMUNITY[i];
|
|
}
|
|
for(i = 0; i < POKER_PLAYER_HAND_SIZE_MAX; i++) {
|
|
winning->full[i + POKER_COMMUNITY_SIZE] = player->hand[i];
|
|
}
|
|
winning->fullSize = POKER_COMMUNITY_SIZE + POKER_PLAYER_HAND_SIZE_MAX;
|
|
|
|
// TODO: Do I need to sort this?
|
|
// 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 == 0xFF) 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) != 0xFF) 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 == 0xFF) 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) != 0xFF) {
|
|
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;
|
|
}
|
|
|
|
inline uint16_t 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;
|
|
}
|
|
}
|
|
|
|
uint8_t pokerWinnerCompare(
|
|
pokerplayerwinning_t *left, pokerplayerwinning_t *right
|
|
) {
|
|
uint8_t
|
|
i, number, card, countCardsSame, index,
|
|
highCardLeft, highCardRight, 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 == 0xFF) {
|
|
// 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 != 0xFF) {
|
|
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 != 0xFF) {
|
|
index = cardContains(left->set, left->setSize, card);
|
|
if(index != 0xFF) 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(
|
|
pokerpot_t *pot,
|
|
pokerplayerwinning_t *winners,
|
|
uint8_t *winnerPlayers,
|
|
uint8_t *winnerCount,
|
|
uint8_t *participants,
|
|
uint8_t *participantCount
|
|
) {
|
|
uint8_t i, j, countPlayers, countWinners, number, highNumber, card, highCard;
|
|
pokerplayerwinning_t *left, *right;
|
|
pokerplayer_t *player;
|
|
bool isWinner;
|
|
|
|
countPlayers = 0;
|
|
countWinners = 0;
|
|
highCard = 0xFF;
|
|
|
|
// Get participating players and their hands.
|
|
for(i = 0; i < POKER_PLAYER_COUNT_MAX; i++) {
|
|
if(pot->players[i] == 0) continue;
|
|
|
|
player = POKER_PLAYERS + i;
|
|
if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) {
|
|
continue;
|
|
}
|
|
|
|
participants[countPlayers] = i;
|
|
pokerWinnerGetForPlayer(i, 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;
|
|
} |