第78章 357ゲーム その3


今回は、前章までで作ったプログラムを少し視覚化して わかりやすくします。



左の図のように、石を「●」で表してみました。 「どの山からとりますか」などの質問文も常に同じ場所に 表示するようにしました。

プログラムの仕組みは簡単です。

コンソール・アプリの場合は第59章の要領でカーソル位置を指定して やればよいのです。その他の場合は、カーソル位置指定をそれぞれの 環境にあった方法で行ってください。



では、プログラムをみてみましょう。

// stone35703.c

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <conio.h>

int comptake(int *);
int judge(int *);
int takestone(int *);
int showno(int *);
int no_of_0(int *);
int locate(int, int);

// sente:人が先手 1,後手 0,    order:現在先手の番 1, 後手の番 0
int sente, order;
//Standard Output Handle
HANDLE hOut;
GetStdHandle関数等を使うのでwindows.hのインクルードが必要です。

_getch関数を使うのでconio.hのインクルードが必要です。

今回新たにlocate関数を作りました。

int main()
{
    // mt[x] 石山の数
    int mt[3], ret;
    char ans[8];

    //hOutの取得
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut ==  INVALID_HANDLE_VALUE)
        return -1;

    mt[0] = 3;
    mt[1] = 5;
    mt[2] = 7;

    showno(mt);

    locate(0, 4);
    printf("あなたが先手になりますか(Y/N)---");
    gets(ans);
    if (strcmp(ans, "y") == 0 || strcmp(ans, "Y") == 0) {
        sente = 1;
    } else {
        sente = 0;
    }

    order = 1;

    while (1) {
        ret = takestone(mt);
        if (ret == 1)
            break;
    }
    
    return 0;
}
プログラムが始まったらすぐにGetStdHandle関数で標準出力ハンドルを 取得してグローバル変数に格納します。

「あなたが先手になりますか」という質問を早速locate関数で 位置を指定して出力しています。後は、今までと同じです。

