第68章 コンピュータと対戦する


今回は、今までのプログラムを改造して、コンピュータと対戦できるようにします。 と、言っても特別なアルゴリズムを使うのではなく、コンピュータは単にあいている 所にランダムに石を置くだけです。コンピュータ(プログラム)は、何も考えていないので、ほとんど人間が勝ちます(^^;)。

(また、このゲーム自体ちょっと変です。ルールを変えなくてはゲームとして成り立たない と思われますが、各自で工夫してみてください。)



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

// game05.c #include <windows.h> #include <stdio.h> #include <conio.h> #include <time.h> #define MASU 6 //MASUの数値を変えると盤面の大きさが変わります int banshow(HANDLE); int Locate(HANDLE, int, int); int check_location(int, int); //すでに駒がおかれていないかどうか調べる int judge(); int stonerecord(int, int); int replay(HANDLE, int); int comp_put(char *); //コンピュータが駒を置きます int ban[MASU][MASU]; int comp; //対戦相手がコンピュータの時1, その他0 int compsente; //コンピュータが先手1, 後手0 struct _tagRec { int row; int col; } Rec[MASU * MASU];

time関数を使うのでtime.hをインクルードしています。

コンピュータに石を置かせるcomp_put関数や、comp, compsenteのグローバル 変数を用意しました。

