★ゲームを作ろう5(ポーカー4)★


ポーカーゲームのラストとして手役の判定を行います。

全体のプログラムも長くなっているので、なるべく手役の判定だけ抜き出して書くように工夫します。


<sample program 108-01>

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

#define NUMBER 13
#define MARK    4
#define CARD_NUM (NUMBER * MARK)

#define SHUFFLE_TIME 2000 /* 入れ替え回数 */

#define HAND_NUM 5 /* 手札の枚数 */

/* カード構造体 */
typedef struct
{
    int number;
    int suit;
    int selectFlag;
} Card;

int main(void)
{
    Card deck[CARD_NUM]; /* デッキ */

    Card hand[HAND_NUM]; /* 手札 */

    int i, j;

    int index1, index2;  /* シャッフル用添え字 */

    Card work;           /* 入れ替え用 */

    int select;          /* カード選択用 */

    srand((unsigned int)time(NULL));

    /* デッキの初期化 */

    for (i = 0; i < CARD_NUM; i++) {

        deck[i].suit = i / NUMBER;

        deck[i].number = i % NUMBER + 1;

        deck[i].selectFlag = 0;
    }

    /* デッキのシャッフル */

    for (i = 0; i < SHUFFLE_TIME; i++) {

        index1 = rand() % CARD_NUM;

        index2 = rand() % CARD_NUM;

        work = deck[index1];
        deck[index1] = deck[index2];
        deck[index2] = work;
    }

    /* 手札を配る */

    for (i = 0; i < HAND_NUM; i++) {

        hand[i] = deck[i];
    }

    /* 手札のソート */

    for (i = 0; i < HAND_NUM - 1; i++) {
        for (j = 0; j < HAND_NUM - 1; j++) {

            if (hand[j].number > hand[j + 1].number) {
                work = hand[j];
                hand[j] = hand[j + 1];
                hand[j + 1] = work;
            }
        }
    }

    /* 手札の表示 */

    for (i = 0; i < HAND_NUM; i++) {

        switch (hand[i].suit) {
        case 0:
            printf("スペード:");
            break;
        case 1:
            printf("ハ ー ト:");
            break;
        case 2:
            printf("ダ イ ヤ:");
            break;
        case 3:
            printf("ク ラ ブ:");
            break;
        }

        printf("%2d\n", hand[i].number);
    }

    for (;;) {

        /* 入れ替えるカードの選択 */

        do {

            scanf("%d", &select);

        } while (select < 0 || select > HAND_NUM);

        if (select == HAND_NUM) {
            break;
        }

        if (hand[select].selectFlag == 0) {
            hand[select].selectFlag = 1;
        }
        else {
            hand[select].selectFlag = 0;
        }

        /* 選択した手札の表示 */

        for (i = 0; i < HAND_NUM; i++) {

            switch (hand[i].suit) {
            case 0:
                printf("スペード:");
                break;
            case 1:
                printf("ハ ー ト:");
                break;
            case 2:
                printf("ダ イ ヤ:");
                break;
            case 3:
                printf("ク ラ ブ:");
                break;
            }

            printf("%2d", hand[i].number);

            if (hand[i].selectFlag == 1) {
                printf(" *\n");
            }
            else {
                printf("\n");
            }
        }

    }

/* 手札の交換 */

    dealCount = HAND_NUM;

    for (i = 0; i < HAND_NUM; i++) {

        if (hand[i].selectFlag == 1) {

            hand[i] = deck[dealCount];

            dealCount++;
        }
    }

    /* 手札のソート */

    for (i = 0; i < HAND_NUM - 1; i++) {
        for (j = 0; j < HAND_NUM - 1; j++) {

            if (hand[j].number > hand[j + 1].number) {
                work = hand[j];
                hand[j] = hand[j + 1];
                hand[j + 1] = work;
            }
        }
    }

    /* 手札の表示 */

    for (i = 0; i < HAND_NUM; i++) {

        switch (hand[i].suit) {
        case 0:
            printf("スペード:");
            break;
        case 1:
            printf("ハ ー ト:");
            break;
        case 2:
            printf("ダ イ ヤ:");
            break;
        case 3:
            printf("ク ラ ブ:");
            break;
        }

        printf("%2d\n", hand[i].number);
    }

    /* 手役の判定 */

    /* ここに判定プログラムを書く */

    return 0;
}

手役として、以下の役の判定を行います。

<手役>

フォーカード
スリーカード
フルハウス
ツーペア
ワンペア
ストレート
フラッシュ
ストレートフラッシュ

