ルールは至って簡単です。石がn個ある山から、交互にm個以内で石を取っていきます。
最後の1個の石を取った人が負けです。自分の順番の時は最低でも1個の石を取らなくては
いけません。
今回は「必勝法もどき」を使わず、コンピュータは乱数に従って石を取ります。 ただ、残りの石がm+1個の時は、迷わずm個の石を取ってコンピュータが勝ちます。
では、プログラムを見てみましょう。
// game2_01.c #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> int takestone(int *, int, int, int); int main() { // nStone:石の数 nGet:取れる石の数 nSente:1なら人が先手 // nOrder:1なら先手、-1なら後手の番 int nStone, nGet, nSente, nOrder, nResult; char answer[8]; printf("石を交互に取り、最後の1個を取った人が負けです\n"); while (1) { nOrder = 1; printf("石の数は(5以上100以下)=="); gets(answer); nStone = atoi(answer); if (nStone < 5 || nStone > 100) { printf("石の数が不正です\n"); continue; } while (1) { printf("一度に取れる石の数は(2以上)=="); gets(answer); nGet = atoi(answer); if (nGet >= nStone) { printf("一度に取れる石の数が多すぎます\n"); continue; } if (nGet < 2) { printf("一度に取れる石の数が少なすぎます\n"); continue; } break; } printf("あなたが先手になりますか(Y/N)--"); gets(answer); if (strcmp(answer, "y") == 0 || strcmp(answer, "Y") == 0) { nSente = 1; } else { nSente = 0; } while (1) { nResult = takestone(&nStone, nGet, nSente, nOrder); if (nResult == -1) break; nOrder *= -1; } printf("続けますか(Y/N)--"); gets(answer); if (strcmp(answer, "n") == 0 || strcmp(answer, "N") == 0) break; printf("======================\n\n"); } return 0; }やたらと、while文が多いですね。しかも、全部無限ループになっています。
最初に、簡単なルールの説明を表示します。
次に1番目の無限ループに入ります。
順番は最初は「先手」に決まっているので、nOrderを1にしておきます。
次に「山の石の数」を決めます。5個以上100個以内としました。 もし、条件に合わない入力があると、「石の数が不正です」と表示して、 continueで最初の質問に戻ります。
石の数を正しく入力すると、2番目の無限ループに入ります。 ここでは、一度に取れる石の数を聞いています。条件に合わない入力をすると continueでこの無限ループ内を回り続けることになります。正しい入力をすると、 breakで2番目の無限ループを抜けます。
次に、人間が先手になるかどうかを決めます。
その後、こんどは第3の無限ループに入ります。 ここでは、takestone関数を呼びだしています。この関数の戻り値が-1になると ループを抜けます。また、takestone関数が呼ばれるたびに、nOrderに-1をかけます。 すると、nOrderの値は、1,-1,1,-1,...というようになります。これが、1の時は 先手、-1の時は後手の順番ということです。なお、takestone関数は勝負がついたら -1を返します。また、この関数の最初の引数にnStoneのアドレスを渡している点に注意してください。
勝敗がついたら、このゲームを続けるかどうかを尋ねます。続ける場合はcontinueで 第1の無限ループの最初に戻り、再度ゲームを始めることになります。続けないと 答えると、ループを抜けて、returnでメイン関数を抜けてプログラムが終了します。
int takestone(int *pstone, int nGet, int nSente, int nOrder) { char answer[8]; int n; srand((unsigned)time(NULL)); if ((nOrder == 1 && nSente == 1) || (nOrder == -1 && nSente == 0)) { //人が先手で現在先手の番 while (1) { printf("取る石の数は--"); gets(answer); n = atoi(answer); if (n > nGet) { printf("一度に取れる石の数は%d個までです\n", nGet); continue; } if (n <= 0) { printf("1個以上を指定してください\n"); continue; } if (n >= *pstone) { printf("そのような取り方は出来ません\n"); continue; } *pstone -= n; if (*pstone == 1) { printf("あなたの勝ちです\n"); return -1; } break; } } else { if (*pstone <= nGet + 1) { n = *pstone - 1; } else { n = rand() % nGet + 1; } printf("コンピュータは%d個取りました\n", n); *pstone -= n; if (*pstone == 1) { printf("コンピュータの勝ちです\n"); return -1; } } printf("残りの石は%d個です\n", *pstone); return 0; }各順番(先手、後手)で、石を取り、残りの石を表示する関数です。
最初の引数がポインタである点に注意してください。この関数内で 処理した値はmain関数内のnStoneに反映されます。
まず、人間の順番の時(nOrderが1でnSenteが1の時、または、nOrderが-1でnSenteが0の時) は、取る石の数を尋ねます。条件に合わない入力をすると無限ループ内を回り続けます。
石を取った結果山の石が1個になった場合は人間の勝ちと判定し、-1を返します。
順番がコンピュータである場合は、まず、山の石の数を調べます。これが1回に取れる 石の個数+1以下の時は、山の石の数-1だけ石を取ります。これでコンピュータが勝ちます。
山の石の数がこの条件に当てはまらない時は、1から1回に取れる石の数の範囲で乱数を 発生させます。
rand()をnGetで割ったあまりは0からnGet-1です。これに1を足すと条件に合う乱数を 発生させることができます。
コンピュータが石を取ったあと山の石の数が1となればコンピュータの勝ちです。
勝敗がつかなかった時は、この関数は0を返して終了します。
このゲームでは、コンピュータは取る石の数を運任せにしています。しかし、実際に やってみると、人間が負けることも多々あります。遊んでみてください。
Update Jan/19/2002 By Y.Kumei