左の図のようなメニューを出すようにします。
今までは、対戦が終わるとプログラムそのものが終了していましたが、 続けて次の対戦が行えます。このためには、プログラムの初期化部分を少し 変更しなくてはいけません。
では、プログラムを見てみましょう。
// 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