第59章 357の山 その3


今回は、C言語編第78章と同じように 少しだけ文字を使ってグラフィックス様に表示してみます。

また、iostream.hは使わないでプログラムを書いてみます。



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

// game35703.cpp

#include <windows.h>
#include <time.h>
#include <conio.h>

class Mountain
{
    HANDLE hOut, hIn;
    int mt[3];
    int sente, order;
    int judge();
    int show_stone();
    int init();
    int take_stone();
    int comp_take();
    int no_of_0();
    int locate(int, int);
    int print(char *);
    int input(char *, int);
public:
    Mountain();
    int play();
};
windows.hは、コンソールアプリでカーソル位置指定などで必要となります。 今回は、これをメインにプログラムを改造します。

また、画面表示のためにprint関数、入力用にinput関数を作ってみました。

Mountain::Mountain()
{
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE)
        exit(-1);
    hIn = GetStdHandle(STD_INPUT_HANDLE);
    if (hIn == INVALID_HANDLE_VALUE)
        exit(-1);
}
コンストラクタで標準入出力ハンドルを取得しています。
int Mountain::show_stone()
{
    int i;

    for (i = 0; i < 3; i++) {
        locate(0, i);
        print("                                  ");
    }
    locate(0, 0);
    print("[A] ");
    for (i = 0; i < mt[0]; i++)
        print( "●");
    locate(0, 1);
    print( "[B] ");
    for (i = 0; i < mt[1]; i++)
        print( "●");
    locate(0, 2);
    print( "[C] ");
    for (i = 0; i < mt[2]; i++)
        print( "●");
    return 0;
}
現在の各山の石の数を黒丸で表示する関数です。早速print関数を使っています。
int Mountain::judge()
{
    if (mt[0] + mt[1] + mt[2] == 1)
        return 0;
    else
        return 1;
}
この関数に変更はありません。
int Mountain::take_stone()
{
    char ans[8];
    int i, stone, mtorder;

    for (i = 4; i <= 5; i++) {
        locate(0, i);
        print("                                       ");
    }
    
    if (sente != order) {
        comp_take();
        if (judge() == 0) {
            for (i = 4; i < 6; i++) {
                locate(0, i);
                print("                               ");
            }
            locate(0, 7);
            print("コンピュータの勝ちです");
            _getch();
            return 1;
        }
        if (order == 1)
            order = 0;
        else
            order = 1;
        return 0;
    }

    while (1) {
        locate(0, 6);
        print("                                   ");
        locate(0, 4);
        print("どの山からとりますか(A,B,C)---");
        if (input(ans, 8) == -1) {
            locate(0, 6);
            print("内部エラーです");
            _getch();
            exit(-1);
        }
        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) {
            for (i = 4; i <= 5; i++) {
                locate(0, i);
                print("                                       ");
            }
            locate(0, 6);
            print("山の指定が正しくありません");
            _getch();
            continue;
        } else {
            ans[0] = toupper(ans[0]);
            mtorder = ans[0] - 'A';
            if (mt[mtorder] == 0) {
                for (i = 4; i <= 5; i++) {
                    locate(0, i);
                    print("                                       ");
                }
                locate(0, 6);
                print("その山にはもう石はありません");
                _getch();
                continue;
            }
            break;
        }
    }
    
    while (1) {
        locate(0, 6);
        print("                                    ");
        locate(0, 5);
        print( "いくつとりますか---");
        if (input(ans, 8) == -1) {
            locate(0, 6);
            print("内部エラーです");
            _getch();
            exit(-1);
        }
        stone = atoi(ans);
        mt[mtorder] -= stone;
        if (mt[0] + mt[1] + mt[2] == 0) {
            locate(0, 5);
            print("                                       ");
            locate(0, 6);
            print("その取り方では山の石が全部0になります");
            _getch();
            mt[mtorder] += stone;
            continue;
        }
        mt[mtorder] += stone;
        if (mt[mtorder] - stone < 0 || stone <= 0) {
            locate(0, 5);
            print("                                       ");
            locate(0, 6);
            print("取る石の数が不正です");
            _getch();
            continue;
        } else {
            break;
        }
    }
    mt[mtorder] -= stone;
    show_stone();
    if (judge() == 0) {
        for (i = 4; i <= 5; i++) {
            locate(0, i);
            print("                                       ");
        }
        locate(0, 7);
        print("あなたの勝ちです");
        return 1;
    }
        
    if (order == 1)
        order = 0;
    else
        order = 1;

    return 0;
}
内容的には同じですが、画面表示にはprint関数を使っています。 また、コンソールからの入力読みとりにはinput関数を使っています。 input関数の2番目の引数は、コンソールから読みとる大きさを指定します。 ここでは、バイト数と思っても間違いではありません。 注意すべき点はinput(x,5);としてコンソールに「0123456」と入力してしまうと 余った入力は、次のinput関数に読みとられるので、思わぬ結果を生じるので 注意が必要です。
int Mountain::init()
{
    char ans[8];
    int i;

    mt[0] = 3;
    mt[1] = 5;
    mt[2] = 7;
    
    for (i = 0; i <= 8; i++) {
        locate(0, i);
        print("                                        ");
    }
    locate(0, 0);
    print("あなたが先手になりますか(Y/N)---");
    if (input(ans, 8) == -1) {
        locate(0, 6);
            print("内部エラーです");
            _getch();
            exit(-1);
    }

    if (ans[0] == 'y' || ans[0] == 'Y') {
        sente = 1;
    } else {
        sente = 0;
    }
    order = 1;
    return 0;
}
これも、中身は同じですが、print関数やinput関数を使って書き換えています。
int Mountain::comp_take()
{
    int mtorder, stone;
    char mtname, szBuf[64];
    int jump = 0, i;


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

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

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

    if (no_of_0() == 2) {
        for (i = 0; i < 3; i++) {
            if (mt[i] != 0) {
                stone = mt[i] - 1;
                mtorder = i;
                mt[i] = 1;
                goto end;
            }
        }
    }
    while (1) {
        mtorder = rand() % 3;
        if (mt[mtorder] == 0)
            continue;
        else
            break;
    }
    while (1) {
        stone = 1;
        mt[mtorder] -= stone;
        if (mt[0] + mt[1] + mt[2] == 0) {
            mt[mtorder] += stone;
            continue;
        } else {
            break;
        }
    }
end:
    mtname = mtorder + 'A';
    locate(0, 4);
    wsprintf(szBuf, "コンピュータは%cから%d個取ります", mtname, stone);
    print(szBuf);
    _getch();
    show_stone();
    return 0;
}
これも中身は同じですが、print関数で書き換えています。
int Mountain::play()
{
    int ret, i;
    char ans[8];

    while (1) {
        init();
        show_stone();
        while (1) {
            ret = take_stone();
            if (ret == 1)
                break;
        }
        locate(0, 8);
        print("もう一度やりますか(Y/N) ");
        if (input(ans, 8) == -1) {
            locate(0, 6);
            print("内部エラーです");
            _getch();
            exit(-1);
        }
        if (ans[0] == 'y' || ans[0] == 'Y') {
            continue;
        } else {
            for (i = 0; i <= 8; i++) {
                locate(0, i);
                print("                               ");
            }
            locate(0, 0);
            break;
        }
    }
    return 0;
}
これまたinput,print関数で置き換えています。
int Mountain::no_of_0()
{
    int no, i;

    no = 0;
    for (i = 0; i < 3; i++) {
        if (mt[i] == 0)
            no++;
    }
    return no;
}
この関数に変更はありません。
int Mountain::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関数についてはC言語編 第59章を参照してください。
int Mountain::print(char *szBuf)
{
    DWORD dwToWrite, dwWritten;

    dwToWrite = (DWORD)strlen(szBuf);
    WriteConsole(hOut, szBuf, dwToWrite, &dwWritten, NULL);
    if (dwToWrite != dwWritten)
        return -1;
    else
        return 0;
}
hOutに出力する関数です。

