カードの役割に意味を持たせる

前回作成したshuffle.c を元に、トランプらしいルールを付け加えて行こう。なお、今回から ルールを付け加えて行くので、ジョーカーなしに単純化して考える。

手札の確保

人間とコンピュータが対戦できるように、人間の持ち札とコンピュータの 持ち札を変数として用意しよう。どんなゲームをする場合でもトランプ全部の 枚数よりも手札が多くなることはないだろう。ただ、「今何枚手札があるか」 という情報はつねに把握しておく必要がある。

これを踏まえて、トランプのカードを53枚持てる構造体を作ると 持ち札の管理がしやすそうである。つまり、1枚のトランプの構造体が

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

のように定義されたものとすると、これを最大53枚持てる構造体、

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

を定義すると、山と手札の管理を一つの構造体変数で行なえるようになる。 このように、構造体のメンバに更に構造体を含めても良い。

カードの初期化

上記の構造体 Cards を利用する場合、ハートのAから クラブのKまでカードを作る方法を考えよう。先週の initcard2.c では カード1枚を保持する構造体Cardの配列を受け取って 初期化したのだが、今週は一つの構造体が山(または手札)全体を保持することに なるので、それに注意して初期化関数を書くと以下のようになる。

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;
  srandom(time(NULL));
  aCard work;
  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);
  }
}

initialize, shuffle を利用して、52枚のカードを シャフルした状態で山に積む部分は以下のようになる。

  Cards deck;
  initialize(&deck);
  shuffle(&deck);

目次