※ストレートは「10JQKA」のように、13を超えて1に戻るものは無しとします。


フォーカード


最初にフォーカードの判定を行います。

手札はソートされていますので、フォーカードは以下の2パターンの内、どちらかになります。

 パターン1(0〜3まで同じ数値)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|1|1|1|2|
   +−+−+−+−+−+

 パターン2(1〜4まで同じ数値)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|2|2|2|2|
   +−+−+−+−+−+

上の2つのどちらかのパターンに当てはまれば、「フォーカード」と表示することにします。

<sample program 108-02>

/* 手役の判定 */

if (hand[0].number == hand[1].number &&
    hand[1].number == hand[2].number &&
    hand[2].number == hand[3].number) {
    printf("フォーカード\n");
}

if (hand[1].number == hand[2].number &&
    hand[2].number == hand[3].number &&
    hand[3].number == hand[4].number) {
    printf("フォーカード\n");
}

ベタなプログラムですが、この2パターンしかありませんので、簡単にチェックしましょう。

とりあえず、上手く動作するかどうかチェックしたいのですが、普通にフォーカードは出ませんよね・・・

このような場合も「デバッグ用コード」で対応します。

手札の交換が終わった後、ソートする前にカードを全部入れ替えます。

<sample program 108-03>

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

#define NUMBER 13
#define MARK    4
#define CARD_NUM (NUMBER * MARK)

#define SHUFFLE_TIME 2000 /* 入れ替え回数 */

#define HAND_NUM 5 /* 手札の枚数 */

/* カード構造体 */
typedef struct
{
    int number;
    int suit;
    int selectFlag;
} Card;

int main(void)
{
    Card deck[CARD_NUM]; /* デッキ */

    Card hand[HAND_NUM]; /* 手札 */

    int i, j;

    int index1, index2;  /* シャッフル用添え字 */

    Card work;           /* 入れ替え用 */

    int select;          /* カード選択用 */

    int dealCount;       /* デッキ用 */

    srand((unsigned int)time(NULL));

    /* デッキの初期化 */

    for (i = 0; i < CARD_NUM; i++) {

        deck[i].suit = i / NUMBER;

        deck[i].number = i % NUMBER + 1;

        deck[i].selectFlag = 0;
    }

    /* デッキのシャッフル */

    for (i = 0; i < SHUFFLE_TIME; i++) {

        index1 = rand() % CARD_NUM;

        index2 = rand() % CARD_NUM;

        work = deck[index1];
        deck[index1] = deck[index2];
        deck[index2] = work;
    }

    /* 手札を配る */

    for (i = 0; i < HAND_NUM; i++) {

        hand[i] = deck[i];
    }

    /* 手札のソート */

    for (i = 0; i < HAND_NUM - 1; i++) {
        for (j = 0; j < HAND_NUM - 1; j++) {

            if (hand[j].number > hand[j + 1].number) {
                work = hand[j];
                hand[j] = hand[j + 1];
                hand[j + 1] = work;
            }
        }
    }

    /* 手札の表示 */

    for (i = 0; i < HAND_NUM; i++) {

        switch (hand[i].suit) {
        case 0:
            printf("スペード:");
            break;
        case 1:
            printf("ハ ー ト:");
            break;
        case 2:
            printf("ダ イ ヤ:");
            break;
        case 3:
            printf("ク ラ ブ:");
            break;
        }

        printf("%2d\n", hand[i].number);
    }

    for (;;) {

        /* 入れ替えるカードの選択 */

        do {

            scanf("%d", &select);

        } while (select < 0 || select > HAND_NUM);

        if (select == HAND_NUM) {
            break;
        }

        if (hand[select].selectFlag == 0) {
            hand[select].selectFlag = 1;
        }
        else {
            hand[select].selectFlag = 0;
        }

        /* 選択した手札の表示 */

        for (i = 0; i < HAND_NUM; i++) {

            switch (hand[i].suit) {
            case 0:
                printf("スペード:");
                break;
            case 1:
                printf("ハ ー ト:");
                break;
            case 2:
                printf("ダ イ ヤ:");
                break;
            case 3:
                printf("ク ラ ブ:");
                break;
            }

            printf("%2d", hand[i].number);

            if (hand[i].selectFlag == 1) {
                printf(" *\n");
            }
            else {
                printf("\n");
            }
        }
    }

    /* 手札の交換 */

    dealCount = HAND_NUM;

    for (i = 0; i < HAND_NUM; i++) {

        if (hand[i].selectFlag == 1) {

            hand[i] = deck[dealCount];

            dealCount++;
        }
    }

    /* デバッグ用コード */
    hand[0].number = 1;
    hand[1].number = 1;
    hand[2].number = 1;
    hand[3].number = 1;
    hand[4].number = 2;

    /* 手札のソート */

    for (i = 0; i < HAND_NUM - 1; i++) {
        for (j = 0; j < HAND_NUM - 1; j++) {

            if (hand[j].number > hand[j + 1].number) {
                work = hand[j];
                hand[j] = hand[j + 1];
                hand[j + 1] = work;
            }
        }
    }

    /* 手札の表示 */

    for (i = 0; i < HAND_NUM; i++) {

        switch (hand[i].suit) {
        case 0:
            printf("スペード:");
            break;
        case 1:
            printf("ハ ー ト:");
            break;
        case 2:
            printf("ダ イ ヤ:");
            break;
        case 3:
            printf("ク ラ ブ:");
            break;
        }

        printf("%2d\n", hand[i].number);
    }

    /* 手役の判定 */

    if (hand[0].number == hand[1].number &&
        hand[1].number == hand[2].number &&
        hand[2].number == hand[3].number) {
        printf("フォーカード\n");
    }

    if (hand[1].number == hand[2].number &&
        hand[2].number == hand[3].number &&
        hand[3].number == hand[4].number) {
        printf("フォーカード\n");
    }

    return 0;
}

