第49章 ゲームを作ろう その5


今回はコンピュータと対戦できるように改良します。
C言語編第68章のC++版です。



では、さっそくプログラムを見てみましょう。

// game05.cpp #define MASU 6 #include <windows.h> //GetStdHandle関数などを使うので必要 #include <iostream.h> #include <stdio.h> #include <conio.h> #include <time.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 *); public: Ban(); int putStone(); };

time関数を使うのでtime.hが必要です。time関数についてはC言語編 第13章を参照してください。

BanクラスのメンバにbComp, bCompSenteが増えました。また、 initialsetting, comp_putメンバ関数が増えました。

Ban::Ban() { int i, j; for (i = 0; i < MASU; i++) { for (j = 0; j < MASU; j++) { ban[i][j] = 0; } } hOut = GetStdHandle(STD_OUTPUT_HANDLE); bSente = TRUE; nTe = 1; initialsetting(); bandraw(); }

コンストラクタですが、initialsetting関数の呼び出しが増えています。 この関数で、コンピュータと対戦するか、その場合先手はどうするかを決めます。

//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[8], szLocation[8], cMoji, szTxt[8], szYesNo[4]; int row, col; int jud; while (1) { if (bSente) strcpy(szSashite, "先手"); else strcpy(szSashite, "後手"); 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; 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: strcpy(szHantei, "先手の勝ち!"); tekazu = nTe - 1; break; case 2: strcpy(szHantei, "後手の勝ち!"); 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(); return 0; }

人間が石を置く場合はgets関数でその位置を入力させます。 人間が置く場合とは、

1.コンピュータと対戦しない場合

2.コンピュータと対戦する場合でコンピュータが先手で、現在後手の差し順の時

3.コンピュータと対戦する場合でコンピュータが後手で、現在先手が差し順の時

の3つの場合です。これ以外の時はコンピュータが石を置きます(comp_put関数を呼び出す)。

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; bandraw(); bSente = !bSente; Locate(0, MASU + 4); printf("再現中.....第%02d手目", i + 1); _getch(); } Locate(0, MASU + 6); return 0; }

(0, MASU + 2)のカーソル位置にスペースを出力して「位置を指定してください(先手/後手)」の表示を 消すことにしました。再現中はこの文字列は目障りなので消しただけです。

int Ban::initialsetting() { char yesno[4]; 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; }

コンピュータと対戦するかどうか、その場合先手はどうするかを決める関数です。 特に難しいところはないですね。

int Ban::comp_put(char *loc) { char a1, a2[4]; int row, col; 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; }

コンピュータに石を置かせる関数です。

と、言っても単にあいているところにランダムに石を置くのみです。

まず、石を置く位置を乱数を発生させることにより決めます。 この位置にすでに石が置かれているかどうかは、checkLocation関数で検査します。 石が置けないときは、再度乱数を発生させます。石が置ける場合はlocにその位置を 書き込みます。文字列操作の関数に注意してください。

(row, col)が(0, 0)の時はlocには「A1」と書き込まれます。まず、locの先頭配列には row + 'A'が書き込まれます。この意味がわからないときはC言語編を復習してみてください。

_itoa関数は

char *_itoa( int value, char *string, int radix );

のように定義されています。

整数値を文字列に変換します。

valueには、変換する数値を指定します。

stringに結果の文字列が入ります。

radixには、何進数かを指定します。10進なら10を指定します。

char *strcat( char *string1, const char *string2 );

string1にstring2を追加します。戻り値はstring1を返します。

int main() { Ban MyBan; MyBan.putStone(); return 0; }

main関数に変更はありません。

文字列操作の関数は山ほどあります。ヘルプ等で調べて整理しておいてください。


[C++Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Aug/20/2001 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。