石取りゲームとは、一つの山から交互に石をとり、最後の1個を 取った人が負けとなるゲームです。自分の番の時は必ず、1個以上の 石を取らなくてはいけません。また、一度に取れる石の個数は、最初に 約束事として決めておきます。
ある条件を満たすと、必ず勝つことが昔からわかっています。 今回は、この必勝法を使わずに、コンピュータは乱数で取る石の数を決定します。
では、プログラムを見てみましょう。
// ishi01.cpp #include <iostream.h> #include <string.h> #include <stdlib.h> #include <time.h> class Mountain { int m, n; //山の石の数、一度に取れる石の数 int turn; //0:人間の番、1:コンピュータの番 public: Mountain(); int get_stone(); };Mountainクラスを作ってみました。
Mountain::Mountain() { char yesno[8]; cout << "交互に石を取り、最後の1個を取った人が負けです" << endl; cout << "先手になりますか(Y/N): "; cin >> yesno; if (strcmp(yesno, "y") == 0 || strcmp(yesno, "Y") == 0) turn = 0; else turn = 1; while (1) { cout << "石の山の数はいくつにしますか(5以上、100以下)"; cin >> m; if (m < 5 || m > 100) { cout << "石の数が不正です" << endl; continue; } else break; } while (1) { cout << "一度に取れる石の数はいくつにしますか(2以上)"; cin >> n; if (n < 2 || n > m - 1) { cout << "石の数が不正です" << endl; continue; } else break; } }コンストラクタです。
ここで、最初の石山の数やら、一度に取れる石の個数などの初期設定を することにします。
int Mountain::get_stone() { int p; //取る石の数 srand((unsigned)time(NULL)); //乱数の初期値 if (turn == 1) { if (m <= n + 1) p = m - 1; else p = rand() % n + 1; cout << "コンピュータは" << p << "個取りました" << endl; } else { while (1) { cout << "取る石の数 "; cin >> p; if (p > 0 && p < m) break; else cout << "取る石の数が不正です" << endl; } } m = m - p; cout << "石の山は" << m << "個です" << endl; if (m == 1) { if (turn == 1) { cout << "コンピュータの勝ちです" << endl; return 1; } else { cout << "あなたの勝ちです" << endl; return 1; } } if (turn == 1) turn = 0; else turn = 1; return 0; }石を取ったり、勝敗を判定するメンバ関数です。
コンピュータの番の時、石山の数(m)が一度に取れる石の数+1(n+1)以下の時は、 コンピュータはm - 1個取って勝ちます。それ以外の時は乱数で取る個数を決めます。
人間の番の時は、取る個数を尋ねます。個数が0以下だったり、残りの山の石の数と同数または それ以上の時は、注意を促し、再度入力を求めます。
取る個数が決まったらm-pで石山の数を計算します。1となれば勝ちで直ちにreturnします。
勝敗がつかない時はturnを反転して戻ります。
int main() { Mountain mt; while (1) { if (mt.get_stone() == 1) break; } return 0; }main関数です。
get_stoneメンバ関数が1を返す(勝敗がつく)まで、繰り返します。
たったこれだけです。
これは、勝敗がつくとプログラムそのものが終了してしまいます。
何回も繰り返して遊べるように改良してみてください。その場合、ゲームの 初期設定をコンストラクタに任せてはいけません。
Update Feb/12/2002 By Y.Kumei