では、関数を使って少しボリュームのあるプログラムを作りましょう。
新しく作るとなると説明が長くなりますので、以前作ったポーカーを関数を使って作ってみます。
一度作っていますので、細かい部分の説明は避けて、関数に分割する部分を中心に説明します。
まずは、main関数を作り、必要なデータ(構造体や配列)を準備します。
<sample program 143-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]; return 0; } |
※とりあえず上記のデータを用意しておき、新しい変数などが必要になったら追加しましょう。
改めてゲームの流れを見てみます。
・デッキを初期化する ↓ ・デッキをシャッフルする ↓ ・手札を配る ↓ ・変更するカードを選択する ↓ ・カードを変更する ↓ ・手役を判定する ↓ ・終了 |
基本的には、この流れを1つ1つ関数に分けて作っていきます。
しかし、以前のプログラムを見るともう少し細かい機能に分けて作っています。
細かい部分も含めた、ゲームの流れを書きます。
1.デッキを初期化する ↓ 2.デッキをシャッフルする ↓ 3.手札を配る ↓ 4.手札をソートする ↓ 5.手札を表示する ↓ 6.変更するカードを選択する(完了したら8へ) ↓ 7.手札を表示する(6へ戻る) ↓ 8.カードを変更する ↓ 9.手札をソートする ↓ 10.手札を表示する ↓ 11.手役を判定して表示する ↓ 終了 |
4と9の「手札をソートする」、5と7と10の「手札を表示する」は関数にすれば1つ作れば使いまわしが出来そうです。
ここからすぐに作り始めるのではなく、関数名や引数、戻り値などをよく考えてから作ることにしましょう。
1.デッキを初期化する
関数名 InitializeDeck 戻り値 なし 引 数 デッキ 機 能 渡されたデッキにトランプのデータを格納する |
2.デッキをシャッフルする
関数名 ShuffleDeck 戻り値 なし 引 数 デッキ 機 能 渡されたデッキの中身をシャッフルする |
3.手札を配る
関数名 DealCard 戻り値 なし 引 数 デッキ、手札 機 能 デッキの先頭5枚を手札にコピーする |
4、9.手札をソートする
関数名 SortHand 戻り値 なし 引 数 手札 機 能 手札を番号の昇順でソートする |
5、7、10.手札を表示する
関数名 ShowHand 戻り値 なし 引 数 手札 機 能 手札を表示する |
6.変更するカードを選択する(完了したら8へ)
関数名 SelectCard 戻り値 なし 引 数 手札 機 能 変更するカードを選択する 0〜4の入力で選択(フラグを立てる) 5の入力で関数から戻る |
8.カードを変更する
関数名 ChangeCard 戻り値 なし 引 数 デッキ、手札 機 能 手札のフラグが立っているカードのみ デッキの5枚目から必要枚数分配る |
11.手役を判定して表示する
関数名 Result 戻り値 なし 引 数 手札 機 能 手役を判定し表示する |
全部戻り値の無い関数になってしまいましたが、1つずつ関数を作ってmain関数から呼び出します。
1.デッキを初期化する
関数名 InitializeDeck 戻り値 なし 引 数 デッキ 機 能 渡されたデッキにトランプのデータを格納する |
void InitializeDeck(Card deck[CARD_NUM]) { int i; for (i = 0; i < CARD_NUM; i++) { deck[i].suit = i / NUMBER; deck[i].number = i % NUMBER + 1; deck[i].selectFlag = 0; } } |
この関数をmain関数を書いたcppファイルに追加していきます。
※プログラムが長くなりますので、main関数部分だけ追加していきます。
※プロトタイプ宣言と関数本体は皆さんで追加してください。
<sample program 143-02>
int main(void)
{
Card deck[CARD_NUM];
Card hand[HAND_NUM];
/* デッキの初期化 */
InitializeDeck(deck);
return 0;
}
|
※デッキのチェック用のデバッグコードは書きませんが、確かめたい人はデバッグ用の関数を作って確認してみてください。
2.デッキをシャッフルする
関数名 ShuffleDeck 戻り値 なし 引 数 デッキ 機 能 渡されたデッキの中身をシャッフルする |
void ShuffleDeck(Card deck[CARD_NUM]) { int i; int index1, index2; Card work; 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; } } |
関数内でしか使わない変数(i, index1, index2, work)は関数内で宣言しましょう。
乱数を使うため、乱数の種を設定しなければならないのですが、乱数の種はプログラム全体で1回だけ設定すれば十分ですので、main関数に書きます。
<sample program 143-03>
int main(void) { Card deck[CARD_NUM]; Card hand[HAND_NUM]; srand((unsigned int)time(NULL)); /* デッキの初期化 */ InitializeDeck(deck); /* デッキのシャッフル */ ShuffleDeck(deck); return 0; } |
3.手札を配る
関数名 DealCard 戻り値 なし 引 数 デッキ、手札 機 能 デッキの先頭5枚を手札にコピーする |
void DealCard(const Card deck[CARD_NUM], Card hand[HAND_NUM]) { int i; for (i = 0; i < HAND_NUM; i++) { hand[i] = deck[i]; } } |
第1引数のデッキは関数内で中身の変更はありませんから「const」を付けています。
<sample program 143-04>
int main(void)
{
Card deck[CARD_NUM];
Card hand[HAND_NUM];
srand((unsigned int)time(NULL));
/* デッキの初期化 */
InitializeDeck(deck);
/* デッキのシャッフル */
ShuffleDeck(deck);
/* 手札を配る */
DealCard(deck, hand);
return 0;
}
|
完成形は最後に記載します。