カードのシャッフルと配布

initdata2.cでは 構造体 Card の53個分の配列(deck)のなかに、 ハートのAから、ジョーカーまでのカードが順番に並んでいる。 実際にゲームをするにはシャッフルしなければならない。また、 構造体の1要素として保持しているものを画面に分かりやすく出す必要もある。

配列要素のシャッフル

配列に入っているものをでたらめな順番に並べ換えるときには、 配列の先頭から要素を見て行き、

  1. 0番目要素と ランダムな位置の要素を交換する
  2. 1番目要素と ランダムな位置の要素を交換する
  3. 2番目要素と ランダムな位置の要素を交換する
    :
    :
    以下同様
    :
  1. 51番目要素と ランダムな位置の要素を交換する
  2. 52番目要素と ランダムな位置の要素を交換する

という動作を繰り返せば良い。そのために必要な

方法について考えよう。

乱数の使い方(復習)

乱数(正確には擬似乱数)を得るにはrandom()関数を使う。 random()は、

#include <stdlib.h>

  :
/* どれかの関数の中で */
  :
  long r;
  srandom(time(NULL));
  r = random();

のようにして利用する。srandom() は乱数の種を初期化する関 数で、通常 time(NULL) で現在時刻(の整数)を与える。

random()はlong型の値を取るので、ある整数以下の乱数を 取り出したいときは、その整数で割った余りを求めれば良い。0〜52のいずれか を取り出したければ、

x = random()%53;

などとする。

値の交換

int型などの整数の値を交換するには、

 /* x と y の値を交換する ダメな例*/
 x = y; ×
 y = x; ×

ではうまく行かない。正しくは、

 /* x と y の値を交換する */
 int w;		/* 作業用の変数が必要 */
 w = x;
 x = y;
 y = w;

のようにする。構造体の値を交換するには一気に代入ができないので

 /* 構造体 a と b の値を交換する ダメな例 */
 /* a, b は構造体 */
 struct foo w;	/* 作業用の変数が必要 */
 w = x; ×
 x = y; ×
 y = w; ×
 構造体を = で一気に代入することはできない

構造体のメンバごとに、代入(数値の場合)かstrlcpy()(文字列の 場合)を繰り返すのが確実だが、構造体の1変数まるごとコピーするには memcopy()を使うと簡単である。

 #include <string.h>  /* 忘れるな! */

 memcpy(コピー先変数のアドレス, コピー元変数のアドレス, バイト数);

memcpy() を使うときにどのヘッダファイルが必要なのかは 良く忘れる。忘れた場合は、

man memcpy

とすると、必要なヘッダファイルや関数の引数の数などの説明が得られる。 man を使えば、strcpy()random() などC言語の全ての ライブラリ関数について調べることができる。

構造体変数 a, b の交換を行なうには以下のようにする。

 /* 構造体 a と b の値を交換する */
 /* a, b は構造体 */
 struct foo w;	/* 作業用の変数が必要 */
 memcpy(&w, &x, sizeof w);
 memcpy(&x, &y, sizeof w);
 memcpy(&y, &w, sizeof w);

以上の知識をふまえて、構造体配列をシャッフルする関数を作る。

#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct card {
  char *suit;
  int  number;
  char *name;
} Card;

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

カードの配布と表示

カードゲームはおおむね、

という手順で進行する。配る前の山となる場所、プレイヤー1人1人が 手札として持つ場所、など全て構造体配列で用意する必要があるだろう。 これらの流れをCのプログラムにすると以下のような構成になる。

ヘッダファイルの読み込み(#include)
各種定数の宣言(#define)

カードの構造体のtypedef

53枚のカードを初期化する関数定義
シャッフルする関数定義

int main()
{
  53枚のカードの構造体の配列を宣言
  初期化する
  シャッフルする

  1枚ずつ配る(最初は画面に出すだけで良い)
}

目次