229 lines
6.4 KiB
C
229 lines
6.4 KiB
C
/**
|
|
* Copyright (c) 2021 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "turn.h"
|
|
|
|
pokerturn_t pokerTurnGet(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)) {
|
|
return pokerTurnOut(poker, playerIndex);
|
|
}
|
|
|
|
// 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->dealer.cardsFacing == 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->dealer, 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 = poker->bet.currentBet - player->currentBet;
|
|
|
|
// 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)(poker->bet.currentBet / 1.75f) - (random / 2) - 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->dealer.cardsFacing < 0x04) {
|
|
if(random < 30) {
|
|
amount = callBet;
|
|
} else {
|
|
amount = maxBet;
|
|
}
|
|
} else {
|
|
amount = (poker->bet.currentBet - callBet) * 9 / 10;
|
|
}
|
|
|
|
// Let's not get caught in a raising loop with AI.
|
|
if(player->timesRaised >= POKER_TURN_MAX_RAISES) amount = callBet;
|
|
|
|
// Did we actually bet?
|
|
if(amount > 0) {
|
|
amount = mathMin(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;
|
|
}
|
|
|
|
void pokerTurnAction(poker_t *poker, pokerplayer_t *player, pokerturn_t *turn) {
|
|
switch(turn->type) {
|
|
case POKER_TURN_TYPE_BET:
|
|
case POKER_TURN_TYPE_CALL:
|
|
case POKER_TURN_TYPE_ALL_IN:
|
|
pokerBetPlayer(&poker->bet, player, turn->chips);
|
|
player->timesRaised++;
|
|
break;
|
|
|
|
case POKER_TURN_TYPE_CHECK:
|
|
pokerBetPlayer(&poker->bet, player, 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_ROUND_MOVE;
|
|
}
|
|
|
|
pokerturn_t pokerTurnOut(poker_t *poker, uint8_t player) {
|
|
pokerturn_t turn;
|
|
turn.type = POKER_TURN_TYPE_OUT;
|
|
return turn;
|
|
}
|
|
|
|
pokerturn_t pokerTurnFold(poker_t *poker, uint8_t player) {
|
|
pokerturn_t turn;
|
|
turn.type = POKER_TURN_TYPE_FOLD;
|
|
return turn;
|
|
}
|
|
|
|
pokerturn_t pokerTurnCheck(poker_t *poker, uint8_t player) {
|
|
pokerturn_t turn;
|
|
turn.type = POKER_TURN_TYPE_CHECK;
|
|
return turn;
|
|
}
|
|
|
|
pokerturn_t pokerTurnCall(poker_t *poker, uint8_t playerIndex) {
|
|
pokerturn_t turn;
|
|
pokerplayer_t *player;
|
|
player = poker->players + playerIndex;
|
|
turn = pokerTurnRaise(
|
|
poker, playerIndex, poker->bet.currentBet - player->currentBet
|
|
);
|
|
return turn;
|
|
}
|
|
|
|
pokerturn_t pokerTurnRaise(poker_t *poker, uint8_t playerIndex, int32_t chips) {
|
|
pokerturn_t turn;
|
|
pokerplayer_t *player;
|
|
player = poker->players + playerIndex;
|
|
|
|
if(chips < poker->bet.currentBet) chips = poker->bet.currentBet;
|
|
|
|
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;
|
|
|
|
if(chips == (poker->bet.currentBet - player->currentBet)) {
|
|
turn.type = POKER_TURN_TYPE_CALL;
|
|
}
|
|
}
|
|
|
|
return turn;
|
|
}
|
|
|
|
bool pokerTurnCanPlayerCheck(poker_t *poker, uint8_t playerIndex) {
|
|
return (poker->players + playerIndex)->currentBet >= poker->bet.currentBet;
|
|
} |