それでは、これまでのことを総合してポーカーゲームを作ってみます。
最初にゲームの概要と流れを定義します。
<ゲーム概要>
1人用ポーカーゲーム 手札は5枚でジョーカー無し カードのチェンジは1回のみ ゲームは1回で終了する |
<ゲームの流れ>
デッキを初期化する ↓ デッキをシャッフルする ↓ 手札としてデッキの先頭から5枚配る ↓ 変更するカードを選択する ↓ カードを変更する ↓ 手役を判定する ↓ 終了 |
簡単に書きましたが、プログラムとして成立させるためには、1つ1つの動作をもっと具体的に考えていかなければなりません。
順を追って作成しましょう。
まずは、必要な定数と構造体の宣言を行います。
<sample program 105-01>
#include <stdio.h> #define NUMBER 13 #define MARK 4 #define CARD_NUM (NUMBER * MARK) /* カード構造体 */ typedef struct { int number; int suit; } Card; int main(void) { Card deck[CARD_NUM]; /* デッキ */ return 0; } |
前に説明した形とほぼ同じです。
次に初期値の設定ですが、これもやりました。
<sample program 105-02>
#include <stdio.h>
#define NUMBER 13
#define MARK 4
#define CARD_NUM (NUMBER * MARK)
/* カード構造体 */
typedef struct
{
int number;
int suit;
} Card;
int main(void)
{
Card deck[CARD_NUM]; /* デッキ */
int i;
/* デッキの初期化 */
for (i = 0; i < CARD_NUM; i++) {
deck[i].suit = i / NUMBER;
deck[i].number = i % NUMBER + 1;
}
return 0;
}
|
ここでデッキの中身を確認するためのデバッグコードを書きましょう。
numberはそのまま表示すれば良いのですが、スートは数値では分かりにくいですね。
<suit>
0 スペード 1 ハート 2 ダイヤ 3 クラブ |
これも分かりやすいように対処したいと思います。
<sample program 105-03>
#include <stdio.h>
#define NUMBER 13
#define MARK 4
#define CARD_NUM (NUMBER * MARK)
/* カード構造体 */
typedef struct
{
int number;
int suit;
} Card;
int main(void)
{
Card deck[CARD_NUM]; /* デッキ */
int i;
/* デッキの初期化 */
for (i = 0; i < CARD_NUM; i++) {
deck[i].suit = i / NUMBER;
deck[i].number = i % NUMBER + 1;
}
/* デッキの確認(デバッグ用) */
for (i = 0; i < CARD_NUM; i++) {
switch (deck[i].suit) {
case 0:
printf("スペード:");
break;
case 1:
printf("ハ ー ト:");
break;
case 2:
printf("ダ イ ヤ:");
break;
case 3:
printf("ク ラ ブ:");
break;
}
printf("%2d\n", deck[i].number);
}
return 0;
}
|
<実行結果>
スペード: 1 スペード: 2 スペード: 3 スペード: 4 スペード: 5 スペード: 6 スペード: 7 スペード: 8 スペード: 9 スペード:10 スペード:11 スペード:12 スペード:13 ハ ー ト: 1 ハ ー ト: 2 ハ ー ト: 3 ハ ー ト: 4 ハ ー ト: 5 ハ ー ト: 6 ハ ー ト: 7 ハ ー ト: 8 ハ ー ト: 9 ハ ー ト:10 ハ ー ト:11 ハ ー ト:12 ハ ー ト:13 ダ イ ヤ: 1 ダ イ ヤ: 2 ダ イ ヤ: 3 ダ イ ヤ: 4 ダ イ ヤ: 5 ダ イ ヤ: 6 ダ イ ヤ: 7 ダ イ ヤ: 8 ダ イ ヤ: 9 ダ イ ヤ:10 ダ イ ヤ:11 ダ イ ヤ:12 ダ イ ヤ:13 ク ラ ブ: 1 ク ラ ブ: 2 ク ラ ブ: 3 ク ラ ブ: 4 ク ラ ブ: 5 ク ラ ブ: 6 ク ラ ブ: 7 ク ラ ブ: 8 ク ラ ブ: 9 ク ラ ブ:10 ク ラ ブ:11 ク ラ ブ:12 ク ラ ブ:13 続行するには何かキーを押してください・・・
それぞれのスートごとに1から13までのカードがそろっていることが分かります。
ハート、ダイヤ、クラブは文字の間に半角スペースを入れ、スペードと同じ「全角4文字分」に合わせています。
次はこのデッキをシャッフルしなければなりませんが、実物のトランプがある訳ではないので、どうやったらシャッフル出来るか考えなければなりません。
手っ取り早い方法として↓の方法を使います。
1.乱数を使って0から51までの添え字を2つ生成します。 2.配列の2つの添え字の箇所を入れ替えます。 3.1と2を何度も繰り返すことで順番がバラバラになります。
今回は入れ替えを2千回として作ってみます。
<sample program 105-04>
#include <stdio.h> #include <stdlib.h> #include <time.h> #define NUMBER 13 #define MARK 4 #define CARD_NUM (NUMBER * MARK) #define SHUFFLE_TIME 2000 /* 入れ替え回数 */ /* カード構造体 */ typedef struct { int number; int suit; } Card; int main(void) { Card deck[CARD_NUM]; /* デッキ */ int i; int index1, index2; /* シャッフル用添え字 */ Card work; /* 入れ替え用 */ srand((unsigned int)time(NULL)); /* デッキの初期化 */ for (i = 0; i < CARD_NUM; i++) { deck[i].suit = i / NUMBER; deck[i].number = i % NUMBER + 1; } /* デッキのシャッフル */ 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 < CARD_NUM; i++) { switch (deck[i].suit) { case 0: printf("スペード:"); break; case 1: printf("ハ ー ト:"); break; case 2: printf("ダ イ ヤ:"); break; case 3: printf("ク ラ ブ:"); break; } printf("%2d\n", deck[i].number); } return 0; } |
<実行結果>
ク ラ ブ:11 スペード: 5 ダ イ ヤ: 1 スペード: 7 ハ ー ト: 4 ダ イ ヤ: 8 ダ イ ヤ:11 ダ イ ヤ:10 ク ラ ブ: 6 ク ラ ブ: 8 ダ イ ヤ: 6 スペード: 4 ク ラ ブ: 7 ダ イ ヤ: 4 ハ ー ト:11 スペード:13 ハ ー ト: 8 スペード:11 ダ イ ヤ: 5 ハ ー ト: 2 ハ ー ト: 6 ク ラ ブ: 1 ダ イ ヤ:13 ク ラ ブ:10 スペード:10 ハ ー ト: 1 ク ラ ブ: 9 ハ ー ト: 7 スペード: 1 ダ イ ヤ: 7 スペード: 6 スペード: 9 ハ ー ト:13 スペード: 8 ク ラ ブ: 2 ク ラ ブ: 4 ダ イ ヤ: 3 ハ ー ト: 5 スペード: 3 ダ イ ヤ:12 ク ラ ブ: 5 ダ イ ヤ: 2 ク ラ ブ: 3 スペード: 2 スペード:12 ハ ー ト:12 ハ ー ト: 9 ダ イ ヤ: 9 ハ ー ト:10 ク ラ ブ:12 ク ラ ブ:13 ハ ー ト: 3 続行するには何かキーを押してください・・・
何度か実行して、毎回並びが違うことを確認してください。
確認できたら、デバッグコードは不要になりますので、消しておきましょう。
とは言え、もしかしたら後々このデバッグコードが必要になるかもしれません・・・
#if 0 〜 #endif を使ってプログラムを「無効」にしておきましょう。
<sample program 105-05>
#include <stdio.h> #include <stdlib.h> #include <time.h> #define NUMBER 13 #define MARK 4 #define CARD_NUM (NUMBER * MARK) #define SHUFFLE_TIME 2000 /* 入れ替え回数 */ /* カード構造体 */ typedef struct { int number; int suit; } Card; int main(void) { Card deck[CARD_NUM]; /* デッキ */ int i; int index1, index2; /* シャッフル用添え字 */ Card work; /* 入れ替え用 */ srand((unsigned int)time(NULL)); /* デッキの初期化 */ for (i = 0; i < CARD_NUM; i++) { deck[i].suit = i / NUMBER; deck[i].number = i % NUMBER + 1; } /* デッキのシャッフル */ 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; } #if 0 /* デッキの確認(デバッグ用) */ for (i = 0; i < CARD_NUM; i++) { switch (deck[i].suit) { case 0: printf("スペード:"); break; case 1: printf("ハ ー ト:"); break; case 2: printf("ダ イ ヤ:"); break; case 3: printf("ク ラ ブ:"); break; } printf("%2d\n", deck[i].number); } #endif return 0; } |
#if 0 から #endif の間のコードは「無効」になりコンパイルされなくなります。
#if 1 にすると「有効」になります。
コメント(/* */)でくくる方法も見かけますが、コメントは注釈という意味ですので、こういったケースでは使わない方が良いと思います。
#で始まる命令はプリプロセッサといって色々な設定が出来るのですが、ここでは説明しません。
※上記のデバッグコードはプログラムが長くなるため、以降は記載しません。
次に手札を準備します。
手札は5枚ですので、別途配列を作ります。
<sample program 105-06>
#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; } Card; int main(void) { Card deck[CARD_NUM]; /* デッキ */ Card hand[HAND_NUM]; /* 手札 */ int i; int index1, index2; /* シャッフル用添え字 */ Card work; /* 入れ替え用 */ srand((unsigned int)time(NULL)); /* デッキの初期化 */ for (i = 0; i < CARD_NUM; i++) { deck[i].suit = i / NUMBER; deck[i].number = i % NUMBER + 1; } /* デッキのシャッフル */ 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; } return 0; } |
デッキの先頭から5枚を手札にコピーすることで配ります。
<sample program 105-07>
#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;
} Card;
int main(void)
{
Card deck[CARD_NUM]; /* デッキ */
Card hand[HAND_NUM]; /* 手札 */
int i;
int index1, index2; /* シャッフル用添え字 */
Card work; /* 入れ替え用 */
srand((unsigned int)time(NULL));
/* デッキの初期化 */
for (i = 0; i < CARD_NUM; i++) {
deck[i].suit = i / NUMBER;
deck[i].number = i % NUMBER + 1;
}
/* デッキのシャッフル */
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];
}
return 0;
}
|
手札は表示しなければプレイヤーが確認出来ませんので、デバッグコードで使ったプログラムを元に表示プログラムを作ります。
<sample program 105-08>
#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;
} Card;
int main(void)
{
Card deck[CARD_NUM]; /* デッキ */
Card hand[HAND_NUM]; /* 手札 */
int i;
int index1, index2; /* シャッフル用添え字 */
Card work; /* 入れ替え用 */
srand((unsigned int)time(NULL));
/* デッキの初期化 */
for (i = 0; i < CARD_NUM; i++) {
deck[i].suit = i / NUMBER;
deck[i].number = i % NUMBER + 1;
}
/* デッキのシャッフル */
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; 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;
}
|
<実行結果>
ダ イ ヤ:10 スペード: 5 ク ラ ブ: 8 ク ラ ブ: 2 ダ イ ヤ: 7 続行するには何かキーを押してください・・・
コンピュータのポーカーゲームをプレイしたことをある人なら分かると思いますが、通常手札は小さい順に並んで表示されます。
ストレートなどの役にしても並んでいた方が気づきやすいと思います。
そこで、手札を表示する前にソートを行います
ソートは「隣接交換法」を使います。
<sample program 105-09>
#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; } Card; int main(void) { Card deck[CARD_NUM]; /* デッキ */ Card hand[HAND_NUM]; /* 手札 */ int i, j; int index1, index2; /* シャッフル用添え字 */ Card work; /* 入れ替え用 */ srand((unsigned int)time(NULL)); /* デッキの初期化 */ for (i = 0; i < CARD_NUM; i++) { deck[i].suit = i / NUMBER; deck[i].number = i % NUMBER + 1; } /* デッキのシャッフル */ 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); } return 0; } |
<実行結果>
ハ ー ト: 1 スペード: 3 ク ラ ブ: 8 ダ イ ヤ:11 スペード:12 続行するには何かキーを押してください・・・
何度か実行して、きちんと出来ているか確認してください。
一回ここで区切って、手札の交換は次回にしましょう。