これは、C言語編第64章からのゲームと同じです。 このC++版というわけです。最初は単に交互に石を置くだけで、勝敗は判定しません。 盤の大きさも3*3しかありません。 ただ、相手の置いたところに石はおけません。
また、VC++のコンソールアプリで作ることを想定してます。 他の環境で作る人は、カーソル位置を指定する方法が異なると思いますので この部分を書き直してください。
まず、この石置きゲームに必要なクラスを考えてみます。
クラスの名前は「盤」からBanとでもしておきます。
このBanクラスに必要なメンバ関数は、カーソル位置を指定する 関数(石を書くのに必要)、石がおけるかどうかを判定する関数、 石を置く関数、盤面を描画する関数などでしょうか。
では、プログラムをみてみましょう。
ban[x][y]には、石の種類を記憶させます。 何も置かれていないところは0,先手が置いたところは1, 後手が置いたところは2とします。// game01.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:後手 int Locate(int, int); int checkLocation(int, int); int bandraw(); public: Ban(); int putStone(); };
BOOL型はTRUEかFALSEしか値を取らないデータ型です。 わかりにくければint型にして0と1で代用してもかまいません。
コンストラクタです。盤をすべて0で埋めて、hOutを取得しておきます。 最初は先手なのでbSenteをTRUEにして、何手目かを表すnTeに1を代入しておきます。 そして、banDrawメンバ関数を呼んで盤面を描画させます。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; }
SetConsoleCursorPosition関数についてはC言語編 第59章を参照してください。
石を置くメンバ関数です。int Ban::putStone() { char szSashite[8], szLocation[8]; int row, col; 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; } return 0; }
まず、差し手に位置を指定させます。差し手が「A2」と指定すると この文字列はszLocationに格納されます。 この文字列の先頭には'A'で次が'2'ということになります。 配列なのでszLocation[0]に'A'がszLocation[1]に'2'が、szLocation[2] に'\0'が格納されています。これを調べて実際の位置を知ります。 もし、今の差し手が先手なら(bSenteがTRUE)ban[0][1]に1を代入します。 そして、bandrawメンバ関数を呼んで盤を描画させます。 この関数はban配列を見て石を書くわけです。
次に何手目か(nTe)を1増やし、bSenteを後手にします。
!bSenteは、もしbSenteがTRUEならFALSEに、FALSEならTRUEにします。
もし、nTeが9より大きいならもう盤に石はおけないので returnします。
指定したところに石が置けるかどうかを判定するメンバ関数です。int Ban::checkLocation(int x, int y) { if (ban[x][y] == 0) return 0; else return -1; }
原理は簡単です。banを見て、指定された位置が0でなければ、 誰かの石が置いてあるので−1を返します。置けるなら0を返します。
盤を描画するメンバ関数です。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; }
ban配列を見て、石を描画します。
main関数です。たったこれだけです。int main() { Ban MyBan; MyBan.putStone(); return 0; }
石を縦、横、対角線に一列に並べた方が勝ちです。勝敗を 判定する関数を考えてみてください。また、盤が4*4とか 5*5のように大きくなったときのプログラムも考えてみてください。
Update May/14/2001 By Y.Kumei