では、プログラムを見てみましょう。
リソース・スクリプトに変更はありません。
// 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は止めますが、 後は偶然に頼る打ち方なので非常に弱いです。改良して強くしてみてください。
Update 08/Apr/2002 By Y.Kumei