また、iostream.hは使わないでプログラムを書いてみます。
では、プログラムを見てみましょう。
// game35703.cpp #include <windows.h> #include <time.h> #include <conio.h> class Mountain { HANDLE hOut, hIn; int mt[3]; int sente, order; int judge(); int show_stone(); int init(); int take_stone(); int comp_take(); int no_of_0(); int locate(int, int); int print(char *); int input(char *, int); public: Mountain(); int play(); };windows.hは、コンソールアプリでカーソル位置指定などで必要となります。 今回は、これをメインにプログラムを改造します。
また、画面表示のためにprint関数、入力用にinput関数を作ってみました。
Mountain::Mountain() { hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) exit(-1); hIn = GetStdHandle(STD_INPUT_HANDLE); if (hIn == INVALID_HANDLE_VALUE) exit(-1); }コンストラクタで標準入出力ハンドルを取得しています。
int Mountain::show_stone() { int i; for (i = 0; i < 3; i++) { locate(0, i); print(" "); } locate(0, 0); print("[A] "); for (i = 0; i < mt[0]; i++) print( "●"); locate(0, 1); print( "[B] "); for (i = 0; i < mt[1]; i++) print( "●"); locate(0, 2); print( "[C] "); for (i = 0; i < mt[2]; i++) print( "●"); return 0; }現在の各山の石の数を黒丸で表示する関数です。早速print関数を使っています。
int Mountain::judge() { if (mt[0] + mt[1] + mt[2] == 1) return 0; else return 1; }この関数に変更はありません。
int Mountain::take_stone() { char ans[8]; int i, stone, mtorder; for (i = 4; i <= 5; i++) { locate(0, i); print(" "); } if (sente != order) { comp_take(); if (judge() == 0) { for (i = 4; i < 6; i++) { locate(0, i); print(" "); } locate(0, 7); print("コンピュータの勝ちです"); _getch(); return 1; } if (order == 1) order = 0; else order = 1; return 0; } while (1) { locate(0, 6); print(" "); locate(0, 4); print("どの山からとりますか(A,B,C)---"); if (input(ans, 8) == -1) { locate(0, 6); print("内部エラーです"); _getch(); exit(-1); } if (strcmp(ans, "A") != 0 && strcmp(ans, "B") != 0 && strcmp(ans, "C") != 0 && strcmp(ans, "a") != 0 && strcmp(ans, "b") != 0 && strcmp(ans, "c") != 0) { for (i = 4; i <= 5; i++) { locate(0, i); print(" "); } locate(0, 6); print("山の指定が正しくありません"); _getch(); continue; } else { ans[0] = toupper(ans[0]); mtorder = ans[0] - 'A'; if (mt[mtorder] == 0) { for (i = 4; i <= 5; i++) { locate(0, i); print(" "); } locate(0, 6); print("その山にはもう石はありません"); _getch(); continue; } break; } } while (1) { locate(0, 6); print(" "); locate(0, 5); print( "いくつとりますか---"); if (input(ans, 8) == -1) { locate(0, 6); print("内部エラーです"); _getch(); exit(-1); } stone = atoi(ans); mt[mtorder] -= stone; if (mt[0] + mt[1] + mt[2] == 0) { locate(0, 5); print(" "); locate(0, 6); print("その取り方では山の石が全部0になります"); _getch(); mt[mtorder] += stone; continue; } mt[mtorder] += stone; if (mt[mtorder] - stone < 0 || stone <= 0) { locate(0, 5); print(" "); locate(0, 6); print("取る石の数が不正です"); _getch(); continue; } else { break; } } mt[mtorder] -= stone; show_stone(); if (judge() == 0) { for (i = 4; i <= 5; i++) { locate(0, i); print(" "); } locate(0, 7); print("あなたの勝ちです"); return 1; } if (order == 1) order = 0; else order = 1; return 0; }内容的には同じですが、画面表示にはprint関数を使っています。 また、コンソールからの入力読みとりにはinput関数を使っています。 input関数の2番目の引数は、コンソールから読みとる大きさを指定します。 ここでは、バイト数と思っても間違いではありません。 注意すべき点はinput(x,5);としてコンソールに「0123456」と入力してしまうと 余った入力は、次のinput関数に読みとられるので、思わぬ結果を生じるので 注意が必要です。
int Mountain::init() { char ans[8]; int i; mt[0] = 3; mt[1] = 5; mt[2] = 7; for (i = 0; i <= 8; i++) { locate(0, i); print(" "); } locate(0, 0); print("あなたが先手になりますか(Y/N)---"); if (input(ans, 8) == -1) { locate(0, 6); print("内部エラーです"); _getch(); exit(-1); } if (ans[0] == 'y' || ans[0] == 'Y') { sente = 1; } else { sente = 0; } order = 1; return 0; }これも、中身は同じですが、print関数やinput関数を使って書き換えています。
int Mountain::comp_take() { int mtorder, stone; char mtname, szBuf[64]; int jump = 0, i; srand((unsigned)time(NULL)); if (no_of_0() == 0) { //どの山も0ではない //2-4-6パターンにする if (mt[0] == 3 && mt[1] == 4 && mt[2] == 6) { mtorder = 0; stone = 1; mt[0] = 2; goto end; } if (mt[1] == 5 && mt[0] == 2 && mt[2] == 6) { mtorder = 1; stone = 1; mt[1] = 4; goto end; } if (mt[2] == 7 && mt[0] == 2 && mt[1] == 4) { mtorder = 2; stone = 1; mt[2] = 6; goto end; } //1-4-5パターンにする if (mt[0] > 1 && mt[1] + mt[2] == 9 && mt[1] * mt[2] == 20) { mtorder = 0; stone = mt[0] - 1; mt[0] = 1; goto end; } if (mt[1] > 4 && mt[0] == 1 && mt[2] == 5) { mtorder = 1; stone = mt[1] - 4; mt[1] = 4; goto end; } if (mt[2] > 4 && mt[0] == 1 && mt[1] == 5) { mtorder = 2; stone = mt[2] - 4; mt[2] = 4; goto end; } if (mt[2] > 5 && mt[0] == 1 && mt[1] == 4) { mtorder = 2; stone = mt[2] - 4; mt[2] = 4; goto end; } //1-2-3パターンにする if (mt[0] > 1 && mt[1] + mt[2] == 5 && mt[1] * mt[2] == 6) { mtorder = 0; stone = mt[0] - 1; mt[0] = 1; goto end; } if (mt[0] > 2 && mt[1] + mt[2] == 4 && mt[1] * mt[2] == 3) { mtorder = 0; stone = mt[0] - 2; mt[0] = 2; goto end; } if (mt[0] == 3 && mt[1] + mt[2] == 3 && mt[1] * mt[2] == 2) { mtorder = 0; stone = mt[0] - 3; mt[0] = 3; goto end; } if (mt[1] > 1 && mt[0] + mt[2] == 5 && mt[0] * mt[2] == 6) { mtorder = 1; stone = mt[1] - 1; mt[1] = 1; goto end; } if (mt[1] > 2 && mt[0] + mt[2] == 4 && mt[0] * mt[2] == 3) { mtorder = 1; stone = mt[1] - 2; mt[1] = 2; goto end; } if (mt[1] > 3 && mt[0] + mt[2] == 3 && mt[0] * mt[2] == 2) { mtorder = 1; stone = mt[1] - 3; mt[1] = 3; goto end; } if (mt[2] > 1 && mt[0] + mt[1] == 5 && mt[0] * mt[1] == 6) { mtorder = 2; stone = mt[2] - 1; mt[2] = 1; goto end; } if (mt[2] > 2 && mt[0] + mt[1] == 4 && mt[0] * mt[1] == 3) { mtorder = 2; stone = mt[2] - 2; mt[2] = 2; goto end; } if (mt[2] > 3 && mt[0] + mt[1] == 3 && mt[0] * mt[1] == 2) { mtorder = 2; stone = mt[2] - 3; mt[2] = 3; goto end; } //1-1-1パターンにする if (mt[0] == mt[1] && mt[0] == 1 && mt[2] > 1) { mtorder = 2; stone = mt[2] - 1; mt[2] = 1; goto end; } if (mt[1] == mt[2] && mt[1] == 1 && mt[0] > 1) { mtorder = 0; stone = mt[0] - 1; mt[0] = 1; goto end; } if (mt[0] == mt[2] && mt[0] == 1 && mt[1] > 1) { mtorder = 1; stone = mt[1] - 1; mt[1] = 1; goto end; } if (mt[0] == mt[1]) { mtorder = 2; stone = mt[2]; mt[mtorder] = 0; goto end;; } if (mt[1] == mt[2]) { mtorder = 0; stone = mt[0]; mt[mtorder] = 0; goto end;; } if (mt[2] == mt[0]) { mtorder = 1; stone = mt[1]; mt[mtorder] = 0; goto end;; } } //0の山が1つだけである if (no_of_0() == 1) { if (mt[0] == 0) { if (mt[1] == 1) { mtorder = 2; stone = mt[2]; mt[2] = 0; goto end; } if (mt[2] == 1) { mtorder = 1; stone = mt[1]; mt[1] = 0; goto end; } if (mt[1] > mt[2]) { mtorder = 1; stone = mt[1] - mt[2]; mt[mtorder] -= stone; goto end; } if (mt[2] > mt[1]) { mtorder = 2; stone = mt[2] - mt[1]; mt[mtorder] -= stone; goto end; } if (mt[2] == mt[1]){ mtorder = 1; stone = 1; mt[mtorder] -= stone; goto end; } } if (mt[1] == 0) { if (mt[0] == 1) { mtorder = 2; stone = mt[2]; mt[2] = 0; goto end; } if (mt[2] == 1) { mtorder = 0; stone = mt[0]; mt[0] = 0; goto end; } if (mt[0] > mt[2]) { mtorder = 0; stone = mt[0] - mt[2]; mt[mtorder] -= stone; goto end; } if (mt[2] > mt[0]) { mtorder = 2; stone = mt[2] - mt[0]; mt[mtorder] -= stone; goto end; } if (mt[0] == mt[2]) { mtorder = 2; stone = 1; mt[mtorder] -= stone; goto end; } } if (mt[2] == 0) { if (mt[1] == 1) { mtorder = 0; stone = mt[0]; mt[0] = 0; goto end; } if (mt[0] == 1) { mtorder = 1; stone = mt[1]; mt[1] = 0; goto end; } if (mt[0] > mt[1]) { mtorder = 0; stone = mt[0] - mt[1]; mt[mtorder] -= stone; goto end; } if (mt[1] > mt[0]) { mtorder = 1; stone = mt[1] - mt[0]; mt[mtorder] -= stone; goto end; } if (mt[1] == mt[0]) { mtorder = 0; stone = 1; mt[mtorder] -= 1; goto end; } } } if (no_of_0() == 2) { for (i = 0; i < 3; i++) { if (mt[i] != 0) { stone = mt[i] - 1; mtorder = i; mt[i] = 1; goto end; } } } while (1) { mtorder = rand() % 3; if (mt[mtorder] == 0) continue; else break; } while (1) { stone = 1; mt[mtorder] -= stone; if (mt[0] + mt[1] + mt[2] == 0) { mt[mtorder] += stone; continue; } else { break; } } end: mtname = mtorder + 'A'; locate(0, 4); wsprintf(szBuf, "コンピュータは%cから%d個取ります", mtname, stone); print(szBuf); _getch(); show_stone(); return 0; }これも中身は同じですが、print関数で書き換えています。
int Mountain::play() { int ret, i; char ans[8]; while (1) { init(); show_stone(); while (1) { ret = take_stone(); if (ret == 1) break; } locate(0, 8); print("もう一度やりますか(Y/N) "); if (input(ans, 8) == -1) { locate(0, 6); print("内部エラーです"); _getch(); exit(-1); } if (ans[0] == 'y' || ans[0] == 'Y') { continue; } else { for (i = 0; i <= 8; i++) { locate(0, i); print(" "); } locate(0, 0); break; } } return 0; }これまたinput,print関数で置き換えています。
int Mountain::no_of_0() { int no, i; no = 0; for (i = 0; i < 3; i++) { if (mt[i] == 0) no++; } return no; }この関数に変更はありません。
int Mountain::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 Mountain::print(char *szBuf) { DWORD dwToWrite, dwWritten; dwToWrite = (DWORD)strlen(szBuf); WriteConsole(hOut, szBuf, dwToWrite, &dwWritten, NULL); if (dwToWrite != dwWritten) return -1; else return 0; }hOutに出力する関数です。
WriteConsole関数についてはC言語編 第59章を参照してください。
int Mountain::input(char *szBuf, int n) { DWORD dwResult; if (ReadConsole(hIn, (LPVOID)szBuf, (DWORD)n, &dwResult, NULL) == 0) { locate(0, 6); print("ReadConsoleエラーです"); _getch(); exit(-1); return -1; } else { szBuf[1] = '\0'; return 0; } }さてReadConsole関数はどうなっているかというと、
BOOL ReadConsole( HANDLE hConsoleInput, LPVOID lpBuffer, DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, LPVOID lpReserved );のように定義されています。
hConsoleInputは入力ハンドルです。
lpBufferはコンソール入力を受け取るバッファのアドレスを指定します。
nNumberOfCharsToReadには読みとる文字数を指定します。文字数は ユニコードでも、通常のANSIでもよいです。ANSIだと文字数がバイト数になり、 ユニコードだと2倍になります。
lpNumberOfCharsReadは実際に読みとった文字数を格納する変数へのポインタです。
lpReservedは予約済みで、NULLを指定します。 関数が失敗すると0が返されます。成功すると0以外が戻ります。
int main() { Mountain m; m.play(); return 0; }main関数に変更はありません。
さて、今回はあちこち変更しましたが、基本的な中身については同じです。
Update May/17/2002 By Y.Kumei