ポーカーを作る

カードの配付

カードの山配列deckから何枚か人間の手札に移動しよう。 手札用の構造体 human を宣言し、そこに5枚の山札を 移動する。

  Cards deck, human;
  int i;
  initialize(&deck);
  shuffle(&deck);
  /* 人間に5枚配る */
  for (human.n=0; human.n<5; human.n++) {
    memcpy(&human.card[human.n], &deck.card[--deck.n], sizeof (aCard));
  }

--deck.n とすることにより、山に残っているカードの カウントが1ずつ減って行く。

カードの表示

ここまでの関数をまとめて、配った5枚のカードを表示する だけのプログラムを作ってみる。

disp5.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CARDS   52       /* 難しいのでジョーカー抜き */

typedef struct {	/* 1枚のカードの構造体 */
  char *suit;           /* スート */
  int  number;		/* 数字 */
  char *name;		/* 読み名 */
} aCard;

typedef struct {	/* 山、手札などカードの集合体 */
  aCard card[CARDS];	/* 単一カードが52枚 */
  int   n;		/* 今何枚あるか */
} Cards;

void initialize(Cards *d)	/* Cards構造体のポインタ */
{
  static char *suits[] = {
    "ハート", "スペード", "ダイヤ", "クラブ", "ジョーカー"
  };
  static char *numbers[] = {
    "JOKER", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
    "J", "Q", "K"
  };				/* ここまでは initcard2 と同じ*/

  int s, n, i;
  for (i=0; i<CARDS; i++) {
    s = i/13;                   /* 13で割ると切捨て */
    n = i%13 + 1;               /* 13で割った余り+1 */
    /* d->card がaCard構造体の配列メンバになる。
       d->cardは配列なのでd->card[x] でアクセスする。 */
    d->card[i].suit=suits[s];
    d->card[i].number = n;
    d->card[i].name = numbers[n];
  }
  d->n = 52;		/* この山が持っている枚数をセット */
}

void shuffle(Cards *d)
{
  int i, pair;
  aCard work;
  int length=d->n;
  srandom(time(NULL));
  for (i=0; i<length; i++) {
    pair = random()%length;  /* 交換相手の決定 */
    memcpy(&work, &d->card[i], sizeof work);
    memcpy(&d->card[i], &d->card[pair], sizeof work);
    memcpy(&d->card[pair], &work, sizeof work);
  }
}

void dispcard(aCard c)
{
  printf("%sの%s\n", c.suit, c.name);
}

int main(int argc, char *argv[])
{
  Cards deck, human;
  int i;
  initialize(&deck);
  shuffle(&deck);
  /* 人間に5枚配る */
  for (human.n=0; human.n<5; human.n++) {
    memcpy(&human.card[human.n], &deck.card[--deck.n], sizeof (aCard));
  }
  
  puts("あなたの手は");
  for (i=0; i<human.n; i++) {
    dispcard(human.card[i]);
  }
}

役の判定

配られた5枚のカードを役判定する。ポーカーの役は以下の通り。

ハイカード

役なし

ワンペア

1組だけ数の同じカードが2枚ある

ツーペア

2組数の同じカードが2枚ずつある

スリーカード

1組数の同じカードが3枚ある

ストレート

スートが揃ってない状態の連続した5つの数になっている(K の次は A につながる)

フラッシュ

5枚とも同じスート

フルハウス

スリーカードとワンペアが同時にある組み合わせ

フォーカード

同じ数のカードが4枚ある

ストレートフラッシュ

フラッシュとストレートが同時にある組み合わせ

ロイヤルストレートフラッシュ

10, J, Q, K, A でできたストレートフラッシュ

役を構成している数やスートも強さの基準となることがあるが、最初は 気にしないことにする。

5枚の手札の組み合わせから役を判定するには、それぞれの役を判定する 関数を作成し、役に一致する場合には1、そうでない場合には0を返すようにする。 各判定関数を is_役名()という名前の関数にし、

int is_royalStraightFlush(Cards *c) { .... }
int is_straightFlush(Cards *c) { .... }
int is_fourCards(Cards *c) { .... }
int is_fullHouse(Cards *c) { .... }
int is_flush(Cards *c) { .... }
int is_straight(Cards *c) { .... }
int is_threeCards(Cards *c) { .... }
int is_twoPairs(Cards *c) { .... }
int is_onePair(Cards *c) { .... }

とそれぞれ作っておく。それを利用して役判定をする部分を作る。

int check(Cards *c)
 ....
 if (is_royalStraightFlush(c)) {
   printf("ロイヤルストレートフラッシュです\n");
 } else if (is_straightFlush(c)) {
   printf("ストレートフラッシュです\n");
 } else if (is_fourCards(c)) {
   printf("フォーカードです\n");
 } else if (is_fullHouse(c)) {
   printf("フルハウスです\n");
 } else if (is_flush(c)) {
   printf("フラッシュです\n");
 } else if (is_straight(c)) {
   printf("ストレートです\n");
 } else if (is_threeCards(c)) {
   printf("スリーカードです\n");
 } else if (is_twoPairs(c)) {
   printf("ツーペアです\n");
 } else if (is_onePair(c)) {
   printf("ワンペアです\n");
 } else {
   printf("ハイカードです\n");
 }

if の条件部分では、0以外の値を取るものが「真」になるので、 役判定関数のうち初めて0以外を返した役の名前が表示される。

役判定関数の例

