Skip to content

Instantly share code, notes, and snippets.

@amoshyc
Last active August 29, 2015 14:12
Show Gist options
  • Save amoshyc/f95a48a54f302832dcc3 to your computer and use it in GitHub Desktop.
Save amoshyc/f95a48a54f302832dcc3 to your computer and use it in GitHub Desktop.
TODO: Test, AI
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h> // for sleep()
int min(const int a, const int b) {
return ((a > b) ? b : a);
}
int max(const int a, const int b) {
return ((a > b) ? a : b);
}
bool is_str_digit(const char* s) {
for (int i=0; i<strlen(s); i++)
if (s[i] < '0' || s[i] > '9')
return false;
return true;
}
int randint(const int a, const int b) {
return (rand() % (b-a+1) + a);
}
void get_input(char* input, const int N) {
fgets(input, N, stdin);
int len = strlen(input);
if (input[len-1] == '\n')
input[len-1] = '\0';
}
bool next_permutation(int *array, int length) {
int i, j;
int temp;
// Find non-increasing suffix
if (length == 0)
return false;
i = length - 1;
while (i > 0 && array[i - 1] >= array[i])
i--;
if (i == 0)
return false;
// Find successor to pivot
j = length - 1;
while (array[j] <= array[i - 1])
j--;
temp = array[i - 1];
array[i - 1] = array[j];
array[j] = temp;
// Reverse suffix
j = length - 1;
while (i < j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
return true;
}
typedef struct {
int rank;
int color;
} Card;
void print_card(const Card c) {
static const char* ranks = "A23456789TJQK";
static const char* colors = "scdh";
printf("%c%c", ranks[c.rank-1], colors[c.color]);
}
int card_cmp(const void* a, const void* b) {
Card* ca = (Card*) a;
Card* cb = (Card*) b;
if (ca->rank < cb->rank)
return -1;
if (ca->rank > cb->rank)
return +1;
if (ca->color < cb->color)
return -1;
if (ca->color > cb->color)
return +1;
return 0;
}
void init_poker(Card poker[52]) {
for (int i=0; i<52; i++) {
poker[i].rank = i / 4 + 1;
poker[i].color = i % 4;
}
}
void shuffle_poker(Card poker[52]) {
for (int i=0; i<51; i++) {
int r = randint(i, 51);
Card temp = poker[i];
poker[i] = poker[r];
poker[r] = temp;
}
}
typedef struct {
Card pri[2];
int bet;
bool is_fold;
int money;
Card best[5];
int level;
} Player;
typedef struct {
int game_state;
/* 0 : init
1 : preflop round
2 : flop round
3 : turn round
4 : river round
5 : showdown
6 : ending
*/
Card poker[52];
int poker_idx;
Card community[5];
int minimum_bet; /* = small blind = 1/2 * big blind */
int pot;
int dealer;
int player_num;
Player players[23];
} Game;
void init_game(Game* game, int min_bet, int ply_num) {
game->minimum_bet = min_bet;
game->player_num = min(max(2, ply_num), 23);
game->dealer = -1;
init_poker(game->poker);
shuffle_poker(game->poker);
game->poker_idx = 0;
}
void new_game(Game* game) {
game->game_state = 0;
game->pot = 0;
game->dealer = (game->dealer+1) % (game->player_num);
Card nullcard;
nullcard.rank = -1;
nullcard.color = -1;
for (int i=0; i<game->player_num; i++) {
game->players[i].bet = 0;
game->players[i].is_fold = false;
game->players[i].money = 1000;
game->players[i].level = -1;
for (int j=0; j<5; j++)
game->players[i].best[j] = nullcard;
}
// deal, precheck
if (52 - (game->poker_idx) <= (game->player_num) * 2 + 5) {
puts("Cards not enough! Re-shuffling......OK");
init_poker(game->poker);
shuffle_poker(game->poker);
game->poker_idx = 0;
}
// deal to community card
for (int i=0; i<5; i++) {
game->community[i] = game->poker[game->poker_idx];
(game->poker_idx)++;
}
// deal to players
for (int i=0; i<game->player_num; i++) {
game->players[i].pri[0] = game->poker[game->poker_idx];
(game->poker_idx)++;
game->players[i].pri[1] = game->poker[game->poker_idx];
(game->poker_idx)++;
// sort
if (game->players[i].pri[0].rank >= game->players[i].pri[1].rank ||
game->players[i].pri[0].color > game->players[i].pri[1].color) {
Card temp = game->players[i].pri[0];
game->players[i].pri[0] = game->players[i].pri[1];
game->players[i].pri[1] = temp;
}
}
}
void print_game(const Game game) {
static const char* game_states[] = {"Init",
"Preflop Round", "Flop Round", "Turn Round",
"River Round", "The ShowDown", "Ending"
};
printf("Game state: %s\n\n", game_states[game.game_state]);
printf("Pot: %6d\n", game.pot);
printf("Community Cards: ");
int num_community_revealed;
if (game.game_state == 1)
num_community_revealed = 0;
else
num_community_revealed = min(5, game.game_state + 1);
for (int i=0; i<5; i++) {
if (i != 0)
printf(" ");
if (i < num_community_revealed || game.game_state >= 5)
print_card(game.community[i]);
else
printf("XX");
}
printf("\n");
for (int i=0; i<game.player_num; i++) {
printf("Player #%02d", i+1);
if (i == game.dealer)
printf(", dealer ");
else if (i == (game.dealer + 1) % game.player_num)
printf(", small blind ");
else if (i == (game.dealer + 2) % game.player_num)
printf(", big blind ");
else
printf(" ");
if (i == 0 || game.game_state >= 5) {
printf(": ");
print_card(game.players[i].pri[0]);
printf(" ");
print_card(game.players[i].pri[1]);
}
else
printf(": XX XX");
printf(" :: ");
if (game.players[i].is_fold)
printf("Fold");
else
printf("%4d", game.players[i].bet);
printf(" :: ");
printf("%4d", game.players[i].money);
printf("\n");
}
printf("----------------------------------\n");
}
bool game_set_bet(Game* game, int id, int delta) {
if (delta > game->players[id].money)
return false;
game->players[id].bet += delta;
game->players[id].money -= delta;
return true;
}
bool game_process_input(Game* game, int id, const char* input) {
if (strcmp(input, "c") == 0 || strcmp(input, "Call") == 0) {
// the first player in the round aren't able to call
if (game->game_state == 1 && id == (game->dealer+3) % game->player_num) {
puts("You aren't allowed to call.");
return false;
}
if (2 <= game->game_state && game->game_state <= 4) {
int first_id = (game->dealer + 1) % game->player_num;
while (game->players[first_id].is_fold == true)
first_id = (first_id+1) % game->player_num;
if (id == first_id) {
puts("You aren't allowed to call.");
return false;
}
}
int pre_id = id-1;
if (pre_id < 0)
pre_id += game->player_num;
int delta = game->players[pre_id].bet - game->players[id].bet;
game_set_bet(game, id, delta);
return true;
}
if (strcmp(input, "a") == 0 || strcmp(input, "All in") == 0) {
if (game->players[id].money == 0) {
puts("That's impossible");
return false;
}
int delta = game->players[id].money - game->players[id].bet;
game_set_bet(game, id, delta);
return true;
}
if (strcmp(input, "f") == 0 || strcmp(input, "Fold") == 0) {
// players are not able to fold when there's only one player not fold
int not_fold_player_cnt = 0;
for (int i=0; i<game->player_num; i++)
if (game->players[i].is_fold == false)
not_fold_player_cnt++;
if (not_fold_player_cnt == 1) {
puts("There's only one players playing. You sure to fold?");
return false;
}
game->players[id].is_fold = true;
return true;
}
if (strcmp(input, "0") == 0 || strcmp(input, "Check") == 0) {
// if any player prior to players[id] bets, player[id] won't be allowed to check
bool any = false;
int round_starter = (game->dealer + ((game->game_state == 1) ? 3 : 1)) % game->player_num;
for (int i=round_starter; i != id; i++) {
if (game->players[i].bet != 0) {
any = true;
break;
}
}
if (any == true) {
puts("You are not allowed to check!");
return false;
}
return true;
}
// raise or bet
if (is_str_digit(input)) {
int delta = atoi(input);
// raise => check if bet is legal
if (game->game_state == 1 || id != (game->dealer+1) % game->player_num) {
int pre_id = id-1;
if (pre_id < 0)
pre_id += game->player_num;
const int new_bet = game->players[id].bet + delta;
if (new_bet < game->players[pre_id].bet) {
puts("Bet should be bigger than previous player's.");
return false;
}
if (new_bet > game->players[id].money) {
puts("You don't have enought money.");
return false;
}
}
game_set_bet(game, id, delta);
}
return false;
}
bool check_round_over(const Game game) {
int not_fold_bet = -1;
for (int i=0; i<game.player_num; i++) {
if (game.players[i].is_fold == false) {
not_fold_bet = game.players[i].bet;
break;
}
}
for (int i=0; i<game.player_num; i++)
if (game.players[i].is_fold == false &&
(game.players[i].bet != not_fold_bet || game.players[i].money == 0))
return false;
return true;
}
int ai(Game game, int id) {
int maximum = -1;
for (int i=0; i<game.player_num; i++)
if (game.players[i].bet > maximum)
maximum = game.players[i].bet;
return maximum - game.players[id].bet;
}
int get_poker_hand_level(const Card card[5]) {
// royal flush
if (card[1].color == card[0].color &&
card[2].color == card[0].color &&
card[3].color == card[0].color &&
card[4].color == card[0].color &&
card[0].rank == 1 &&
card[1].rank == 10 &&
card[2].rank == 11 &&
card[3].rank == 12 &&
card[4].rank == 13)
return 10;
// straight flush
if (card[1].color == card[0].color &&
card[2].color == card[0].color &&
card[3].color == card[0].color &&
card[4].color == card[0].color &&
card[1].rank == card[0].rank+1 &&
card[2].rank == card[0].rank+2 &&
card[3].rank == card[0].rank+3 &&
card[4].rank == card[0].rank+4)
return 9;
// four of a kind: AAAAB, ABBBB
if (card[1].rank == card[0].rank &&
card[2].rank == card[0].rank &&
card[3].rank == card[0].rank)
return 8;
if (card[2].rank == card[1].rank &&
card[3].rank == card[1].rank &&
card[4].rank == card[1].rank)
return 8;
// full house: AAABB, AABBB
if (card[1].rank == card[0].rank &&
card[2].rank == card[0].rank &&
card[4].rank == card[3].rank)
return 7;
if (card[1].rank == card[0].rank &&
card[3].rank == card[2].rank &&
card[4].rank == card[2].rank)
return 7;
// flush
if (card[1].color == card[0].color &&
card[2].color == card[0].color &&
card[3].color == card[0].color &&
card[4].color == card[0].color)
return 6;
// straight
if (card[1].rank == card[0].rank + 1 &&
card[2].rank == card[0].rank + 2 &&
card[3].rank == card[0].rank + 3 &&
card[4].rank == card[0].rank + 4)
return 5;
if (card[0].rank == 1 &&
card[1].rank == 10 &&
card[2].rank == 11 &&
card[3].rank == 12 &&
card[4].rank == 13)
return 5;
// three of a kind: AAABC, ABBBC, ABCCC
for (int i=0; i<=2; i++)
if (card[i+1].rank == card[i].rank &&
card[i+2].rank == card[i].rank &&
card[i+3].rank == card[i].rank)
return 4;
// two pairs: AABBC, AABCC, CAABB
int pair_cnt = 2;
for (int i=0; i<4; i++)
if (card[i+1].rank == card[i].rank)
pair_cnt--;
if (pair_cnt == 0)
return 3;
// one pair
for (int i=0; i<4; i++)
if (card[i+1].rank == card[i].rank)
return 2;
// hight card
return 1;
}
int get_best_card(const Card card[7], Card* best_card) {
int flag[7] = {0, 0, 1, 1, 1, 1, 1};
int best_level = -1;
do {
Card query[5];
int idx = 0;
for (int i=0; i<7; i++)
if (flag[i] == 1)
query[idx++] = card[i];
int level = get_poker_hand_level(query);
if (level > best_level) {
best_level = level;
memcpy(best_card, query, sizeof(query));
}
} while (next_permutation(flag, 7));
return best_level;
}
int get_winner_level(Game* game) {
int best_level_of_all = -1;
for (int i=0; i<game->player_num; i++) {
if (game->players[i].is_fold == true)
continue;
Card cards[7];
for (int i=0; i<5; i++)
cards[i] = (game->community)[i];
cards[5] = game->players[i].pri[0];
cards[6] = game->players[i].pri[1];
qsort(cards, 7, sizeof(Card), card_cmp);
int level = get_best_card(cards, game->players[i].best);
if (level > game->players[i].level)
game->players[i].level = level;
if (level > best_level_of_all)
best_level_of_all = level;
}
return best_level_of_all;
}
int main() {
srand(time(NULL));
char input[1024];
while (true) {
printf("How many players do you want? [2~23]: ");
get_input(input, 1024);
if (is_str_digit(input) == false) {
puts("Illegal Input");
continue;
}
break;
}
int ply_num = atoi(input);
printf("------------------------------\n");
Game game;
init_game(&game, 10, ply_num);
while (true) {
// new game
new_game(&game);
print_game(game);
const int N = game.player_num;
for (int game_round=1; game_round <= 4; game_round++) {
game.game_state = game_round;
if (game.game_state == 1) {
// preflop round: blind bet
game_set_bet(&game, (game.dealer+1) % N, game.minimum_bet);
game_set_bet(&game, (game.dealer+2) % N, 2 * (game.minimum_bet));
print_game(game);
}
// main loop
for (int i=0; i < N || check_round_over(game) == false; i++) {
int id;
if (game.game_state == 1)
id = (game.dealer + i + 3) % N;
else
id = (game.dealer + i + 1) % N;
if (game.players[id].is_fold) {
print_game(game);
//sleep(1);
continue;
}
if (id != 0) {
game_set_bet(&game, id, ai(game, id));
//sleep(1);
}
else {
while (true) {
printf("[number(raise or bet), 0 (Check), c(Call), f(Fold), a(All in)] : ");
get_input(input, 1024);
if (game_process_input(&game, id, input) == true)
break;
else
printf("Illegal Input\n");
}
}
print_game(game);
}
// move all bets to pot
for (int i=0; i<N; i++) {
if (game.players[i].is_fold == false) {
game.pot += game.players[i].bet;
game.players[i].bet = 0;
}
}
}
// The showdown
{
game.game_state = 5;
print_game(game);
int best_level = get_winner_level(&game);
int num_of_winner = 0;
printf("Winner(s): \n");
for (int i=0; i<N; i++) {
if (game.players[i].level == best_level) {
num_of_winner++;
printf("Player #%02d: ", i+1);
printf("%2d : ", game.players[i].level);
for (int j=0; j<5; j++) {
print_card(game.players[i].best[j]);
printf(" ");
}
printf("\n");
}
}
printf("All: \n");
for (int i=0; i<N; i++) {
printf("Player #%02d: ", i+1);
printf("%2d : ", game.players[i].level);
if (game.players[i].is_fold == true) {
printf("None\n");
continue;
}
for (int j=0; j<5; j++) {
print_card(game.players[i].best[j]);
printf(" ");
}
printf("\n");
}
// winner(s) takes pot
int gain = game.pot / num_of_winner;
for (int i=0; i<N; i++) {
if (game.players[i].level == best_level) {
game.players[i].money += gain;
printf("Player #%02d gain $%d!", i+1, gain);
}
}
game.pot = 0;
}
// Ending
{
game.game_state = 6;
printf("Play Again? <y/n>");
get_input(input, 1024);
if (strcmp(input, "n") == 0 || strcmp(input, "no") == 0)
break;
}
puts("New Game! Press Enter to continue");
get_input(input, 1024);
}
puts("program exits");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment