第54章 ゲームを作ろう その10


今回は、ファイルに記録した対戦を読み出して、再現します。また、メニューを表示して 何回もゲームを繰り返して行えるようにします。



左の図のようなメニューを出すようにします。

今までは、対戦が終わるとプログラムそのものが終了していましたが、 続けて次の対戦が行えます。このためには、プログラムの初期化部分を少し 変更しなくてはいけません。



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

// game10.cpp

#define MASU 6
#include <windows.h> //GetStdHandle関数などを使うので必要
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <fstream.h>

class Ban
{
    int ban[MASU][MASU], nTe; //ban[x][y]石の位置種類 nTe:何手目か
    int tekazu; //何手で勝負がついたか
    HANDLE hOut;
    BOOL bSente; //TRUE:先手 FALSE:後手
    BOOL bComp; //TRUE:コンビユータと対戦
    BOOL bCompSente; //TRUE:コンピュータが先手
    char szHantei[32];
    struct _tagRec {
        int row;
        int col;
    }Rec[MASU * MASU];
    int initialsetting();
    int Locate(int, int);
    int checkLocation(int, int);
    int bandraw();
    int judge();
    int record(int, int);
    int replay();
    int comp_put(char *);
    int isStoneGet(int, int, BOOL);//石が取れるかどうかを調べる
    int search_pos(char *); //相手の石が取れる位置を探す
    int search_corner(char *); //4隅を探す
    int set_loc(int, int, char*);
    int search_win_pos(char *, BOOL);
    int record_to_file();
    int read_from_file();
    int putStone();
    int cls();
public:
    Ban();
    int menu();
};
メンバ関数に、read_from_fileが増えました。

また、putStoneメンバ関数はプライベートとしました。そのかわり 新規のmemuメンバ関数がパブリックとなりました。

Ban::Ban()
{
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
}
コンストラクタでは、hOutを取得するのみとなりました。 これは、その他の初期化が対戦をするたびに必要なので コンストラクタに置いていたのではまずいからです。
//VC++のコンソールアプリ以外の人は、この関数を
//自分の環境に合うように書き直してください

int Ban::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;
}

int Ban::putStone()
{
    char szSashite[16], szLocation[8], cMoji, szTxt[8], szYesNo[4];
    int row, col;
    int jud;

    while (1) {
        if (bComp == FALSE) {
            if (bSente)
                strcpy(szSashite, "先手");
            else
                strcpy(szSashite, "後手");
        } else {
            if (bSente && bCompSente)
                strcpy(szSashite, "Comp[○]");
            if (bSente && !bCompSente)
                strcpy(szSashite, "Man[○]");
            if (!bSente && bCompSente)
                strcpy(szSashite, "Man[×]");
            if (!bSente && !bCompSente)
                strcpy(szSashite, "Comp[×]");
        }
        Locate(28, MASU + 2);
        printf("       ");
        Locate(0, MASU + 2);
        printf("位置を指定してください(%s)", szSashite);
        if (bComp == FALSE || (bComp && bCompSente && !bSente) || (bComp && !bCompSente && bSente)) {
            gets(szLocation);
        } else {
            comp_put(szLocation);
        }

        if (isalpha(szLocation[0]) == 0) {
            Locate(0, MASU + 3);
            printf("指定が違います");
            continue;
        }
        if (isupper(szLocation[0]) == 0)
            cMoji = toupper(szLocation[0]);
        else
            cMoji = szLocation[0];
        row = cMoji - 'A';
        if (row < 0 || row > MASU - 1) {
            Locate(0, MASU + 3);
            printf("行の指定が違います");
            continue;
        }

        if (isdigit(szLocation[1]) != 0 && isdigit(szLocation[2]) != 0 && szLocation[3] == '\0') {
            szTxt[0] = szLocation[1];
            szTxt[1] = szLocation[2];
            szTxt[2] = '\0';
            col = atoi(szTxt) - 1;
        } else if (isdigit(szLocation[1]) != 0 && szLocation[2] == '\0') {
            szTxt[0] = szLocation[1];
            szTxt[1] = '\0';
            col = atoi(szTxt) - 1;
        } else {
            Locate(0, MASU + 3);
            printf("列の指定が違います");
            continue;
        }
            
        if (col < 0 || col > MASU - 1) {
            Locate(0, MASU + 3);
            printf("列の位置指定が違います");
            continue;
        }

        if (checkLocation(row, col) != 0) {
            Locate(0, MASU + 3);
            printf("そこには置けません!");
            continue;
        }
            
        if (bSente)
            ban[row][col] = 1;
        else
            ban[row][col] = 2;
        isStoneGet(row, col, bSente);
        record(row, col);
        bSente = !bSente;
        bandraw();
        Locate(0, MASU + 3);
        printf("                    ");
        nTe++;
        if (nTe > MASU * MASU + 1)
            break;
        jud = judge();
        switch (jud) {
            case 0:
                strcpy(szHantei, "");
                break;
            case 1:
                if (!bComp)
                    strcpy(szHantei, "先手の勝ち!");
                else {
                    if (bCompSente)
                        strcpy(szHantei, "Compの勝ち");
                    else
                        strcpy(szHantei, "あなたの勝ち");
                }
                tekazu = nTe - 1;
                break;
            case 2:
                if (!bComp)
                    strcpy(szHantei, "後手の勝ち!");
                else {
                    if (bCompSente)
                        strcpy(szHantei, "あなたの勝ち");
                    else
                        strcpy(szHantei, "Compの勝ち");
                }
                tekazu = nTe - 1;
                break;
            default:
                strcpy(szHantei, "Error!");
                break;
        }
        if (jud == 0 && nTe == MASU * MASU + 1) {
            strcpy(szHantei, "引き分け!");
            tekazu = nTe - 1;
            jud = 2;
        }
        Locate(0, MASU + 4);
        printf("%s\n", szHantei);
        if (jud != 0)
            break;
    }
    Locate(0, MASU + 6);
    printf("再現しますか(Y/N) ");
    gets(szYesNo);
    if (strcmp(szYesNo, "y") == 0 || strcmp(szYesNo, "Y") == 0)
        replay();
    Locate(0, MASU + 6);
    printf("                          ");
    Locate(0, MASU + 6);
    printf("対戦を記録しますか(Y/N) ");
    gets(szYesNo);
    if (strcmp(szYesNo, "y") == 0 || strcmp(szYesNo, "Y") == 0)
        record_to_file();
    return 0;
}

