左の図のように、石を「●」で表してみました。
「どの山からとりますか」などの質問文も常に同じ場所に
表示するようにしました。
プログラムの仕組みは簡単です。
コンソール・アプリの場合は第59章の要領でカーソル位置を指定して やればよいのです。その他の場合は、カーソル位置指定をそれぞれの 環境にあった方法で行ってください。
では、プログラムをみてみましょう。
// stone35703.c #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <conio.h> int comptake(int *); int judge(int *); int takestone(int *); int showno(int *); int no_of_0(int *); int locate(int, int); // sente:人が先手 1,後手 0, order:現在先手の番 1, 後手の番 0 int sente, order; //Standard Output Handle HANDLE hOut;GetStdHandle関数等を使うのでwindows.hのインクルードが必要です。
_getch関数を使うのでconio.hのインクルードが必要です。
今回新たにlocate関数を作りました。
int main() { // mt[x] 石山の数 int mt[3], ret; char ans[8]; //hOutの取得 hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) return -1; mt[0] = 3; mt[1] = 5; mt[2] = 7; showno(mt); locate(0, 4); printf("あなたが先手になりますか(Y/N)---"); gets(ans); if (strcmp(ans, "y") == 0 || strcmp(ans, "Y") == 0) { sente = 1; } else { sente = 0; } order = 1; while (1) { ret = takestone(mt); if (ret == 1) break; } return 0; }プログラムが始まったらすぐにGetStdHandle関数で標準出力ハンドルを 取得してグローバル変数に格納します。
「あなたが先手になりますか」という質問を早速locate関数で 位置を指定して出力しています。後は、今までと同じです。
int comptake(int *mtno) { int mtorder, stone, jump = 0, i; srand((unsigned)time(NULL)); if (no_of_0(mtno) == 0) { //どの山も0ではない //2-4-6パターンにする if (mtno[0] == 3 && mtno[1] == 4 && mtno[2] == 6) { mtorder = 0; stone = 1; mtno[0] = 2; goto end; } if (mtno[1] == 5 && mtno[0] == 2 && mtno[2] == 6) { mtorder = 1; stone = 1; mtno[1] = 4; goto end; } if (mtno[2] == 7 && mtno[0] == 2 && mtno[1] == 4) { mtorder = 2; stone = 1; mtno[2] = 6; goto end; } //1-4-5パターンにする if (mtno[0] > 1 && mtno[1] + mtno[2] == 9 && mtno[1] * mtno[2] == 20) { mtorder = 0; stone = mtno[0] - 1; mtno[0] = 1; goto end; } if (mtno[1] > 4 && mtno[0] == 1 && mtno[2] == 5) { mtorder = 1; stone = mtno[1] - 4; mtno[1] = 4; goto end; } if (mtno[2] > 4 && mtno[0] == 1 && mtno[1] == 5) { mtorder = 2; stone = mtno[2] - 4; mtno[2] = 4; goto end; } if (mtno[2] > 5 && mtno[0] == 1 && mtno[1] == 4) { mtorder = 2; stone = mtno[2] - 4; mtno[2] = 4; goto end; } //1-2-3パターンにする if (mtno[0] > 1 && mtno[1] + mtno[2] == 5 && mtno[1] * mtno[2] == 6) { mtorder = 0; stone = mtno[0] - 1; mtno[0] = 1; goto end; } if (mtno[0] > 2 && mtno[1] + mtno[2] == 4 && mtno[1] * mtno[2] == 3) { mtorder = 0; stone = mtno[0] - 2; mtno[0] = 2; goto end; } if (mtno[0] == 3 && mtno[1] + mtno[2] == 3 && mtno[1] * mtno[2] == 2) { mtorder = 0; stone = mtno[0] - 3; mtno[0] = 3; goto end; } if (mtno[1] > 1 && mtno[0] + mtno[2] == 5 && mtno[0] * mtno[2] == 6) { mtorder = 1; stone = mtno[1] - 1; mtno[1] = 1; goto end; } if (mtno[1] > 2 && mtno[0] + mtno[2] == 4 && mtno[0] * mtno[2] == 3) { mtorder = 1; stone = mtno[1] - 2; mtno[1] = 2; goto end; } if (mtno[1] > 3 && mtno[0] + mtno[2] == 3 && mtno[0] * mtno[2] == 2) { mtorder = 1; stone = mtno[1] - 3; mtno[1] = 3; goto end; } if (mtno[2] > 1 && mtno[0] + mtno[1] == 5 && mtno[0] * mtno[1] == 6) { mtorder = 2; stone = mtno[2] - 1; mtno[2] = 1; goto end; } if (mtno[2] > 2 && mtno[0] + mtno[1] == 4 && mtno[0] * mtno[1] == 3) { mtorder = 2; stone = mtno[2] - 2; mtno[2] = 2; goto end; } if (mtno[2] > 3 && mtno[0] + mtno[1] == 3 && mtno[0] * mtno[1] == 2) { mtorder = 2; stone = mtno[2] - 3; mtno[2] = 3; goto end; } //1-1-1パターンにする if (mtno[0] == mtno[1] && mtno[0] == 1 && mtno[2] > 1) { mtorder = 2; stone = mtno[2] - 1; mtno[2] = 1; goto end; } if (mtno[1] == mtno[2] && mtno[1] == 1 && mtno[0] > 1) { mtorder = 0; stone = mtno[0] - 1; mtno[0] = 1; goto end; } if (mtno[0] == mtno[2] && mtno[0] == 1 && mtno[1] > 1) { mtorder = 1; stone = mtno[1] - 1; mtno[1] = 1; goto end; } if (mtno[0] == mtno[1]) { mtorder = 2; stone = mtno[2]; mtno[mtorder] = 0; goto end;; } if (mtno[1] == mtno[2]) { mtorder = 0; stone = mtno[0]; mtno[mtorder] = 0; goto end;; } if (mtno[2] == mtno[0]) { mtorder = 1; stone = mtno[1]; mtno[mtorder] = 0; goto end;; } } //0の山が1つだけである if (no_of_0(mtno) == 1) { if (mtno[0] == 0) { if (mtno[1] == 1) { mtorder = 2; stone = mtno[2]; mtno[2] = 0; goto end; } if (mtno[2] == 1) { mtorder = 1; stone = mtno[1]; mtno[1] = 0; goto end; } if (mtno[1] > mtno[2]) { mtorder = 1; stone = mtno[1] - mtno[2]; mtno[mtorder] -= stone; goto end; } if (mtno[2] > mtno[1]) { mtorder = 2; stone = mtno[2] - mtno[1]; mtno[mtorder] -= stone; goto end; } if (mtno[2] == mtno[1]){ mtorder = 1; stone = 1; mtno[mtorder] -= stone; goto end; } } if (mtno[1] == 0) { if (mtno[0] == 1) { mtorder = 2; stone = mtno[2]; mtno[2] = 0; goto end; } if (mtno[2] == 1) { mtorder = 0; stone = mtno[0]; mtno[0] = 0; goto end; } if (mtno[0] > mtno[2]) { mtorder = 0; stone = mtno[0] - mtno[2]; mtno[mtorder] -= stone; goto end; } if (mtno[2] > mtno[0]) { mtorder = 2; stone = mtno[2] - mtno[0]; mtno[mtorder] -= stone; goto end; } if (mtno[0] == mtno[2]) { mtorder = 2; stone = 1; mtno[mtorder] -= stone; goto end; } } if (mtno[2] == 0) { if (mtno[1] == 1) { mtorder = 0; stone = mtno[0]; mtno[0] = 0; goto end; } if (mtno[0] == 1) { mtorder = 1; stone = mtno[1]; mtno[1] = 0; goto end; } if (mtno[0] > mtno[1]) { mtorder = 0; stone = mtno[0] - mtno[1]; mtno[mtorder] -= stone; goto end; } if (mtno[1] > mtno[0]) { mtorder = 1; stone = mtno[1] - mtno[0]; mtno[mtorder] -= stone; goto end; } if (mtno[1] == mtno[0]) { mtorder = 0; stone = 1; mtno[mtorder] -= 1; goto end; } } } if (no_of_0(mtno) == 2) { for (i = 0; i < 3; i++) { if (mtno[i] != 0) { stone = mtno[i] - 1; mtorder = i; mtno[i] = 1; goto end; } } } while (1) { mtorder = rand() % 3; if (mtno[mtorder] == 0) continue; else break; } while (1) { stone = 1; mtno[mtorder] -= stone; if (mtno[0] + mtno[1] + mtno[2] == 0) { mtno[mtorder] += stone; continue; } else { break; } } end: for (i = 4; i < 7; i++) { locate(0, i); printf(" "); } for (i = 4; i < 8; i++) { locate(0, i); printf(" "); } locate(0, 6); printf("コンピュータは%cから%d個取りました", mtorder + 'A', stone); showno(mtno); _getch(); return 0; }最後の方だけ、少し変更しました。
文字表示の時、位置指定しています。また、コンピュータが 石をとった後、_getch関数を入れてユーザーが何かキーを押さないと 次に行かないようにしてみました。
int takestone(int *mt) { char ans[8]; int i, stone, mtorder; if (sente != order) { comptake(mt); if (judge(mt) == 0) { locate(0, 7); printf(" "); locate(0, 7); printf("コンピュータの勝ちです\n"); return 1; } if (order == 1) order = 0; else order = 1; return 0; } while (1) { for (i = 4; i < 8; i++) { locate(0, i); printf(" "); } locate(0, 4); printf("どの山からとりますか(A,B,C)---"); gets(ans); locate(0, 7); printf(" "); 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) { locate(0, 7); printf("山の指定が正しくありません"); _getch(); continue; } else { ans[0] = toupper(ans[0]); mtorder = ans[0] - 'A'; if (mt[mtorder] == 0) { locate(0, 7); printf("その山にはもう石はありません"); _getch(); continue; } break; } } while (1) { locate(0, 5); printf(" "); locate(0, 5); printf("いくつとりますか---"); gets(ans); stone = atoi(ans); mt[mtorder] -= stone; if (mt[0] + mt[1] + mt[2] == 0) { locate(0, 7); printf("その取り方では山の石が全部0になります"); mt[mtorder] += stone; _getch(); continue; } mt[mtorder] += stone; if (mt[mtorder] - stone < 0 || stone <= 0) { locate(0, 7); printf("取る石の数が不正です"); _getch(); continue; } else { break; } } mt[mtorder] -= stone; showno(mt); if (judge(mt) == 0) { locate(0, 7); printf(" "); locate(0, 7); printf("あなたの勝ちです\n"); return 1; } if (order == 1) order = 0; else order = 1; return 0; }文字列表示をすべて位置指定しています。また、何かを 出力するときに、前の表示が残っていてはみっともないので スペースを大量に書き込んで以前の表示を消してから 新しい表示を書くようにしました。これも、プログラム的には みっともないので、もう少しスマートなものに書き換えてください。 コンソールアプリの時は、画面表示をすべて消すときに使ったアレを 使ってください。
int judge(int *mt) { if (mt[0] + mt[1] + mt[2] == 1) return 0; else return 1; }この関数に変更はありません。
int showno(int *mt) { int i; for (i = 0; i < 3; i++) { locate(0, i); printf(" "); } locate(0, 0); printf("[A] "); for (i = 0; i < mt[0]; i++) printf("●"); locate(0, 1); printf("[B] "); for (i = 0; i < mt[1]; i++) printf("●"); locate(0, 2); printf("[C] "); for (i = 0; i < mt[2]; i++) printf("●"); return 0; }石を黒丸で表示する部分です。特に難しくはないですね。forを使うときは うっかりミスの無いようにしてください。(0から始まるのかどうか、 条件にイコールが入っているのかどうかなど)
int no_of_0(int *mt) { int no, i; no = 0; for (i = 0; i < 3; i++) { if (mt[i] == 0) no++; } return no; }この関数に変更はありません。
int 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関数などの コンソールAPIを使えない環境の人は、その環境にあったカーソル位置指定法を 使ってください。
今回は簡単でしたね。
Update May/07/2002 By Y.Kumei