第332章 コンピュータを少し強くする


今回は、表題の通りコンピュータを少しだけ強くします。 強くするといっても、人間が3を作ったらこれを止めます。 4が出来たら5にならないように止めます。これだけです。 しかし、実際にプログラムを書いてみると結構面倒くさくなります。



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

リソース・スクリプトに変更はありません。
//        gomoku07x.cpp

#ifndef STRICT
    #define STRICT
#endif
#include <windows.h>
#include <windowsx.h>
#include <time.h>
#include "resource.h"

#define SHUI 30 //碁盤の周囲の幅
#define KANKAKU 20 //碁盤のマス目の間隔
#define STONESIZE 10 //碁石の半径

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK MyNameProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
BOOL MyMakeBan(HDC);
BOOL MyCircle(HDC, int, int, int); //盤(X,Y)に半径Rの円を描画
BOOL SetStone(HWND, int, int);
BOOL MyStoneDraw(HDC);
int Is5(HWND, int, int);
int Is4(HWND); //4があるかどうか調べその数を返す
int Is3(HWND); //3があるかどうか調べその数を返す
BOOL MyRecord(HWND, char *, char *); //対戦を記録する
BOOL MyGetTime(char *);
BOOL MyRead(HWND); //対戦をファイルから読み込む
int SetRecFromFile(char *); //ファイルからの情報を元にRec配列をセットする
BOOL Replay(HWND, int); //対戦を再現する
BOOL CompPlay(HWND); // コンピュータと対戦する時、先手かどうか決める
BOOL CompPut(HWND); //コンピュータが打つ
BOOL SearchPos(HWND, int *, int *); //コンピュータが打つべき位置を探す
int Comp3, Comp4; //コンピュータの3,4の数

char szClassName[] = "gomoku07";    //ウィンドウクラス
HINSTANCE hInst;
char szBuf[128];
BOOL bSente = TRUE; //現在の差し手 先手:TRUE 後手:FALSE
BOOL bStart = FALSE; //対戦中かどうか
BOOL bShoHai = FALSE; //勝敗がついているか
int ban[15][15]; //0:石無し 1:先手 2:後手
int nTe; //何手目か
BOOL bName = FALSE; //対戦者氏名を記録するかどうか
BOOL bComp = FALSE; //コンピュータと対戦するかどうか
BOOL bCompSente; //コンピュータが先手かどうか
char szSenteName[32], szGoteName[32]; //対戦者氏名
char szStartTime[32], szEndTime[32]; //対戦開始、終了日時
struct _tagRec{
    int row;
    int col;
} Rec[15 * 15];

//4の時の空いている場所
struct _tagTobi4{
    int i;
    int j;
} Akiin4[4];
BOOL bAkiin4[4];
新しく4が出来たときの空きの位置を格納する構造体を作りました。 これは、どういうことかというと、4の存在を調査するとき連続する5つのますについて 1つの空きと、同じ石4個が存在するかどうかを調べました。この時の「空き」を意味しています。 そして、これが存在すればbAkiin4配列をTRUEにします。4方向について調べるので配列としています。
WinMain, InitApp, InitInstance, WndProc, MyMakeBan, MyCircle, SetStone, MyStoneDraw,
Is5の各関数に変更はありません。
int Is4(HWND hWnd) //4になっていないか調べる
{
    int i, j, k, wa = 0, nCount0 = 0, nCount4 = 0, nJibunwa;
    BOOL bLoop = TRUE;
    bAkiin4[0] = bAkiin4[1] = bAkiin4[2] = bAkiin4[3] = FALSE;

    if (bSente)
        nJibunwa = 4;
    else
        nJibunwa = 8;

    for (i = 0; i < 11; i++) {
        for (j = 0; j < 15; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i + k][j] == 0) {
                    nCount0++;
                    Akiin4[0].i = i + k;
                    Akiin4[0].j = j;
                }
                wa = wa + ban[i + k][j];
                
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bAkiin4[0] = TRUE;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }


    bLoop = TRUE;
    for (i = 0; i < 15; i++) {
        for (j = 0; j < 11; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i][j + k] == 0) {
                    nCount0++;
                    Akiin4[1].i = i;
                    Akiin4[1].j = j + k;
                }
                wa += ban[i][j + k];
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bAkiin4[1] = TRUE;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }

    bLoop = TRUE;
    for (i = 0; i < 11; i++) {
        for (j = 0; j < 11; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i + k][j + k] == 0) {
                    nCount0++;
                    Akiin4[2].i = i + k;
                    Akiin4[2].j = j + k;
                }
                wa += ban[i + k][j + k];
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bAkiin4[2] = TRUE;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }

    bLoop = TRUE;
    for (i = 4; i < 15; i++) {
        for (j = 0; j < 11; j++) {
            nCount0 = 0;
            wa = 0;
            for (k = 0; k < 5; k++) {
                if (ban[i - k][j + k] == 0) {
                    nCount0++;
                    Akiin4[3].i = i - k;
                    Akiin4[3].j = j + k;
                }
                wa += ban[i - k][j + k];
            }
            if (nCount0 == 1 && wa == nJibunwa) {
                nCount4++;
                bAkiin4[3] = TRUE;
                bLoop = FALSE;
                break;
            }
        }
        if (bLoop != TRUE)
            break;
    }
    return nCount4;
}
コンピュータと対戦する時、コンピュータが石を置く位置を考えるときにも使えるよう 少し変更しました。

