ブラックジャック【C言語】

ブラックジャック【C言語】

C言語講座で勉強した内容を復習するのにうってつけのゲームを思いつきました。

前回のジャンケンゲームよりは実装が重めかもしれません。内容がよくわからない方は講座記事を一読してから見てみると良いかもです。

製作物の概要

ブラックジャックについてのルールを簡単に記述しておきます。

  • トランプを最初に2枚引きます。
  • 引いたトランプの数の合計が22以上にならない範囲で21に近くなるようにトランプを追加で引きます。
  • 一番21に近い人が勝ちです。
  • 22以上の人は無条件に負けになります。
  • トランプの『A』は1or11の都合の良い方を採用します。
  • トランプの『J』・『Q』・『K』は全部10として考えます。

概ねこんな感じのルールだと思います。

今回作るのは簡単のためにプレイヤー1人vsコンピュータ1人の対戦形式とし、相手の手持ちは見えないという設定で実装していきます。

製作過程

早速コードを実装していきます。まずはお決まりのmain関数を書きファイルを作ります。

#include <stdio.h>

int main() {
  return 0;
}

まずはトランプの実装から始めます。トランプのカードは1,2,3,4,5,6,7,8,9,10,J,Q,Kのみでジョーカーはないものとして扱います。

#include <stdio.h>
#define CARDS 52

int main() {
  /*trump card*/
  int trump_card[CARDS];
  for(int i = 0; i < CARDS; i++) trump_card[i] = 0; //0は未使用を意味する
  return 0;
}

トランプのカード数については今後色々使いそうなので定数として管理することにしました。同じカードが出現すると違和感があるので、カードが場に出ているかを管理する配列を作っておきました。

次にプレイヤー部分を作っていきます。まずは2枚をランダムに取り出したいのでsrand関数とrand関数を合わせて実装していきましょう。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define CARDS 52

int main() {
  /*trump card*/
  int trump_card[CARDS];
  for(int i = 0; i < CARDS; i++) trump_card[i] = 0; //0は未使用を意味する

  /*乱数生成*/
  srand(time(NULL));

  /*player*/
  int number_of_player_cards = 2;
  int player_card[20];
  for(int i = 0; i < 20; i++) player_card[i] = -1; //手札を保存する
  for(int i = 0; i < 2; i++) {
    int temp = rand() % CARDS;
    while(trump_card[temp]) temp = (temp + 1) % CARDS; //すでに使われたトランプを排除
    trump_card[temp] = 1;
    player_card[i] = temp;
  }

  /*output*/
  for(int i = 0; i < number_of_player_cards; i++) {
    char mark;
    if((player_card[i] / 13) == 0) mark = 'S'; //スペード
    else if((player_card[i] / 13) == 1) mark = 'C'; //クラブ
    else if((player_card[i] / 13) == 2) mark = 'H'; //ハート
    else mark = 'D'; //ダイヤ
    printf("【%c %2d】\n", mark, player_card[i] % 13 + 1);
  }
  return 0;
}

出力部はプレイヤーの枚数が可変的であることから、枚数をカウントしてfor文を用いて出力するように実装してみました。ランダムにとってきたトランプがすでに使われている場合はwhile文で+1ずつズラす処理も加えました。

プレイヤーがもう一枚引くかどうかの判定を作っていきます。判定の際には現在の手札を見て判断しなければならないので表示部分も一緒に作っていきます。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define CARDS 52

char get_mark(int);
void get_card(int, int *, int *);

int main() {
  /*trump card*/
  int trump_card[CARDS];
  for(int i = 0; i < CARDS; i++) trump_card[i] = 0; //0は未使用を意味する

  /*乱数生成*/
  srand(time(NULL));

  /*player*/
  int number_of_player_cards = 2;
  int player_card[20];
  for(int i = 0; i < 20; i++) player_card[i] = -1; //手札を保存する
  for(int i = 0; i < 2; i++) get_card(i, player_card, trump_card);

  /*player add cards*/
  int flag;
  do {
    for(int i = 0; i < number_of_player_cards; i++) {
      printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
    }
    printf("もう一枚引きますか?[0:いいえ、1:はい]");
    scanf("%d", &flag);
    if(flag) {
      get_card(number_of_player_cards, player_card, trump_card);
      number_of_player_cards++;
    }
  } while(flag);

  /*output*/
  for(int i = 0; i < number_of_player_cards; i++) {
    printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
  }
  return 0;
}

char get_mark(int tmp_trump) {
  char mark;
  if(tmp_trump / 13 == 0) mark = 'S'; //スペード
  else if(tmp_trump / 13 == 1) mark = 'C'; //クラブ
  else if(tmp_trump / 13 == 2) mark = 'H'; //ハート
  else mark = 'D'; //ダイヤ
  return mark;
}

void get_card(int ind, int *player, int *trump) {
  int temp = rand() % CARDS;
  while(trump[temp]) temp = (temp + 1) % CARDS; //すでに使われたトランプを排除
  trump[temp] = 1;
  player[ind] = temp;
  return;
}

実装で繰り返し出てくる部分については関数化して切り離してみました。