WriteConsole関数についてはC言語編 第59章を参照してください。

int Mountain::input(char *szBuf, int n)
{
    DWORD dwResult;

    if (ReadConsole(hIn, (LPVOID)szBuf, (DWORD)n, &dwResult, NULL) == 0) {
        locate(0, 6);
        print("ReadConsoleエラーです");
        _getch();
        exit(-1);
        return -1;
    } else {
        szBuf[1] = '\0';
        return 0;
    }
}
さてReadConsole関数はどうなっているかというと、
BOOL ReadConsole(
  HANDLE hConsoleInput,
  LPVOID lpBuffer,
  DWORD nNumberOfCharsToRead,
  LPDWORD lpNumberOfCharsRead,
  LPVOID lpReserved
);
のように定義されています。

hConsoleInputは入力ハンドルです。

lpBufferはコンソール入力を受け取るバッファのアドレスを指定します。

nNumberOfCharsToReadには読みとる文字数を指定します。文字数は ユニコードでも、通常のANSIでもよいです。ANSIだと文字数がバイト数になり、 ユニコードだと2倍になります。

lpNumberOfCharsReadは実際に読みとった文字数を格納する変数へのポインタです。

lpReservedは予約済みで、NULLを指定します。 関数が失敗すると0が返されます。成功すると0以外が戻ります。

int main()
{
    Mountain m;
    
    m.play();
    return 0;
}
main関数に変更はありません。

さて、今回はあちこち変更しましたが、基本的な中身については同じです。


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

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