int Ban::checkLocation(int x, int y)
{
    if (ban[x][y] == 0)
        return 0;
    else
        return -1;
}

int Ban::bandraw()
{
    int i, j;

    Locate(0, 0);
    printf("   ");
    for (i = 0; i < MASU; i++)
        printf("[%2d]", i + 1);
    printf("\n");
    for (i = 0; i < MASU; i++)
        printf("[%c]\n", 'A' + i);
    for (i = 0; i < MASU; i++) {
        for (j = 0; j < MASU; j++) {
            if (ban[i][j] == 1) {
                Locate(j * 4 + 4, i + 1);
                printf("○");
            }
            if (ban[i][j] == 2) {
                Locate(j * 4 + 4, i + 1);
                printf("×");
            }
        }
    }
    return 0;
}

int Ban::judge() //戻り値 1:先手の勝ち 2:後手の勝ち 0:勝敗はまだ
{
    int i, j, rseki[MASU], rwa[MASU], cseki[MASU], cwa[MASU];
    int cross1seki = 1, cross1wa = 0, cross2seki = 1, cross2wa = 0;

    // 配列の初期化
    for (i = 0; i < MASU; i++) {
        rseki[i] = 1;
        rwa[i] = 0;
        cseki[i] = 1;
        cwa[i] = 0;
    }
    
    for (i = 0; i < MASU; i++) {
        for (j = 0; j < MASU; j++) {
            rseki[i] = rseki[i] * ban[i][j];
            rwa[i]   = rwa[i] + ban[i][j];
            cseki[i] = cseki[i] * ban[j][i];
            cwa[i]   = cwa[i] + ban[j][i];
                
        }


        cross1seki = cross1seki * ban[i][i];
        cross1wa   = cross1wa + ban[i][i];
        cross2seki = cross2seki * ban[MASU - 1 - i][i];
        cross2wa   = cross2wa + ban[MASU - 1 - i][i];
    }

    for (i = 0; i < MASU; i++) {
        if (rseki[i] != 0 && rwa[i] == MASU)
            return 1;
        if (rseki[i] != 0 && rwa[i] == MASU * 2)
            return 2;
        if (cseki[i] != 0 && cwa[i] == MASU)
            return 1;
        if (cseki[i] != 0 && cwa[i] == MASU * 2)
            return 2;
    }
    if (cross1seki != 0 && cross1wa == MASU)
        return 1;
    if (cross1seki != 0 && cross1wa == MASU * 2)
        return 2;
    if (cross2seki != 0 && cross2wa == MASU)
        return 1;
    if (cross2seki != 0 && cross2wa == MASU * 2)
        return 2;
    return 0;
}