int main() { HANDLE hOut; char cMoji, szTxt[8]; BOOL bOrd = TRUE; //FALSE:後手 TRUE:先手 int i, row =0, col=0, jud, tekazu; //何手で勝負がついたか; char location[8]; //位置指定 A2, B1,など char *sente = "先手", *gote = "後手"; char order[8], hantei[16], yesno[8]; hOut = GetStdHandle(STD_OUTPUT_HANDLE); printf("コンピュータと対戦しますか(y/n)"); gets(yesno); if (strcmp(yesno, "Y") == 0 || strcmp(yesno, "y") == 0) { comp = 1; printf("あなたが先手になりますか(y/n)"); gets(yesno); if (strcmp(yesno, "Y") == 0 || strcmp(yesno, "y") == 0) { compsente = 0; } else { compsente = 1; } } else { comp = 0; } Locate(hOut, 0, 0); printf(" "); Locate(hOut, 0, 1); printf(" "); banshow(hOut); for (i = 0; i < MASU * MASU; i++) { if (bOrd) strcpy(order, sente); else strcpy(order, gote); INP: Locate(hOut, 28, MASU + 2); printf(" "); Locate(hOut, 0, MASU + 2); printf("位置を指定してください(%s)", order); if (comp == 0 || (comp == 1 && compsente == 1 && !bOrd) || (comp == 1 && compsente == 0 && bOrd)) { gets(location); } else { comp_put(location); } if (location[0] == '0') return -1; if (isalpha(location[0]) == 0) { Locate(hOut, 0, MASU + 3); printf("指定が違います"); goto INP; } if (isupper(location[0]) == 0) cMoji = toupper(location[0]); else cMoji = location[0]; row = cMoji - 'A'; if (row < 0 || row > MASU - 1) { Locate(hOut, 0, MASU + 3); printf("行の指定が違います"); goto INP; } if (isdigit(location[1]) != 0 && isdigit(location[2]) != 0 && location[3] == '\0') { szTxt[0] = location[1]; szTxt[1] = location[2]; szTxt[2] = '\0'; col = atoi(szTxt) - 1; } else if (isdigit(location[1]) != 0 && location[2] == '\0') { szTxt[0] = location[1]; szTxt[1] = '\0'; col = atoi(szTxt) - 1; } else { Locate(hOut, 0, MASU + 3); printf("列の指定が違います"); goto INP; } if (col < 0 || col > MASU - 1) { Locate(hOut, 0, MASU + 3); printf("列の位置指定が違います"); goto INP; } if (check_location(row, col) != 0) { Locate(hOut, 0, MASU + 3); printf("そこには置けません!"); goto INP; } if (bOrd) ban[row][col] = 1; else ban[row][col] = 2; stonerecord(row, col); bOrd = !bOrd; banshow(hOut); Locate(hOut, 0, MASU + 3); printf(" "); jud = judge(); switch (jud) { case 0: strcpy(hantei, ""); break; case 1: strcpy(hantei, "先手の勝ち"); tekazu = i +1; break; case 2: strcpy(hantei, "後手の勝ち"); tekazu = i + 1; break; default: strcpy(hantei, "内部エラー"); break; } Locate(hOut, 0, MASU + 4); printf(hantei); if (jud != 0) break; } if (jud == 0) { printf("引き分けです"); tekazu = MASU * MASU; } printf("\n\n"); printf("再現しますか(Y/N)-->"); gets(yesno); if (strcmp(yesno, "y") == 0 || strcmp(yesno, "Y") == 0) { replay(hOut, tekazu); } Locate(hOut, 0, MASU + 6); return 0; }

まず、最初にコンピュータと対戦するかどうかを尋ねます。対戦する場合は compに1が入ります。そうでない場合は0です。

次に、自分が先手になるかどうかを尋ねます。コンピュータが先手の場合は comp_senteに1が入ります。そうでない場合は0となります。

さて、キーボードから石の位置を入力しなくてはいけないのは

1.コンピュータと対戦しない(compが0) 2.コンピュータが先手(compsenteが1)で石を置く順が後手(bOrdがFALSE)の場合 3.コンピュータが後手(compsenteが0)で石を置く順が先手(bOrdがTRUE)の場合

のいずれかの場合です。この時は今まで通りキーボードから入力させ、 その他の場合はcomp_put関数を呼んで、コンピュータに石を置かせます。

int banshow(HANDLE hStdOut) { int i, j; Locate(hStdOut, 0, 0); printf(" "); for (i = 0; i < MASU; i++) { printf("[%2d]", i + 1); } printf("\n"); for (i = 0; i < MASU; i++) { printf("[%c]\n", i + 'A'); } for (i = 0; i < MASU; i++) { for (j = 0; j < MASU; j++) { if (ban[i][j] == 1) { Locate(hStdOut, j * 4 + 4, i + 1); printf("○"); } if (ban[i][j] == 2) { Locate(hStdOut, j * 4 + 4, i + 1); printf("×"); } } } return 0; } int Locate(HANDLE hOut, 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 check_location(int row, int col) { if (ban[row][col] == 0) return 0; else return -1; } int 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 stonerecord(int row, int col) { static int nTe = 1; Rec[nTe - 1].row = row; Rec[nTe - 1].col = col; nTe++; return 0; } int replay(HANDLE hOut, int tekazu) { int i, j, stone; BOOL bSente = TRUE; //盤面初期化 Locate(hOut, 0, MASU + 4); printf(" "); for (i = 0; i < MASU; i++) { for (j = 0; j < MASU; j++) { Locate(hOut, 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; banshow(hOut); bSente = !bSente; Locate(hOut, 0, MASU + 4); printf("再現中.....第%02d手目", i + 1); _getch(); } return 0; }

これらの関数に変更はありません。

int comp_put(char *loc) { char a1, a2[4]; int row, col; srand((unsigned)time(NULL)); while (1) { row = rand() % MASU; col = rand() % MASU; if (check_location(row, col) == 0) break; } a1 = (char)row + 'A'; _itoa(col + 1, a2, 10); loc[0] = a1; loc[1] = '\0'; strcat(loc, a2); return 0; }

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

石の置ける範囲はMASUが6の場合A1-F6までです。

rand() % 6

とするとこの値は0から5までの整数となります。
従って、rowやcolの値を

rand() % MASU

で表すことができます。しかし、この範囲でもすでに石が 置かれている可能性があるのでcheck_location関数を 呼んで大丈夫かどうか調べます。もし、だめなら再度乱数を 発生させます。この関数内でチェックしなくてもmain関数でチェックしているので 一応大丈夫なのですが、そうすると何回もこの関数が呼ばれて、表示がちらつくことがあります。

rowやcolの値が決まったら、文字列にします。 この関数の引数locは、main関数のlocationのアドレスなので これに、文字列をコピーしてやればmain関数に反映されます。

さて、今回のプログラムを改良して、すぐにはコンピュータが負けないようにしてみて ください。


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

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