今回は、前章までで作ったプログラムを少し視覚化して
わかりやすくします。
左の図のように、石を「●」で表してみました。
「どの山からとりますか」などの質問文も常に同じ場所に
表示するようにしました。
プログラムの仕組みは簡単です。
コンソール・アプリの場合は第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