int Ban::record(int row, int col)
{
    Rec[nTe - 1].row = row;
    Rec[nTe - 1].col = col;
    return 0;
}

int Ban::replay()
{
    int i, j, stone;
    BOOL bSente = TRUE;
    //盤面初期化
    Locate(0, MASU + 4);
    printf("                    ");
    Locate(0, MASU + 2);
    printf("                                 ");
    for (i = 0; i < MASU; i++) {
        for (j = 0; j < MASU; j++) {
            Locate(i * 4 + 4, j + 1);
            printf("    ");
            ban[i][j] = 0;
        }
    }
    for (i = 0; i < tekazu; i++) {
        if (bSente)
            stone = 1;
        else
            stone = 2;
        ban[Rec[i].row][Rec[i].col] = stone;
        isStoneGet(Rec[i].row, Rec[i].col, bSente);
        bandraw();
        bSente = !bSente;
        Locate(0, MASU + 4);
        printf("再現中.....第%02d手目", i + 1);
        _getch();
    }
    Locate(0, MASU + 6);
    return 0;
}
これらの関数に変更はありません。
int Ban::initialsetting()
{
    char yesno[4];
    int i, j;

    for (i = 0; i < MASU; i++) {
        for (j = 0; j < MASU; j++) {
            ban[i][j] = 0;
        }
    }
    
    bSente = TRUE;
    nTe = 1;
    
    cls();
    Locate(0, 0);
    printf("コンピュータと対戦しますか(Y/N)==> ");
    gets(yesno);
    if (strcmp(yesno, "Y") == 0 || strcmp(yesno, "y") == 0) {
        bComp = TRUE;
        Locate(0, 1);
        printf("あなたが先手になりますか(Y/N)==> ");
        gets(yesno);
        if (strcmp(yesno, "Y") == 0 || strcmp(yesno, "y") == 0)
            bCompSente = FALSE;
        else
            bCompSente = TRUE;
    } else {
        bComp = FALSE;
    }
    Locate(0, 0);
    printf("                                             ");
    Locate(0, 1);
    printf("                                             ");
    return 0;
}
ban配列を0で埋め尽くす作業はこの関数で行うことにしました。
int Ban::comp_put(char *loc)
{
    char a1, a2[4];
    int row, col;
    
    //あと1手でコンピュータが勝つかどうか調べ勝ちます
    if (search_win_pos(loc, TRUE) == 0)
        return 0;
    //あと1手で人間が勝つかどうか調べ妨害します
    if (search_win_pos(loc, FALSE) == 0)
        return 0;
    if (search_pos(loc) == 0)
        return 0;
    if (search_corner(loc) == 0)
        return 0;

    srand((unsigned)time(NULL));

    while (1) {
        row = rand() % MASU;
        col = rand() % MASU;

        if (checkLocation(row, col) == 0)
            break;
    }

    a1 = (char)row + 'A';
    _itoa(col + 1, a2, 10);
    
    
    loc[0] = a1;
    loc[1] = '\0';
    strcat(loc, a2);

    return 0;
}

//石が取れるかどうかを調べる
int Ban::isStoneGet(int row, int col, BOOL bOrd)
{
    int jibun, aite;

    if (bOrd) {
        jibun = 1;
        aite = 2;
    } else {
        jibun = 2;
        aite = 1;
    }

    if (row - 2 >= 0 && col - 2 >=0 &&
        ban[row - 2][col - 2] == jibun && 
        ban[row - 1][col - 1] == aite) {
        ban[row - 1][col - 1] = jibun;
    }
    if (row - 2 >= 0 &&
        ban[row - 2][col] == jibun &&
        ban[row - 1][col] == aite) {
        ban[row - 1][col] = jibun;
    }
    if (row - 2 >= 0 && col + 2 < MASU &&
        ban[row - 2][col + 2] == jibun &&
        ban[row - 1][col + 1] == aite) {
        ban[row - 1][col + 1] = jibun;
    }
    if (col - 2 >= 0 &&
        ban[row][col - 2] == jibun &&
        ban[row][col - 1] == aite) {
        ban[row][col - 1] = jibun;
    }
    if (col + 2 < MASU &&
        ban[row][col + 2] == jibun &&
        ban[row][col + 1] == aite) {
        ban[row][col + 1] = jibun;
    }
    if (row + 2 < MASU && col - 2 >= 0 &&
        ban[row + 2][col - 2] == jibun &&
        ban[row + 1][col - 1] == aite) {
        ban[row + 1][col - 1] = jibun;
    }
    if (row + 2 < MASU &&
        ban[row + 2][col] == jibun &&
        ban[row + 1][col] == aite) {
        ban[row + 1][col] = jibun;
    }
    if (row + 2 < MASU && col + 2 < MASU &&
        ban[row + 2][col + 2] == jibun &&
        ban[row + 1][col + 1] == aite) {
        ban[row + 1][col + 1] = jibun;
    }
    return 0;
}