int comptake(int *mtno)
{
    int mtorder, stone, jump = 0, i;


    srand((unsigned)time(NULL));
    
    if (no_of_0(mtno) == 0) { //どの山も0ではない
        //2-4-6パターンにする
        if (mtno[0] == 3 && mtno[1] == 4 && mtno[2] == 6) {
            mtorder = 0;
            stone = 1;
            mtno[0] = 2;
            goto end;
        }
        if (mtno[1] == 5 && mtno[0] == 2 && mtno[2] == 6) {
            mtorder = 1;
            stone = 1;
            mtno[1] = 4;
            goto end;
        }
        if (mtno[2] == 7 && mtno[0] == 2 && mtno[1] == 4) {
            mtorder = 2;
            stone = 1;
            mtno[2] = 6;
            goto end;
        }
        //1-4-5パターンにする
        if (mtno[0] > 1 && mtno[1] + mtno[2] == 9 && mtno[1] * mtno[2] == 20) {
            mtorder = 0;
            stone = mtno[0] - 1;
            mtno[0] = 1;
            goto end;
        }
        if (mtno[1] > 4 && mtno[0] == 1 && mtno[2] == 5) {
            mtorder = 1;
            stone = mtno[1] - 4;
            mtno[1] = 4;
            goto end;
        }
        if (mtno[2] > 4 && mtno[0] == 1 && mtno[1] == 5) {
            mtorder = 2;
            stone = mtno[2] - 4;
            mtno[2] = 4;
            goto end;
        }
        if (mtno[2] > 5 && mtno[0] == 1 && mtno[1] == 4) {
            mtorder = 2;
            stone = mtno[2] - 4;
            mtno[2] = 4;
            goto end;
        }
        //1-2-3パターンにする
        if (mtno[0] > 1 && mtno[1] + mtno[2] == 5 && mtno[1] * mtno[2] == 6) {
            mtorder = 0;
            stone = mtno[0] - 1;
            mtno[0] = 1;
            goto end;
        }
        if (mtno[0] > 2 && mtno[1] + mtno[2] == 4 && mtno[1] * mtno[2] == 3) {
            mtorder = 0;
            stone = mtno[0] - 2;
            mtno[0] = 2;
            goto end;
        }
        if (mtno[0] == 3 && mtno[1] + mtno[2] == 3 && mtno[1] * mtno[2] == 2) {
            mtorder = 0;
            stone = mtno[0] - 3;
            mtno[0] = 3;
            goto end;
        }
        if (mtno[1] > 1 && mtno[0] + mtno[2] == 5 && mtno[0] * mtno[2] == 6) {
            mtorder = 1;
            stone = mtno[1] - 1;
            mtno[1] = 1;
            goto end;
        }
        if (mtno[1] > 2 && mtno[0] + mtno[2] == 4 && mtno[0] * mtno[2] == 3) {
            mtorder = 1;
            stone = mtno[1] - 2;
            mtno[1] = 2;
            goto end;
        }
        if (mtno[1] > 3 && mtno[0] + mtno[2] == 3 && mtno[0] * mtno[2] == 2) {
            mtorder = 1;
            stone = mtno[1] - 3;
            mtno[1] = 3;
            goto end;
        }
        if (mtno[2] > 1 && mtno[0] + mtno[1] == 5 && mtno[0] * mtno[1] == 6) {
            mtorder = 2;
            stone = mtno[2] - 1;
            mtno[2] = 1;
            goto end;
        }
        if (mtno[2] > 2 && mtno[0] + mtno[1] == 4 && mtno[0] * mtno[1] == 3) {
            mtorder = 2;
            stone = mtno[2] - 2;
            mtno[2] = 2;
            goto end;
        }
        if (mtno[2] > 3 && mtno[0] + mtno[1] == 3 && mtno[0] * mtno[1] == 2) {
            mtorder = 2;
            stone = mtno[2] - 3;
            mtno[2] = 3;
            goto end;
        }
        //1-1-1パターンにする
        if (mtno[0] == mtno[1] && mtno[0] == 1 && mtno[2] > 1) {
            mtorder = 2;
            stone = mtno[2] - 1;
            mtno[2] = 1;
            goto end;
        }
        if (mtno[1] == mtno[2] && mtno[1] == 1 && mtno[0] > 1) {
            mtorder = 0;
            stone = mtno[0] - 1;
            mtno[0] = 1;
            goto end;
        }
        if (mtno[0] == mtno[2] && mtno[0] == 1 && mtno[1] > 1) {
            mtorder = 1;
            stone = mtno[1] - 1;
            mtno[1] = 1;
            goto end;
        }
        if (mtno[0] == mtno[1]) {
            mtorder = 2;
            stone = mtno[2];
            mtno[mtorder] = 0;
            goto end;;
        }
        if (mtno[1] == mtno[2]) {
            mtorder = 0;
            stone = mtno[0];
            mtno[mtorder] = 0;
            goto end;;
        }
        if (mtno[2] == mtno[0]) {
            mtorder = 1;
            stone = mtno[1];
            mtno[mtorder] = 0;
            goto end;;
        }
    }

    //0の山が1つだけである
    if (no_of_0(mtno) == 1) {
        if (mtno[0] == 0) {
            if (mtno[1] == 1) {
                mtorder = 2;
                stone = mtno[2];
                mtno[2] = 0;
                goto end;
            }
            if (mtno[2] == 1) {
                mtorder = 1;
                stone = mtno[1];
                mtno[1] = 0;
                goto end;
            }

            if (mtno[1] > mtno[2]) {
                mtorder = 1;
                stone = mtno[1] - mtno[2];
                mtno[mtorder] -= stone;
                goto end;
            }
            if (mtno[2] > mtno[1]) {
                mtorder = 2;
                stone = mtno[2] - mtno[1];
                mtno[mtorder] -= stone;
                goto end;
            } 
            if (mtno[2] == mtno[1]){
                mtorder = 1;
                stone = 1;
                mtno[mtorder] -= stone;
                goto end;
            }
        }
        if (mtno[1] == 0) {
            if (mtno[0] == 1) {
                mtorder = 2;
                stone = mtno[2];
                mtno[2] = 0;
                goto end;
            }
            if (mtno[2] == 1) {
                mtorder = 0;
                stone = mtno[0];
                mtno[0] = 0;
                goto end;
            }
            if (mtno[0] > mtno[2]) {
                mtorder = 0;
                stone = mtno[0] - mtno[2];
                mtno[mtorder] -= stone;
                goto end;
            }
            if (mtno[2] > mtno[0]) {
                mtorder = 2;
                stone = mtno[2] - mtno[0];
                mtno[mtorder] -= stone;
                goto end;
            }
            if (mtno[0] == mtno[2]) {
                mtorder = 2;
                stone = 1;
                mtno[mtorder] -= stone;
                goto end;
            }
        }
        if (mtno[2] == 0) {
            if (mtno[1] == 1) {
                mtorder = 0;
                stone = mtno[0];
                mtno[0] = 0;
                goto end;
            }
            if (mtno[0] == 1) {
                mtorder = 1;
                stone = mtno[1];
                mtno[1] = 0;
                goto end;
            }
            if (mtno[0] > mtno[1]) {
                mtorder = 0;
                stone = mtno[0] - mtno[1];
                mtno[mtorder] -= stone;
                goto end;
            }
            if (mtno[1] > mtno[0]) {
                mtorder = 1;
                stone = mtno[1] - mtno[0];
                mtno[mtorder] -= stone;
                goto end;
            }
            if (mtno[1] == mtno[0]) {
                mtorder = 0;
                stone = 1;
                mtno[mtorder] -= 1;
                goto end;
            }
        }
    }

    if (no_of_0(mtno) == 2) {
        for (i = 0; i < 3; i++) {
            if (mtno[i] != 0) {
                stone = mtno[i] - 1;
                mtorder = i;
                mtno[i] = 1;
                goto end;
            }
        }
    }
    while (1) {
        mtorder = rand() % 3;
        if (mtno[mtorder] == 0)
            continue;
        else
            break;
    }
    while (1) {
        stone = 1;
        mtno[mtorder] -= stone;
        if (mtno[0] + mtno[1] + mtno[2] == 0) {
            mtno[mtorder] += stone;
            continue;
        } else {
            break;
        }
    }
end:
    for (i = 4; i < 7; i++) {
        locate(0, i);
        printf("                                                 ");
    }
    for (i = 4; i < 8; i++) { 
            locate(0, i);
            printf("                                            ");
    }
    locate(0, 6);
    printf("コンピュータは%cから%d個取りました", mtorder + 'A', stone);
    showno(mtno);
    _getch();
    return 0;
}
最後の方だけ、少し変更しました。

