では、プログラムを見てみましょう。
「**の勝ち」などの判定の文字列を格納するszHanteと判定を行うjudgeメンバ関数が 増えました。// game02.cpp #include <windows.h> //GetStdHandle関数などを使うので必要 #include <iostream.h> #include <stdio.h> class Ban { int ban[3][3], nTe; //ban[x][y]石の位置種類 nTe:何手目か HANDLE hOut; BOOL bSente; //TRUE:先手 FALSE:後手 char szHantei[32]; int Locate(int, int); int checkLocation(int, int); int bandraw(); int judge(); public: Ban(); int putStone(); };
コンストラクタに変更はありません。Ban::Ban() { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { ban[i][j] = 0; } } hOut = GetStdHandle(STD_OUTPUT_HANDLE); bSente = TRUE; nTe = 1; bandraw(); }
カーソル位置を指定するメンバ関数に変更はありません。//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; }
jud = judge();で判定結果をjudに格納します。judの値によってszHanteiに勝敗の文字列を書き込みます。 勝敗がついていないときは何も書き込みません。判定がついたとき(judが0以外の時)は無限ループを抜けます。int Ban::putStone() { char szSashite[8], szLocation[8]; int row, col; int jud; while (1) { if (bSente) strcpy(szSashite, "先手"); else strcpy(szSashite, "後手"); Locate(28, 5); printf(" "); Locate(0, 5); printf("位置を指定してください(%s)", szSashite); gets(szLocation); if (szLocation[0] == 'A' || szLocation[0] == 'a') row = 0; else if (szLocation[0] == 'B' || szLocation[0] == 'b') row = 1; else if (szLocation[0] == 'C' || szLocation[0] == 'c') row = 2; else if (szLocation[0] == 'Q' || szLocation[0] == 'q') break; else { Locate(0, 6); printf("指定が違います"); continue; } if (szLocation[1] == '1') col = 0; else if (szLocation[1] == '2') col = 1; else if (szLocation[1] == '3') col = 2; else { Locate(0, 6); printf("指定が違います"); continue; } if (checkLocation(row, col) != 0) { Locate(0, 6); printf("そこには置けません!"); continue; } if (bSente) ban[row][col] = 1; else ban[row][col] = 2; bSente = !bSente; bandraw(); Locate(0, 6); printf(" "); nTe++; if (nTe > 9) break; jud = judge(); switch (jud) { case 0: strcpy(szHantei, ""); break; case 1: strcpy(szHantei, "先手の勝ち!"); break; case 2: strcpy(szHantei, "後手の勝ち!"); break; default: strcpy(szHantei, "Error!"); break; } Locate(0, 7); printf("%s\n", szHantei); if (jud != 0) break; } 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(" [1][2][3]\n"); printf("[A]\n"); printf("[B]\n"); printf("[C]\n"); for (i = 0; i < 3; i++) { for (j = 0; j < 3; 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[3], rwa[3], cseki[3], cwa[3]; int cross1seki = 1, cross1wa = 0, cross2seki = 1, cross2wa = 0; // 配列の初期化 for (i = 0; i < 3; i++) { rseki[i] = 1; rwa[i] = 0; cseki[i] = 1; cwa[i] = 0; } for (i = 0; i < 3; i++) { for (j = 0; j < 3; 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[2 - i][i]; cross2wa = cross2wa + ban[2 - i][i]; } for (i = 0; i < 3; i++) { if (rseki[i] != 0 && rwa[i] == 3) return 1; if (rseki[i] != 0 && rwa[i] == 6) return 2; if (cseki[i] != 0 && cwa[i] == 3) return 1; if (cseki[i] != 0 && cwa[i] == 6) return 2; } if (cross1seki != 0 && cross1wa == 3) return 1; if (cross1seki != 0 && cross1wa == 6) return 2; if (cross2seki != 0 && cross2wa == 3) return 1; if (cross2seki != 0 && cross2wa == 6) return 2; return 0; }
原理は縦、横、対角線についてban[x][y]の和と積を求めます。 和が3で積が0でなければ先手の勝ちです。和が6で積が0でなければ 後手の勝ちです。これ以外の時はまだ勝敗がついていません。 なぜ、このようなことが言えるのか考えてみてください。他にも もっとよいアルゴリズムがあるはずなので、これも考えてみてください。
main関数に変更はありません。int main() { Ban MyBan; MyBan.putStone(); return 0; }
現在盤面は3*3と非常に小さく、ゲームとしては大変つまらないものです。 これを拡張してみてください。また、両端を挟まれると、相手の石になってしまう というようなルールを加えるとどうなるかも考えてみてください。
Update Jun/03/2001 By Y.Kumei