841 lines
23 KiB
C
841 lines
23 KiB
C
/**
|
|
* Copyright (c) 2021 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "poker.h"
|
|
|
|
void pokerInit(poker_t *poker) {
|
|
poker->playerCount = 0;
|
|
poker->playerDealer = 0;
|
|
poker->playerSmallBlind = 0;
|
|
poker->playerBigBlind = 0;
|
|
pokerResetRound(poker);
|
|
}
|
|
|
|
void pokerResetRound(poker_t *poker) {
|
|
uint8_t i;
|
|
pokerplayer_t *player;
|
|
|
|
poker->deckSize = cardWriteDeck(poker->deck);
|
|
poker->graveSize = 0;
|
|
poker->communitySize = 0;
|
|
poker->potCount = 0;
|
|
pokerPotAdd(poker);
|
|
|
|
pokerResetBettingRound(poker);
|
|
|
|
for(i = 0; i < poker->playerCount; i++) {
|
|
player = poker->players + i;
|
|
player->cardCount = 0;
|
|
player->currentBet = 0;
|
|
player->state &= ~(
|
|
POKER_PLAYER_STATE_FOLDED |
|
|
POKER_PLAYER_STATE_HAS_BET_THIS_ROUND |
|
|
POKER_PLAYER_STATE_SHOWING
|
|
);
|
|
}
|
|
}
|
|
|
|
void pokerResetBettingRound(poker_t *poker) {
|
|
uint8_t i;
|
|
pokerplayer_t *player;
|
|
|
|
// Reset the round betting state.
|
|
for(i = 0; i < poker->playerCount; i++) {
|
|
player = poker->players + i;
|
|
player->state =flagOff(player->state,POKER_PLAYER_STATE_HAS_BET_THIS_ROUND);
|
|
}
|
|
|
|
// Now reset to the first player that needs to bet.
|
|
if(poker->playerCount > 0) {
|
|
poker->better = (poker->playerBigBlind + 1) % poker->playerCount;
|
|
} else {
|
|
poker->better = 0x00;
|
|
}
|
|
|
|
// Then we check who's remaining. We do this because the default better may
|
|
// have folded already.
|
|
poker->better = pokerPlayerGetRemainingBetter(poker);
|
|
}
|
|
|
|
void pokerNewDealer(poker_t *poker) {
|
|
uint8_t i, j, k;
|
|
pokerplayer_t *player;
|
|
bool foundDealer;
|
|
bool foundSmall;
|
|
|
|
foundDealer = false;
|
|
foundSmall = false;
|
|
|
|
j = poker->playerDealer + 1;
|
|
for(i = 0; i < poker->playerCount; i++) {
|
|
k = (j + 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);
|
|
}
|
|
|
|
int32_t pokerGetCallValue(poker_t *poker) {
|
|
uint8_t i;
|
|
int32_t call;
|
|
call = 0;
|
|
for(i = 0; i < poker->playerCount; i++) {
|
|
call = mathMax(call, poker->players[i].currentBet);
|
|
}
|
|
return call;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
void pokerPotAddPlayer(pokerpot_t *pot, uint8_t playerIndex) {
|
|
if(arrayContains(
|
|
sizeof(uint8_t), pot->players, pot->playerCount, &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;
|
|
|
|
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;
|
|
pokerplayer_t *player;
|
|
|
|
count = 0;
|
|
for(i = 0; i < poker->playerCount; i++) {
|
|
player = poker->players + i;
|
|
if(player->state & (POKER_PLAYER_STATE_FOLDED | POKER_PLAYER_STATE_OUT)) {
|
|
continue;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
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
|
|
// pokerWinnerPlayerGet(&poker->community, 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)poker->bet.pot);
|
|
// } else {
|
|
// potOdds = (
|
|
// 1.0f / (float)pokerBetGetRemainingPlayerCount(&poker->bet, poker->players)
|
|
// );
|
|
// }
|
|
|
|
// 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 = pokerTurnRaise(poker, playerIndex, amount);
|
|
// turn.confidence = confidence;
|
|
// } else if(pokerTurnCanPlayerCheck(poker, playerIndex)) {
|
|
// turn = pokerTurnCheck(poker, playerIndex);
|
|
// turn.confidence = 1;
|
|
// } else {
|
|
// turn = pokerTurnFold(poker, playerIndex);
|
|
// turn.confidence = 1 - confidence;
|
|
// }
|
|
|
|
|
|
return turn;
|
|
}
|
|
|
|
// Winning
|
|
void pokerHandGetFull(
|
|
poker_t *poker, pokerplayer_t *player, card_t cards[POKER_WINNING_FULL_SIZE]
|
|
) {
|
|
uint8_t i;
|
|
|
|
// Add the dealer hand
|
|
for(i = 0; i < poker->communitySize; i++) {
|
|
cards[i] = poker->community[i];
|
|
}
|
|
|
|
// Add the player hand
|
|
for(i = 0; i < player->cardCount; i++) {
|
|
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]];
|
|
}
|
|
// arrayCopy(sizeof(int32_t), pairs, pairCount, winning->set+winning->setSize);
|
|
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;
|
|
}
|