前章では、コンピュータは取る石の個数を乱数で決めていました。
今回は、必勝法もどきを考えてみます。と、言ってもこれは昔からやり方が決まっています。
石の山の数がn個、一度に取れる最大の石の個数がm個の場合を考えてみます。
もし自分が石を取って石山の数がk(m+1)+1個となったらどうでしょうか。
次に相手がp個取ったら、自分は(m+1-p)個とります。
石山の数は(k-1)(m+1)+1個となります。つまり、1回のターンでm+1個ずつ 石山の数が減るように石を取ることが出来るのです。ついには1個のみが残ることになります。
では、どのように石をとれば石山の数がk(m+1)+1個となるのでしょうか。
n-x=k(m+1)+1
x=-k(m+1)+(n-1)・・・(*)
x=(n-1)%(m+1)
最後の変形はわかったでしょうか。
a%b=c
これは、
c=-kb+a
と同義です。これは、(*)と同じ形になっていますね。
では、プログラムを見てみましょう。前章とほとんど同じでよいはずです。
コンピュータが取る時に乱数ではなく、(n-1)%(m+1)個取ります。 これで、残りの石はk(m+1)+1個となります。あとは、相手の取る石(p)に合わせて (m+1-p)個取っていけばよいです。
しかし、最初に取る石の個数が0個((n-1)%(m+1)=0)となってしまう場合もあります。 この時は、1個だけ取って相手の出方を待ちます。
// game2_02.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int takestone(int *, int, int, int);
int think(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;
}
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 = think(*pstone, nGet);
}
printf("コンピュータは%d個取りました\n", n);
*pstone -= n;
if (*pstone == 1) {
printf("コンピュータの勝ちです\n");
return -1;
}
}
printf("残りの石は%d個です\n", *pstone);
return 0;
}
int think(int nStone, int nGet)
{
int x;
x = (nStone - 1) % (nGet + 1);
if (x == 0)
return 1;
else
return x;
}
これで、遊んでみてください。コンピュータは結構強いことがわかります。また、石の個数を「○」などで表示しておくとゲームの臨場感(?)が高まります。 挑戦してみてください。
Update Feb/06/2002 By Y.Kumei