<実行結果1>

ハ ー ト: 1
スペード: 2
ハ ー ト: 5
ハ ー ト: 8
スペード:10
5
ハ ー ト: 1
スペード: 1
ハ ー ト: 1
ハ ー ト: 1
スペード: 2
フォーカード
続行するには何かキーを押してください・・・

ハートの1が3枚ありますので、実際にはあり得ない状態ですが、きちんと「フォーカード」と表示されました。

どの手役をチェックするにも、デバッグ用コードを変えて実験すればよさそうです。


スリーカード


では、スリーカードについて考えてみましょう。

スリーカードは次の3パターンが考えられます。

 パターン1(0〜2まで同じ数値)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|1|1|2|3|
   +−+−+−+−+−+

 パターン2(1〜3まで同じ数値)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|2|2|2|3|
   +−+−+−+−+−+

 パターン3(2〜4まで同じ数値)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|2|3|3|3|
   +−+−+−+−+−+

とりあえずこの3パターンをフォーカードのチェックの下に書いてみましょう。

<sample program 108-04>

if (hand[0].number == hand[1].number &&
    hand[1].number == hand[2].number) {
    printf("スリーカード\n");
}

if (hand[1].number == hand[2].number &&
    hand[2].number == hand[3].number) {
    printf("スリーカード\n");
}

if (hand[2].number == hand[3].number &&
    hand[3].number == hand[4].number) {
    printf("スリーカード\n");
}

デバッグ用コードを書き直し、3パターンともチェックしてみてください。


この時点で抱えている問題点について指摘し、対応策を考えます。

デバッグ用コードをフォーカードの状態にします。

/* デバッグ用コード */
hand[0].number = 1;
hand[1].number = 1;
hand[2].number = 1;
hand[3].number = 1;
hand[4].number = 2;

このまま実行し、すぐに5を入力します。

<実行結果>

ハ ー ト: 1
ク ラ ブ: 4
スペード: 5
ダ イ ヤ: 6
ハ ー ト: 7
5
ハ ー ト: 1
ク ラ ブ: 1
スペード: 1
ダ イ ヤ: 1
ハ ー ト: 2
フォーカード
スリーカード
スリーカード
続行するには何かキーを押してください・・・

フォーカードの後に続いてスリーカードの表示が2回出ています。

これは、フォーカードのチェックを行った後、スリーカードのチェックも行っていることが原因です。

フォーカードはスリーカードの2つのパターンの条件を満たしています。

これを回避するためフォーカードであれば、他の役のチェックを行わないようにしなければなりません。

方法はいくつかありますが、今回はループを抜けるbreakを使った方法を紹介します。

for (;;) {

}

これは無限ループですが、これを下のように変えると、

for (;;) {

    break;
}

一度もループしないループ?になります。

これを使って、今までの手役の判定部分を書き換えてみます。

<sample program 108-05>

/* 手役の判定 */

