今回は、ファイルに記録した対戦を読み出して、再現します。また、メニューを表示して
何回もゲームを繰り返して行えるようにします。
左の図のようなメニューを出すようにします。
今までは、対戦が終わるとプログラムそのものが終了していましたが、 続けて次の対戦が行えます。このためには、プログラムの初期化部分を少し 変更しなくてはいけません。
では、プログラムを見てみましょう。
// 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され プログラムの終了となります。
いろいろ不満のあるプログラムですが、遊んでみてください。
Update Jan/24/2002 By Y.Kumei