/* 一人で遊ぶポーカーのプログラム ジョーカーは使わない。最初にコインを何枚か賭ける。五枚のカードが手札と して配られ、そのうちの零枚から五枚を選んで、一度だけ交換すると、交換後 の手札の役に応じた枚数のコインがもらえる。これのくり返し。 トランプカードは、0 から 51 の数で表す。0 から 12 がスペードの A から K を、13 から 25 がハートの A から K を、26 から 38 がクラブの A から K を、39 から 51 がダイヤモンドの A から K を意味する。(こう決めると、数 を 13 で割った商がスートを、余りがランクを表すことになる。これは、昔 ながらの決まったやり方の一つらしい。) ここまでで習ったことだけを使って書いてある。C言語の全貌を学べばもっと うまく書ける部分を含んでいる。 (画面制御エスケープシーケンスは使っていない。) */ #include #include /* rand() */ #include /* time() */ #define YES 1 #define NO 0 #define EMPTY (-1) /* カッコでくくるのは、もしも a = 1 + EMPTY * b などの式 */ /* を書いてもエラーにならないため。念のため。 */ #define COINS 100 /* 最初に持っているコインの枚数。変えてよい */ int card[52]; /* ジョーカーは使わないので 52 枚 */ int hand[6]; /* 手札。hand[0] は使わない */ int a[6]; /* hand[i] を新たにひくなら a[i] には YES を、そうでなければ */ /* NO を入れる。a[0] は使わない */ void init(void); void draw(void); void printhands(void); int analyse(void); main() { int i, x, y; int coins, bet, get; for (coins = COINS; coins > 0; ) { printf("--------------------\n"); /* 新しいゲームの始まりを示す線 */ for (bet = -1; bet < 0; ) { printf("%d 枚のコインを持っています.\n", coins); printf("何枚賭けますか? (0 を入力すれば終了)>"); scanf("%d", &bet); if (bet < 0) { printf("負の枚数は賭けられません.\n"); } if (bet > coins) { printf("そんなに持っていませんよ.\n"); bet = -1; /* これでやり直すことになる */ } } if (bet == 0) { coins = 0; /* これでプログラムの終了へ */ } else { init(); /* 乱数とカードの初期化 */ for (i = 1; i <= 5; i++) { /* 最初は五枚ひくので全部 YES */ a[i] = YES; } draw(); /* カードをひく */ printhands(); /* 手札を画面表示 */ printf("どれを交換しますか? --- 例:1, 2, 5 枚目"); printf("なら 125 と入力。一枚も変えないなら 0 と入力\n"); scanf("%d", &x); /* ユーザが x に入力した値を分析し、i 枚目を交換するなら a[i] に */ /* YES が、交換しないなら NO がはいるようにする。次とその次の */ /* ループでそうなるんだけど、どうしてかは自分で考えて。 */ for (i = 1; i <= 5; i++) { a[i] = NO; } for ( ; x != 0; x = x / 10) { y = x % 10; if (y >= 1 && y <= 5) { a[y] = YES; } } draw(); /* カードを交換する(実際はひく)*/ printhands(); /* 手札を画面表示 */ get = analyse(); /* 手札を分析。倍率が返ってくる */ if (get > 0) { printf("%d 枚のコインが当たりました.\n", get * bet); } else { printf("残念でした.\n"); } coins = coins + (get - 1) * bet; if (coins == 0) { printf("あなたは破産しました....\n"); } } } } /* 乱数と card[ ](カード)を初期化する */ void init(void) { int i; { /* この中カッコの中(乱数の種の設定)はわからなくてもよい */ unsigned seed = (unsigned)time(NULL); /* 現在時刻を取得して */ printf("乱数の種は %u です.\n", seed); srand(seed); /* それを乱数の種に */ } for (i = 0; i < 52; i++) { /* YES はまだそのカードが引かれていない */ card[i] = YES; /* ことを示す印 */ } } /* カードをひく。a[i](i は 1 から 5)が YES であるような i に対し、hand[i] */ /* を選ぶ。カードはスペードの A からダイヤモンドの K まで順番に並んでおり、 */ /* 乱数を使ってその中から一枚を選ぶ。すでにひかれたカードを再度選ぶことを */ /* 防ぐため、ひいたカードに対する card[n] は YES から NO に変える。NO の */ /* カードを選んでしまったら、再度ひく。このゲームでは最大で 10 枚のカード */ /* しか使わないので、このやり方でまず大丈夫であろう。 */ void draw(void) { int i, n; for (i = 1; i <= 5; i++) { if (a[i] == YES) { for (hand[i] = EMPTY; hand[i] == EMPTY; ) { n = rand() % 52; /* これが選ばれたカード。 */ if (card[n] == YES) { /* 「残っている」だったら */ hand[i] = n; /* そのカードを hand[i] に代入、*/ card[n] = NO; /* 「残っていない」に変える */ } } } } } /* 手札の出力。非常にシンプルにしてある。ここだけを変えることも可能。hand[i] */ /* の値は i 番目のカードを意味する。値とカードの関係は冒頭のコメントを参照。 */ void printhands(void) { int i, y; for (i = 1; i <= 5; i++) { /* カードの数(ランク)を表示 */ y = hand[i] % 13; /* これがカードの数(ランク)を示す */ if (y == 0) { /* 数(ランク)の表示 */ printf(" A "); } else if (y == 10) { printf(" J "); } else if (y == 11) { printf(" Q "); } else if (y == 12) { printf(" K "); } else { printf("%2d ", y+1); } } printf("\n"); for (i = 1; i <= 5; i++) { /* カードのマーク(スート)を表示 */ y = hand[i] / 13; /* これがカードのマーク(スート)を示す */ if (y == 0) { printf(" ! "); /* スペード(のつもり)*/ } else if (y == 1) { printf(" & "); /* ハート(のつもり)*/ } else if (y == 2) { printf(" %% "); /* クラブ(のつもり)*/ } else { /*(「%」を出力させるときは「%%」と書く)*/ printf(" @ "); /* ダイヤモンド(のつもり) */ } } printf("\n"); } /* 手札の役の解析を行ない、結果(どんな役だったか)の出力も行なう */ int analyse(void) { int i, j, x, y; int samerank, sequence, sequence2, flush, get; /* 数(ランク)が一致する組(ペア)の数を数える。この数でワンペア、 */ /* ツーペア、スリーカード、フルハウスなどが判定できるのは有名な事実 */ samerank = 0; for (i = 1; i <= 5; i++) { for (j = i+1; j <= 5; j++) { if (hand[i] % 13 == hand[j] % 13) { samerank++; } } } /* ランクが一致するペアがない場合に限り、隣り合ったランクのペアの数を */ /* 数える。これはストレートかどうかを判定するためなので、ランクが一致 */ /* するペアがある場合は 0 とする。 */ sequence = 0; sequence2 = 0; if (samerank == 0) { for (i = 1; i <= 5; i++) { for (j = i+1; j <= 5; j++) { x = hand[i] % 13 - hand[j] % 13; if (x == 1 || x == -1) { sequence++; } } } /* ここで sequence が 4 ならストレート(10-J-Q-K-A 以外)*/ for (i = 1; i <= 5; i++) { for (j = i+1; j <= 5; j++) { if (hand[i] % 13 == 0) { /* A は 13 と置き換えてから */ x = 13; } else { x = hand[i] % 13; } if (hand[j] % 13 == 0) { y = 13; } else { y = hand[j] % 13; } if (x == y + 1 || x + 1 == y) { /* 上と同じことを行なう */ sequence2++; /* (細部は違うけど) */ } } } /* ここで sequence2 が 4 ならストレート(A-2-3-4-5 以外) */ } /* 次に、マーク(スート)がすべて同じかどうかの判定を行なう */ /* (素直に && で四つの条件をつなげたほうがよかったかも) */ flush = YES; for (i = 1; i <= 4; i++) { if (hand[i] / 13 != hand[i+1] / 13) { flush = NO; } } /* 以上で、解析の材料はそろった。あとは判定するのみ。get は倍率。*/ if (samerank == 1) { printf("ワンペアです.\n"); get = 1; } else if (samerank == 2) { printf("ツーペアです.\n"); get = 2; } else if (samerank == 3) { printf("スリーカードです.\n"); get = 3; } else if ((sequence == 4 || sequence2 == 4) && flush == NO) { printf("ストレートです!\n"); get = 4; } else if (sequence != 4 && sequence2 != 4 && flush == YES) { printf("フラッシュです!\n"); get = 5; } else if (samerank == 4) { printf("フルハウスです!\n"); get = 10; } else if (samerank == 6) { printf("フォーカードです!!\n"); get = 20; } else if (sequence == 4 && flush == YES) { printf("ストレートフラッシュです!!\n"); get = 50; } else if (sequence != 4 && sequence2 == 4 && flush == YES) { printf("ロイヤルストレートフラッシュです!!!\n"); get = 500; } else { get = 0; } return get; }