int Ban::search_pos(char *loc)
{
    int i, j, jibun, aite;

    if (bCompSente == 1) {
        jibun = 1;
        aite = 2;
    } else {
        jibun = 2;
        aite = 1;
    }

    for (i = 0; i < MASU - 2; i++) {
        for (j = 0; j < MASU; j++) {
            if (ban[i][j] == 0 && ban[i + 1][j] == aite && ban[i + 2][j] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 2; i < MASU; i++) {
        for (j = 0; j < MASU; j++) {
            if (ban[i][j] == 0 && ban[i - 1][j] == aite && ban[i - 2][j] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 0; i < MASU; i++) {
        for (j = 0; j < MASU - 2; j++) {
            if (ban[i][j] == 0 && ban[i][j + 1] == aite && ban[i][j + 2] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 0; i < MASU; i++) {
        for (j = 2; j < MASU; j++) {
            if (ban[i][j] == 0 && ban[i][j - 1] == aite && ban[i][j - 2] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 0; i < MASU - 2; i++) {
        for (j = 0; j < MASU - 2; j++) {
            if (ban[i][j] == 0 && ban[i + 1][j + 1] == aite && ban[i + 2][j + 2] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 2; i < MASU; i++) {
        for (j = 2; j < MASU; j++) {
            if (ban[i][j] == 0 && ban[i - 1][j - 1] == aite && ban[i - 2][j - 2] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 2; i < MASU; i++) {
        for (j = 0; j < MASU - 2; j++) {
            if (ban[i][j] == 0 && ban[i - 1][j + 1] == aite && ban[i - 2][j + 2] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    for (i = 0; i < MASU - 2; i++) {
        for (j = 2; j < MASU; j++) {
            if (ban[i][j] == 0 && ban[i + 1][j - 1] == aite && ban[i + 2][j -2] == jibun) {
                set_loc(i, j, loc);
                return 0;
            }
        }
    }
    return -1;
}

int Ban::search_corner(char *loc)
{
    if (ban[0][0] == 0) {
        strcpy(loc, "A1");
        return 0;
    }
    if (ban[0][MASU - 1] == 0) {
        sprintf(loc, "A%d", MASU);
        return 0;
    }
    if (ban[MASU - 1][0] == 0) {
        sprintf(loc, "%c1", MASU + 'A' - 1);
        return 0;
    }
    if (ban[MASU - 1][MASU - 1] == 0) {
        sprintf(loc, "%c%d", MASU + 'A' - 1, MASU);
        return 0;
    }
    return -1;
}

int Ban::set_loc(int row, int col, char *loc)
{
    char szBuf[8], szTxt[256];

    loc[0] = 'A' + row;
    loc[1] = '\0';
    _itoa(col + 1, szBuf, 10);
    strcat(loc, szBuf);
    sprintf(szTxt, "コンピュータは(%s)に駒を置いてあなたの石を取ります。", loc);
    Locate(0, MASU + 5);
    printf(szTxt);
    _getch();
    Locate(0, MASU + 5);
    printf("                                                                                ");
    return 0;
}

int Ban::search_win_pos(char *loc, BOOL bCompWin)
{
    int jibun, aite, i, j, nWinner;
    char szBuf[8];

    if (bSente == TRUE) {
        jibun = 1;
        aite = 2;
    } else {
        jibun = 2;
        aite = 1;
    }

    if (bCompWin)
        nWinner = jibun;
    else
        nWinner = aite;
    
    for (i = 0; i < MASU; i++) {
        for (j = 0; j < MASU; j++) {
            if (ban[i][j] == 0) {
                if (bCompWin)
                    ban[i][j] = jibun;
                else
                    ban[i][j] = aite;
                if (judge() == nWinner) {
                    loc[0] = 'A' + i;
                    loc[1] = '\0';
                    _itoa(j + 1, szBuf, 10);
                    strcat(loc, szBuf);
                    ban[i][j] = 0;
                    Locate(0, MASU + 6);
                    if (bCompWin)
                        printf("(%s)でコンピュータが勝ちます!!", loc);
                    else
                        printf("(%s)で人間が勝ちます!!", loc);
                    _getch();
                    Locate(0, MASU + 6);
                    printf("                                 ");
                    return 0;
                }else {
                    ban[i][j] = 0;
                }
            }
        }
    }
    return -1;
}

int Ban::record_to_file()
{
    int i;
    ofstream Jout;
    ifstream Jin;
    char filename[64];
start:
    Locate(0, MASU + 6);
    printf("                                         ");
    Locate(0, MASU + 6);
    printf("保存するファイルの名前==>");
    gets(filename);
    Jin.open(filename, ios::nocreate);
    if (Jin.fail() == FALSE) {
        Jin.close();
        Locate(0, MASU + 6);
        printf("                                          ");
        Locate(0, MASU + 6);
        printf("すでに存在するファイルです");
        _getch();
        goto start;
    }
    Jout.open(filename);
    Jout.seekp(0);
    for (i = 0; i < tekazu; i++) {
        Jout << Rec[i].row;
        Jout << ",";
        Jout << Rec[i].col;
        Jout << endl;
    }
    Jout.close();
    return 0;
}
これらの関数に変更はありません。
int Ban::read_from_file()
{
    ifstream Jin;
    char filename[64], buf[32], *token, sep[] = ",";
    int i = 0;

    cls();
    Locate(0, 0);
    cout << "読み込むファイル名==>";
    cin >> filename;
    Jin.open(filename, ios::nocreate);
    if (Jin.is_open() == 0) {
        cout << "ファイルが存在しません" << endl;
        getch();
        return -1;
    }
    cls();
    while (1) {
        Jin >> buf;
        if (Jin.eof())
            break;
        token = strtok(buf, sep);
        Rec[i].row = atoi(token);
        token = strtok(NULL, sep);
        Rec[i].col = atoi(token);
        i++;
    }
    Jin.close();
    tekazu = i;
    replay();

    return 0;
}
今回、新たに作ったファイルから読み出して再現するメンバ関数です。

読み出すファイル名を聞き出したら、無限ループに入ります。

ファイルから1行読みだしてbufに格納します。 もし、ファイルの終わりまできていれば、breakでループを脱出します。 ファイルの終わりまで来ているかどうかは、eof関数で調べます。

ios::eof
int eof() const;
これは、ファイルの終わりに達していれば0以外を返します。

次に読み込んだ内容の処理です。

「3,4」などのように書かれていますので、これを「,」で切り分ける必要があります。 これには、strtok関数が便利です。この関数についてはSDK編 第78章に 解説があるので参照してみてください。

カンマで切り分けたら、Rec構造体配列に書き込んでいきます。そして、prplay関数を 呼べばよいですね。

int Ban::cls()
{
    COORD co;
    DWORD dwWritten, dwFillSize;
    CONSOLE_SCREEN_BUFFER_INFO ci;
 
    co.X = 0;
    co.Y = 0;
    GetConsoleScreenBufferInfo(hOut, &ci);
    dwFillSize = ci.dwSize.X * ci.dwSize.Y;

    FillConsoleOutputCharacter(hOut, ' ', dwFillSize, co, &dwWritten);
    return 0;
}
画面消去のメンバ関数です。これは、C言語編 第61章のものをそのまま流用しています。

コンソール・アプリケーション以外の人は独自に工夫してください。

int Ban::menu()
{
    int select, end = 0;
    
    while (1) {
        cls();
        Locate(0, 0);
        cout << "===========" << endl;
        cout << "1.対戦" << endl;
        cout << "2.記録の読み込み" << endl;
        cout << "3.終了" << endl;
        cout << "===========" << endl;
        cout << "選択== ";
        cin >> select;

        switch (select) {
            case 1: initialsetting();
                bandraw();
                putStone();
                break;
            case 2:
                read_from_file();
                break;
            case 3:
                end = 1;
                break;
        }
        if (end == 1)
            break;
    }
    return 0;
}
メニュー表示のためのメンバ関数です。「対戦」が選択されるたびに initialsetting関数を呼んで初期化しています。

さて、終了ですがこの時endに1を設定してbreakで、1つ外側のループに出ます。 そこでは、endが1ならさらにbreakされて、さらにループを抜けて、returnされ プログラムの終了となります。

いろいろ不満のあるプログラムですが、遊んでみてください。


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

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