第75章 石取りゲーム その2


前章では、コンピュータは取る石の個数を乱数で決めていました。 今回は、必勝法もどきを考えてみます。と、言ってもこれは昔からやり方が決まっています。



石の山の数が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;
}
これで、遊んでみてください。コンピュータは結構強いことがわかります。

また、石の個数を「○」などで表示しておくとゲームの臨場感(?)が高まります。 挑戦してみてください。


[Index][総合Index] [Previous Chapter] [Next Chapter]

Update Feb/06/2002 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。