ここまでを実行してみると直前の表示が邪魔で間違えて引いちゃいそうな感じがしたのでsystem(“cls”);で画面をクリアしてみます。Macの人はclsではなくてclearを使用していただければうまく動くと思います。

ついでに21以下かを判定する関数も実装しておきます。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define CARDS 52

char get_mark(int);
void get_card(int, int *, int *);
int sum_point(int, int *);

int main() {
  /*trump card*/
  int trump_card[CARDS];
  for(int i = 0; i < CARDS; i++) trump_card[i] = 0; //0は未使用を意味する

  /*乱数生成*/
  srand(time(NULL));

  /*player*/
  int number_of_player_cards = 2;
  int player_card[20];
  for(int i = 0; i < 20; i++) player_card[i] = -1; //手札を保存する
  for(int i = 0; i < 2; i++) get_card(i, player_card, trump_card);

  /*player add cards*/
  int flag;
  do {
    system("cls");
    for(int i = 0; i < number_of_player_cards; i++) {
      printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
    }
    printf("もう一枚引きますか?[0:いいえ、1:はい]");
    scanf("%d", &flag);
    if(flag) {
      get_card(number_of_player_cards, player_card, trump_card);
      number_of_player_cards++;
    }
  } while(flag && sum_point(number_of_player_cards, player_card));

  /*output*/
  for(int i = 0; i < number_of_player_cards; i++) {
    printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
  }
  return 0;
}

char get_mark(int tmp_trump) {
  char mark;
  if(tmp_trump / 13 == 0) mark = 'S'; //スペード
  else if(tmp_trump / 13 == 1) mark = 'C'; //クラブ
  else if(tmp_trump / 13 == 2) mark = 'H'; //ハート
  else mark = 'D'; //ダイヤ
  return mark;
}

void get_card(int ind, int *player, int *trump) {
  int temp = rand() % CARDS;
  while(trump[temp]) temp = (temp + 1) % CARDS; //すでに使われたトランプを排除
  trump[temp] = 1;
  player[ind] = temp;
  return;
}

int sum_point(int ind, int *tmp) {
  int sum = 0;
  int cnt = 0; //『A』の数をカウント
  for(int i = 0; i < ind; i++) {
    int point = tmp[i] % 13 + 1; //トランプの数字
    if(point == 1) {
      cnt++;
      point = 11;
    } else if(point > 10) {
      point = 10;
    }
    sum += point;
  }
  while(sum > 21 && cnt) {
    cnt--;
    sum -= 10;
  }
  if(sum <= 21) return sum;
  else return 0;
}

判定用の関数をdo-while文に追加しました。

最後にコンピュータを実装して完成です。コンピュータは簡単のために手持ちの合計が16以下なら引く、17以上ならこれ以上引かないことにします。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define CARDS 52

char get_mark(int);
void get_card(int, int *, int *);
int sum_point(int, int *);

int main() {
  /*trump card*/
  int trump_card[CARDS];
  for(int i = 0; i < CARDS; i++) trump_card[i] = 0; //0は未使用を意味する

  /*乱数生成*/
  srand(time(NULL));

  /*player*/
  int number_of_player_cards = 2;
  int player_card[20];
  for(int i = 0; i < 2; i++) get_card(i, player_card, trump_card);

  /*player add cards*/
  int flag;
  do {
    system("cls");
    for(int i = 0; i < number_of_player_cards; i++) {
      printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
    }
    printf("もう一枚引きますか?[0:いいえ、1:はい]");
    scanf("%d", &flag);
    if(flag) {
      get_card(number_of_player_cards, player_card, trump_card);
      number_of_player_cards++;
    }
  } while(flag && sum_point(number_of_player_cards, player_card));

  /*computer*/
  int number_of_computer_cards = 2;
  int computer_card[20];
  for(int i = 0; i < 2; i++) get_card(i, computer_card, trump_card);

  /*computer add cards*/
  do {
    get_card(number_of_computer_cards, computer_card, trump_card);
    number_of_computer_cards++;
  } while(sum_point(number_of_computer_cards, computer_card) && sum_point(number_of_computer_cards, computer_card) <= 16);

  /*output*/
  printf("あなたのカードは:\n");
  for(int i = 0; i < number_of_player_cards; i++) {
    printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
  }
  printf("%d点\n", sum_point(number_of_player_cards, player_card));

  printf("あいてのカードは:\n");
  for(int i = 0; i < number_of_computer_cards; i++) {
    printf("【%c %2d】\n", get_mark(computer_card[i]), computer_card[i] % 13 + 1);
  }
  printf("%d点\n", sum_point(number_of_computer_cards, computer_card));

  if(sum_point(number_of_player_cards, player_card) == sum_point(number_of_computer_cards, computer_card)) {
    printf("引き分けです!!\n");
  } else if(sum_point(number_of_player_cards, player_card) > sum_point(number_of_computer_cards, computer_card)) {
    printf("あなたの勝ちです!!\n");
  } else {
    printf("あなたの負けです!!\n");
  }
  return 0;
}