ワンペアを判定する関数を作ってみよう。カードの「数」の部分だけを 調べて、その「数」の登場回数を数えるようにする。登場回数が1以上のものが 4種類あればワンペアである。

int is_onePair(Cards *c)
{
  int box[14];          /* 該当番号が何個あるかの配列 */
  int i, j;
  int sum=0;		/* 1枚以上のカードが何種類? */
  for (i=1; i<=13; i++) {/* 全て0に初期化 box[0] は使わない */
    box[i] = 0;
  }
  for (j=0; j<c->n; j++) {      /* Count each number */
    /* c->card[j] が手持の5枚目のカードの構造体 */
    /* c->card[j].number は、その数字 */
    box[c->card[j].number]++;
  }
  for (i=1; i<=13; i++) { /* A〜Kまでのboxを調べる */
    if (box[i]>0)	/* 1以上のものがあれば sum を1増やす */
      sum++;
  }
  if (sum == 4)
    return 1;
  return 0;
}

暫定完成形

poker.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define CARDS   52       /* 難しいのでジョーカー抜き */

typedef struct {	/* 1枚のカードの構造体 */
  char *suit;           /* スート */
  int  number;		/* 数字 */
  char *name;		/* 読み名 */
} aCard;

typedef struct {	/* 山、手札などカードの集合体 */
  aCard card[CARDS];	/* 単一カードが52枚 */
  int   n;		/* 今何枚あるか */
} Cards;

char *hand[] = {	/* 役の名前の配列 */
  "ハイカード",
  "ワンペア",
  "ツーペア",
  "スリーカード",
  "ストレート",
  "フラッシュ",
  "フルハウス",
  "フォーカード",
  "ストレートフラッシュ",
  "ロイヤルストレートフラッシュ",
};

void initialize(Cards *d)	/* Cards構造体のポインタ */
{
  static char *suits[] = {
    "ハート", "スペード", "ダイヤ", "クラブ", "ジョーカー"
  };
  static char *numbers[] = {
    "JOKER", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10",
    "J", "Q", "K"
  };				/* ここまでは initcard2 と同じ*/

  int s, n, i;
  for (i=0; i<CARDS; i++) {
    s = i/13;                   /* 13で割ると切捨て */
    n = i%13 + 1;               /* 13で割った余り+1 */
    /* d->card がaCard構造体の配列メンバになる。
       d->cardは配列なのでd->card[x] でアクセスする。 */
    d->card[i].suit=suits[s];
    d->card[i].number = n;
    d->card[i].name = numbers[n];
  }
  d->n = 52;		/* この山が持っている枚数をセット */
}

void shuffle(Cards *d)
{
  int i, pair;
  int length=d->n;
  aCard work;
  srandom(time(NULL));
  for (i=0; i<length; i++) {
    pair = random()%length;  /* 交換相手の決定 */
    memcpy(&work, &d->card[i], sizeof work);
    memcpy(&d->card[i], &d->card[pair], sizeof work);
    memcpy(&d->card[pair], &work, sizeof work);
  }
}

void dispcard(aCard c)
{
  printf("%sの%s\n", c.suit, c.name);
}

int is_royalStraightFlush(Cards *c) { return 0; }
int is_straightFlush(Cards *c) { return 0; }
int is_fourCards(Cards *c) { return 0; }
int is_fullHouse(Cards *c) { return 0; }
int is_flush(Cards *c) { return 0; }
int is_straight(Cards *c) { return 0; }
int is_threeCards(Cards *c) { return 0; }
int is_twoPairs(Cards *c) { return 0; }
int is_onePair(Cards *c)
{
  int box[14];          /* 該当番号が何個あるかの配列 */
  int i, j;
  int sum=0;		/* 1枚以上のカードが何種類? */
  for (i=1; i<=13; i++) {/* 全て0に初期化 box[0] は使わない */
    box[i] = 0;
  }
  for (j=0; j<c->n; j++) {      /* Count each number */
    /* c->card[j] が手持の5枚目のカードの構造体 */
    /* c->card[j].number は、その数字 */
    box[c->card[j].number]++;
  }
  for (i=1; i<=13; i++) {
    if (box[i]>0)
      sum++;
  }
  printf("sum=%d\n", sum);
  if (sum == 4)
    return 1;
  return 0;
}

int judge(Cards *c)
{
  if (is_royalStraightFlush(c)) {
    printf("ロイヤルストレートフラッシュです\n");
  } else if (is_straightFlush(c)) {
    printf("ストレートフラッシュです\n");
  } else if (is_fourCards(c)) {
    printf("フォーカードです\n");
  } else if (is_fullHouse(c)) {
    printf("フルハウスです\n");
  } else if (is_flush(c)) {
    printf("フラッシュです\n");
  } else if (is_straight(c)) {
    printf("ストレートです\n");
  } else if (is_threeCards(c)) {
    printf("スリーカードです\n");
  } else if (is_twoPairs(c)) {
    printf("ツーペアです\n");
  } else if (is_onePair(c)) {
    printf("ワンペアです\n");
  } else {
    printf("ハイカードです\n");
  }
}

int main(int argc, char *argv[])
{
  Cards deck, human, com;
  int i;
  initialize(&deck);
  shuffle(&deck);
  /* 人間に5枚配る */
  for (human.n=0; human.n<5; human.n++) {
    memcpy(&human.card[human.n], &deck.card[--deck.n], sizeof (aCard));
  }
  
  puts("あなたの手は");
  for (i=0; i<human.n; i++) {
    dispcard(human.card[i]);
  }
  judge(&human);
}

目次