for (;;) {

    if (hand[0].number == hand[1].number &&
        hand[1].number == hand[2].number &&
        hand[2].number == hand[3].number) {
        printf("フォーカード\n");
        break;
    }

    if (hand[1].number == hand[2].number &&
        hand[2].number == hand[3].number &&
        hand[3].number == hand[4].number) {
        printf("フォーカード\n");
        break;
    }

    if (hand[0].number == hand[1].number &&
        hand[1].number == hand[2].number) {
        printf("スリーカード\n");
        break;
    }

    if (hand[1].number == hand[2].number &&
        hand[2].number == hand[3].number) {
        printf("スリーカード\n");
        break;
    }

    if (hand[2].number == hand[3].number &&
        hand[3].number == hand[4].number) {
        printf("スリーカード\n");
        break;
    }

    break;
}

どれかの役の条件に当てはまれば、breakして抜け出すため他の役の判定は行われません。

どの役にも当てはまらない場合、最後のbreakで絶対に抜けますので、無限ループにはなりません。

これで先ほどのフォーカードのチェックを行うと。

<実行結果>

スペード: 2
ハ ー ト: 3
ク ラ ブ: 4
ダ イ ヤ: 6
ダ イ ヤ: 8
5
スペード: 1
ハ ー ト: 1
ク ラ ブ: 1
ダ イ ヤ: 1
ダ イ ヤ: 2
フォーカード
続行するには何かキーを押してください・・・

これで上手くいきそうです。

残りのペア系の役を判定しましょう。


フルハウス


フルハウスはスリーカードとワンペアの組み合わせです。

パターンとしては2パターンです。

 パターン1(0〜2まで同じ数値、3・4でワンペア)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|1|1|2|2|
   +−+−+−+−+−+
 
 パターン2(2〜4まで同じ数値、0・1でワンペア)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|1|2|2|2|
   +−+−+−+−+−+

スリーカードの条件はすでに作ってあるため、これを改良して作りましょう。

<sample program 108-06>

if (hand[0].number == hand[1].number &&
    hand[1].number == hand[2].number) {

    if (hand[3].number == hand[4].number) {
        printf("フルハウス\n");
    }
    else{
        printf("スリーカード\n");
    }

    break;
}

if (hand[1].number == hand[2].number &&
    hand[2].number == hand[3].number) {

    printf("スリーカード\n");

    break;
}

if (hand[2].number == hand[3].number &&
    hand[3].number == hand[4].number) {

    if (hand[0].number == hand[1].number) {
        printf("フルハウス\n");
    }
    else{
        printf("スリーカード\n");
    }

    break;
}

デバッグ用コードを書き換え、チェックしてみてください。


ツーペア


ツーペアは以下の3パターンです。

 パターン1(0・1と2・3がペア)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|1|2|2|3|
   +−+−+−+−+−+

 パターン2(0・1と3・4がペア)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|1|2|3|3|
   +−+−+−+−+−+

 パターン3(1・2と3・4がペア)
    0 1 2 3 4
   +−+−+−+−+−+
 手札|1|2|2|3|3|
   +−+−+−+−+−+

<sample program 108-07>

if (hand[0].number == hand[1].number &&
    hand[2].number == hand[3].number) {
    printf("ツーペア\n");
    break;
 }

if (hand[0].number == hand[1].number &&
    hand[3].number == hand[4].number) {
    printf("ツーペア\n");
    break;
}

if (hand[1].number == hand[2].number &&
    hand[3].number == hand[4].number) {
    printf("ツーペア\n");
    break;
}

ワンペア


ワンペアは以下の4パターンになります。

パターン1(0・1がペア)
   0 1 2 3 4
  +−+−+−+−+−+
手札|1|1|2|3|4|
  +−+−+−+−+−+

パターン2(1・2がペア)
   0 1 2 3 4
  +−+−+−+−+−+
手札|1|2|2|3|4|
  +−+−+−+−+−+

パターン3(2・3がペア)
   0 1 2 3 4
  +−+−+−+−+−+
手札|1|2|3|3|4|
  +−+−+−+−+−+

パターン4(3・4がペア)
   0 1 2 3 4
  +−+−+−+−+−+
手札|1|2|3|4|4|
  +−+−+−+−+−+

<sample program 108-08>

if (hand[0].number == hand[1].number ||
    hand[1].number == hand[2].number ||
    hand[2].number == hand[3].number ||
    hand[3].number == hand[4].number) {
    printf("ワンペア\n");
    break;
}

これでペア系は終わりです。

デバッグ用コードを「無効」にして少し遊んでみてください。

残りの役は次回にしましょう。


次へ

戻る

目次へ