char get_mark(int tmp_trump) {
  char mark;
  if(tmp_trump / 13 == 0) mark = 'S'; //スペード
  else if(tmp_trump / 13 == 1) mark = 'C'; //クラブ
  else if(tmp_trump / 13 == 2) mark = 'H'; //ハート
  else mark = 'D'; //ダイヤ
  return mark;
}

void get_card(int ind, int *player, int *trump) {
  int temp = rand() % CARDS;
  while(trump[temp]) temp = (temp + 1) % CARDS; //すでに使われたトランプを排除
  trump[temp] = 1;
  player[ind] = temp;
  return;
}

int sum_point(int ind, int *tmp) {
  int sum = 0;
  int cnt = 0; //『A』の数をカウント
  for(int i = 0; i < ind; i++) {
    int point = tmp[i] % 13 + 1; //トランプの数字
    if(point == 1) {
      cnt++;
      point = 11;
    } else if(point > 10) {
      point = 10;
    }
    sum += point;
  }
  while(sum > 21 && cnt) {
    cnt--;
    sum -= 10;
  }
  if(sum <= 21) return sum;
  else return 0;
}

実装しました。これでプレイヤーとコンピュータの対戦ができるようになりました。実装した本人はコンピュータの動きがわかってしまうので少しつまらないですが、乱数と組み合わせて判定したり残りのカード(山札)から判断するように実装すれば改良できそうですね。

完成コード

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define CARDS 52

char get_mark(int);
void get_card(int, int *, int *);
int sum_point(int, int *);

int main() {
  /*trump card*/
  int trump_card[CARDS];
  for(int i = 0; i < CARDS; i++) trump_card[i] = 0; //0は未使用を意味する

  /*乱数生成*/
  srand(time(NULL));

  /*player*/
  int number_of_player_cards = 2;
  int player_card[20];
  for(int i = 0; i < 2; i++) get_card(i, player_card, trump_card);

  /*player add cards*/
  int flag;
  do {
    system("cls");
    for(int i = 0; i < number_of_player_cards; i++) {
      printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
    }
    printf("もう一枚引きますか?[0:いいえ、1:はい]");
    scanf("%d", &flag);
    if(flag) {
      get_card(number_of_player_cards, player_card, trump_card);
      number_of_player_cards++;
    }
  } while(flag && sum_point(number_of_player_cards, player_card));

  /*computer*/
  int number_of_computer_cards = 2;
  int computer_card[20];
  for(int i = 0; i < 2; i++) get_card(i, computer_card, trump_card);

  /*computer add cards*/
  do {
    get_card(number_of_computer_cards, computer_card, trump_card);
    number_of_computer_cards++;
  } while(sum_point(number_of_computer_cards, computer_card) && sum_point(number_of_computer_cards, computer_card) <= 16);

  /*output*/
  printf("あなたのカードは:\n");
  for(int i = 0; i < number_of_player_cards; i++) {
    printf("【%c %2d】\n", get_mark(player_card[i]), player_card[i] % 13 + 1);
  }
  printf("%d点\n", sum_point(number_of_player_cards, player_card));

  printf("あいてのカードは:\n");
  for(int i = 0; i < number_of_computer_cards; i++) {
    printf("【%c %2d】\n", get_mark(computer_card[i]), computer_card[i] % 13 + 1);
  }
  printf("%d点\n", sum_point(number_of_computer_cards, computer_card));

  if(sum_point(number_of_player_cards, player_card) == sum_point(number_of_computer_cards, computer_card)) {
    printf("引き分けです!!\n");
  } else if(sum_point(number_of_player_cards, player_card) > sum_point(number_of_computer_cards, computer_card)) {
    printf("あなたの勝ちです!!\n");
  } else {
    printf("あなたの負けです!!\n");
  }
  return 0;
}

char get_mark(int tmp_trump) {
  char mark;
  if(tmp_trump / 13 == 0) mark = 'S'; //スペード
  else if(tmp_trump / 13 == 1) mark = 'C'; //クラブ
  else if(tmp_trump / 13 == 2) mark = 'H'; //ハート
  else mark = 'D'; //ダイヤ
  return mark;
}

void get_card(int ind, int *player, int *trump) {
  int temp = rand() % CARDS;
  while(trump[temp]) temp = (temp + 1) % CARDS; //すでに使われたトランプを排除
  trump[temp] = 1;
  player[ind] = temp;
  return;
}

int sum_point(int ind, int *tmp) {
  int sum = 0;
  int cnt = 0; //『A』の数をカウント
  for(int i = 0; i < ind; i++) {
    int point = tmp[i] % 13 + 1; //トランプの数字
    if(point == 1) {
      cnt++;
      point = 11;
    } else if(point > 10) {
      point = 10;
    }
    sum += point;
  }
  while(sum > 21 && cnt) {
    cnt--;
    sum -= 10;
  }
  if(sum <= 21) return sum;
  else return 0;
}

C言語講座


最後まで記事を見ていただきありがとうございます。また別の記事でお会いできることを祈っております。

Print Friendly, PDF & Email

C言語カテゴリの最新記事