文字表示の時、位置指定しています。また、コンピュータが 石をとった後、_getch関数を入れてユーザーが何かキーを押さないと 次に行かないようにしてみました。

int takestone(int *mt)
{
    char ans[8];
    int i, stone, mtorder;

    if (sente != order) {
        comptake(mt);
        if (judge(mt) == 0) {
            locate(0, 7);
            printf("                                                 ");
            locate(0, 7);
            printf("コンピュータの勝ちです\n");
            return 1;
        }
        if (order == 1)
            order = 0;
        else
            order = 1;
        return 0;
    }
    while (1) {
        for (i = 4; i < 8; i++) { 
            locate(0, i);
            printf("                                            ");
        }
        locate(0, 4);
        printf("どの山からとりますか(A,B,C)---");
        gets(ans);
        locate(0, 7);
        printf("                                   ");
        if (strcmp(ans, "A") != 0 && strcmp(ans, "B") != 0 && strcmp(ans, "C") != 0 &&
            strcmp(ans, "a") != 0 && strcmp(ans, "b") != 0 && strcmp(ans, "c") != 0) {
            locate(0, 7);
            printf("山の指定が正しくありません");
            _getch();
            continue;
        } else {
            ans[0] = toupper(ans[0]);
            mtorder = ans[0] - 'A';
            if (mt[mtorder] == 0) {
                locate(0, 7);
                printf("その山にはもう石はありません");
                _getch();
                continue;
            }
            break;
        }
    }
    
    while (1) {
        locate(0, 5);
        printf("                          ");
        locate(0, 5);
        printf("いくつとりますか---");
        gets(ans);
        stone = atoi(ans);
        mt[mtorder] -= stone;
        if (mt[0] + mt[1] + mt[2] == 0) {
            locate(0, 7);
            printf("その取り方では山の石が全部0になります");
            mt[mtorder] += stone;
            _getch();
            continue;
        }
        mt[mtorder] += stone;
        if (mt[mtorder] - stone < 0 || stone <= 0) {
            locate(0, 7);
            printf("取る石の数が不正です");
            _getch();
            continue;
        } else {
            break;
        }
    }
    mt[mtorder] -= stone;
    showno(mt);
    if (judge(mt) == 0) {
        locate(0, 7);
        printf("                                                 ");
        locate(0, 7);
        printf("あなたの勝ちです\n");
        return 1;
    }
        
    if (order == 1)
        order = 0;
    else
        order = 1;

    return 0;
}
文字列表示をすべて位置指定しています。また、何かを 出力するときに、前の表示が残っていてはみっともないので スペースを大量に書き込んで以前の表示を消してから 新しい表示を書くようにしました。これも、プログラム的には みっともないので、もう少しスマートなものに書き換えてください。 コンソールアプリの時は、画面表示をすべて消すときに使ったアレを 使ってください。
int judge(int *mt)
{
    if (mt[0] + mt[1] + mt[2] == 1)
        return 0;
    else
        return 1;
}
この関数に変更はありません。
int showno(int *mt)
{
    int i;

    for (i = 0; i < 3; i++) {
        locate(0, i);
        printf("                                 ");
    }
    locate(0, 0);
    printf("[A] ");
    for (i = 0; i < mt[0]; i++)
        printf("●");
    locate(0, 1);
    printf("[B] ");
    for (i = 0; i < mt[1]; i++)
        printf("●");
    locate(0, 2);
    printf("[C] ");
    for (i = 0; i < mt[2]; i++)
        printf("●");

    return 0;
}
石を黒丸で表示する部分です。特に難しくはないですね。forを使うときは うっかりミスの無いようにしてください。(0から始まるのかどうか、 条件にイコールが入っているのかどうかなど)
int no_of_0(int *mt)
{
    int no, i;

    no = 0;
    for (i = 0; i < 3; i++) {
        if (mt[i] == 0)
            no++;
    }
    return no;
}
この関数に変更はありません。
int locate(int x, int y)
{
    COORD dwPos;

    dwPos.X = (SHORT)x;
    dwPos.Y = (SHORT)y;

    if (SetConsoleCursorPosition(hOut, dwPos) == 0)
        return -1;
    else
        return 0;
}
カーソル位置を指定する関数です。SetConsoleCursorPosition関数などの コンソールAPIを使えない環境の人は、その環境にあったカーソル位置指定法を 使ってください。

今回は簡単でしたね。


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

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