今回は、357ゲームで勝敗がついた時点で、それまでの経過(誰がどの山から
何個の石を取ったか)を表示しゲームの分析ができるようにします。(分析と言うのは
ちょっとオーバーです)また、今までは勝敗がついたら、その時点でプログラムが終了していましたが、繰り返し ゲームができるように改良します。
では、プログラムを見てみましょう。
// stone35704.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);
int my_cls(HANDLE);
// sente:人が先手 1,後手 0, order:現在先手の番 1, 後手の番 0
int sente, order, te;
HANDLE hOut;
struct _tagStone {
char name[32]; //対戦者氏名
int IsSente; //対戦者が先手 1, 後手 0
int mt[15]; //どの山から A-0, B-1, C-2
int num[15]; //いくつ取ったか
} strecord;
新しい関数としてmy_cls関数が増えました。単に画面を全消去する関数です。
コンソール・アプリケーション以外の人は動作しないので、ご自分の環境で使える
関数を用意してください。新たな変数として現在何手目かを表すte(第n手目の時はn-1)が増えました。 また、記録用に構造体を一つ用意しました。構造体が嫌いな人は普通の変数を 用意しておいてもかまいません。
int main()
{
// mt[x] 石山の数
int mt[3], ret, i;
char ans[8], nm[8];
char *cls = " ";
//hOutの取得
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE)
return -1;
while (1) {
memset(&strecord, 0, sizeof(strecord));
te = 0;
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;
}
strecord.IsSente = sente;
locate(0, 5);
printf("あなたの氏名---");
gets(strecord.name);
order = 1;
while (1) {
ret = takestone(mt);
if (ret == 1)
break;
te++;
}
locate(0, 10);
printf("****%sさんの記録*****\n", strecord.name);
for (i = 0; i < 15; i++) {
if (strecord.num[i] == 0)
break;
if ((sente == 1 && i % 2 == 0) || (sente == 0 && i % 2 != 0)) {
strcpy(nm, "You");
} else {
strcpy(nm, "Com");
}
printf(" 第%d手 %sは%cから%d個\n", i + 1, nm, strecord.mt[i] + 'A', strecord.num[i]);
}
locate(0, 8);
printf("もう一度やりますか(Y/N)");
gets(ans);
if (strcmp(ans, "N") == 0 || strcmp(ans, "n") == 0) {
my_cls(hOut);
break;
}
my_cls(hOut);
}
return 0;
}
main関数です。
ゲームの初期化をしてから、勝敗がつくまでをwhileで無限ループにしています。
そして、「もう一度やりますか」の質問にNまたはnと答えるとループを抜けて
プログラムが終了します。1ターン(人間もしくはコンピュータが石を取った)毎にteを1増やしています。
また、ゲーム開始時に対戦者の名前を尋ねるようにしました。
勝敗がついたら、第1手目からの経過を構造体から読み出して表示するようにもしました。 さて、これで骨格はできました。後は、どこで構造体に記録するかですが、これは簡単ですね。コンピュータまたは人間が取る石を決定したときですね。と、言うことはこのあとの2カ所で記録する作業が出てくるはずです。
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);
strecord.mt[te] = mtorder;
strecord.num[te] = stone;
showno(mtno);
_getch();
return 0;
}
大変長い関数ですが、これの最後の方を見てください。コンピュータはどこどこから何個の石を取りました、と表示しますが この後で構造体に記録しています。この時配列の添え字が必要なのでteという変数を 導入していたのです。
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;
strecord.mt[te] = mtorder;
strecord.num[te] = 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;
}
人間が石を取る関数ですが、取る石が決定したところで構造体に記録しています。
これで、構造体への記録箇所がすべて出そろいました。
judge, showno, no_of_0, locateの各関数に変更はありません。
int my_cls(HANDLE hOut)
{
COORD coset;
DWORD dwWritten;
WORD wAttribute;
CONSOLE_SCREEN_BUFFER_INFO cinf;
GetConsoleScreenBufferInfo(hOut, &cinf);
coset.X = 0;
coset.Y = 0;
if (FillConsoleOutputCharacter(hOut, ' ', cinf.dwSize.X * cinf.dwSize.Y, coset, &dwWritten) ==0)
return -1;
return 0;
}
画面消去関数です。単に画面をスペースで埋め尽くしているだけです。
C言語編第61章を参照してみてください。さて、このプログラムでは対戦経過を画面表示するだけですが ファイルに記録するように改良してみてください。
また、記録した対戦を再現できるようにしてみてください。
Update Jun/16/2002 By Y.Kumei