縦、横、対角線方向で4が出来た時bAkiin4[x]にTRUEが入ります。また、この時 5つの並びの空きの位置をAkiin4[x]構造体に格納します。

Is3, MyRecord, MyGetTime, MyNameProc, MyRead, SetRecFromFile, Replay, CompPlay,
CompPutの各関数に変更はありません。
BOOL SearchPos(HWND hWnd, int *x, int *y)
{
    int i, j, n = 0;
    int Jibun, Aite;

    if (bCompSente) {
        Jibun = 1;
        Aite = 2;
    } else {
        Jibun = 2;
        Aite = 1;
    }

    if ((nTe == 0 || nTe == 1) && ban[7][7] == 0) {
        *x = 7;
        *y = 7;
        return TRUE;
    }

    //自分に5が出来ないか調べ、出来るのであれば5にして勝つ
    for (j = 0; j < 15; j++) {
        for (i = 0; i < 15; i++) {
            if (ban[i][j] == 0) {
                ban[i][j] = Jibun;
                if (Is5(hWnd, i, j) == 1) {
                    *x = i;
                    *y = j;
                    ban[i][j] = 0;
                    return TRUE;
                } else {
                    ban[i][j] = 0;
                }
            }
        }
    }

    //相手に5が出来ないか調べもし出来るのであればこれを防ぐ
    bSente = !bSente;
    for (j = 0; j < 15; j++) {
        for (i = 0; i < 15; i++) {
            if (ban[i][j] == 0) {
                ban[i][j] = Aite;
                
                if (Is5(hWnd, i, j) == 1) {
                    *x = i;
                    *y = j;
                    ban[i][j] = 0;
                    bSente = !bSente;
                    return TRUE;
                } else {
                    ban[i][j] = 0;
                }
                
            }
        }
    }
    bSente = !bSente;

    //相手に4が出来ないか調べ出来るのであればこれを防ぐ
    bSente = !bSente;
    for (j = 0; j < 15; j++) {
        for (i = 0; i < 15; i++) {
            if (ban[i][j] == 0) {
                ban[i][j] = Aite;
                if (Is4(hWnd) >= 1) {
                    for (n = 0; n < 4; n++) {
                        if (bAkiin4[n] == TRUE) {
                            *x = Akiin4[n].i;
                            *y = Akiin4[n].j;
                        } else {
                            continue;
                        }
                        ban[i][j] = 0;
                        bSente = !bSente;
                        return TRUE;
                    }
                } else {
                    ban[i][j] = 0;
                }
            }
        }
    }
    bSente = !bSente;


    for (j = 0; j < 15; j++) {
        for (i = 0; i < 15; i++) {
            if (ban[i][j] == 0) {
                ban[i][j] = Jibun;
                if (Is4(hWnd) >= 1 && Comp4 == 0){
                    *x = i;
                    *y = j;
                    ban[i][j] = 0;
                    Comp4++;
                    return TRUE;
                } else if (Is3(hWnd) >= 1 && Comp3 == 0) {
                    *x = i;
                    *y = j;
                    ban[i][j] = 0;
                    Comp3++;
                    return TRUE;
                } else {
                    ban[i][j] = 0;
                }
            }
        }
    }
    Comp4 = 0;
    Comp3 = 0;
    return FALSE;
}
コンピュータが石を置く位置を決定する関数です。

まず、盤面に片端から石を置き自分が5になって勝たないかを調べます。 勝つ位置が存在するなら、そこに石を置いて勝ちます。

次に、相手が5になる位置がないか調べて、もしあるならそこに石を置いて妨害します。

次に、相手が4になる位置がないか調べます。これは、相手に止めていない3が出来ていないか 調べることに相当します。そして、存在するならこれを止めます。

これには、Is4関数を変更して連続した5つの並びの1つの空きの位置を調べていることが役立ちます。 この空きの位置に石を置くと、相手の3を止めたことになります。

あとは、自分に4や3が出来ないかを調べています。

ここでは、5並べのアルゴリズムは何も使っていません。人間の打った3や4は止めますが、 後は偶然に頼る打ち方なので非常に弱いです。改良して強くしてみてください。


[SDK第4部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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