左の図のようなメニューが出ます。ここから「3.終了」を
選択するまで、このプログラムは動き続けます。
では、どこをどのように修正すればよいのでしょうか。
ここでは、今まで作った部分をなるべく修正しないように試みてみます。
従って、ちょっとみっともないプログラムとなります。
では、プログラムを見てみましょう。
メニューを表示するためのmenu関数、記録を読み出すためのread_record関数、画面消去のための cls関数が増えました。// game10.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(HANDLE, char*); //コンピュータが駒を置きます int IsStoneGet(HANDLE, int, int, BOOL); int search_pos(HANDLE, char *); //相手の駒が取れる位置を探します int set_loc(HANDLE, int, int, char *); int is_getcorner(char *); //4隅に駒が置けるかどうかを調べます int search_win_pos(HANDLE, char*, BOOL); //自分・相手があと1手で勝てるかどうか調べます int record_to_file(HANDLE, int); //ファイルに記録します int menu(HANDLE); //メニューを表示します int read_record(HANDLE); //記録を読み出します int cls(HANDLE); //画面をスペースで埋めます int ban[MASU][MASU]; int comp; //対戦相手がコンピュータの時1, その他0 int compsente; //コンピュータが先手1, 後手0 struct _tagRec { int row; int col; } Rec[MASU * MASU];
main関数内の大部分をwhileで無限ループにしました。これを抜けるには menu関数の返す値が0となる時です。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[32] = "先手", gote[32] = "後手"; static char order[8]; char hantei[32], yesno[8]; int menu_no; //メニュー関数が返す値 hOut = GetStdHandle(STD_OUTPUT_HANDLE); while (1) { memset(ban, 0, sizeof(ban)); menu_no = menu(hOut); if (menu_no == 0) return 0; if (menu_no == -1) continue; Locate(hOut, 0, 0); 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; strcpy(sente, "あなた=○"); strcpy(gote, "コンピュータ=×"); } else { compsente = 1; strcpy(sente, "コンピュータ=○"); strcpy(gote, "あなた=×"); } } 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(hOut, 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; IsStoneGet(hOut, row, col, bOrd); stonerecord(row, col); bOrd = !bOrd; banshow(hOut); Locate(hOut, 0, MASU + 3); printf(" "); jud = judge(); switch (jud) { case 0: strcpy(hantei, ""); break; case 1: if (comp == 0) strcpy(hantei, "先手の勝ち"); else if (compsente == 1) strcpy(hantei, "コンピュータの勝ち"); else strcpy(hantei, "あなたの勝ち"); tekazu = i +1; break; case 2: if (comp == 0) strcpy(hantei, "後手の勝ち"); else if (compsente == 1) strcpy(hantei, "あなたの勝ち"); else 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) { record_to_file(hOut, tekazu); } printf("再現しますか(Y/N)-->"); gets(yesno); if (strcmp(yesno, "y") == 0 || strcmp(yesno, "Y") == 0) { replay(hOut, tekazu); } } Locate(hOut, 0, MASU + 8); return 0; }
あとは、変更していません。
この部分に特に変更はありません。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(" "); Locate(hOut, 0, MASU + 2); 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; IsStoneGet(hOut, Rec[i].row, Rec[i].col, bSente); banshow(hOut); bSente = !bSente; Locate(hOut, 0, MASU + 4); printf("再現中.....第%02d手目", i + 1); _getch(); } return 0; } int comp_put(HANDLE hOut, char *loc) { char a1, a2[4]; int row, col; //あと1手でコンピュータが勝つかどうか調べ勝ちます if (search_win_pos(hOut, loc, TRUE) == 0) return 0; //あと1手で人間が勝つかどうか調べ妨害します if (search_win_pos(hOut, loc, FALSE) == 0) return 0; if (search_pos(hOut, loc) == 0) return 0; if (is_getcorner(loc) == 0) return 0; 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; } int IsStoneGet(HANDLE hOut, int row, int col, BOOL bOrd) { int jibun, aite; if (bOrd) { jibun = 1; aite = 2; } else { jibun = 2; aite = 1; } if (row - 2 >= 0 && col - 2 >=0 && ban[row - 2][col - 2] == jibun && ban[row - 1][col - 1] == aite) { ban[row - 1][col - 1] = jibun; } if (row - 2 >= 0 && ban[row - 2][col] == jibun && ban[row - 1][col] == aite) { ban[row - 1][col] = jibun; } if (row - 2 >= 0 && col + 2 < MASU && ban[row - 2][col + 2] == jibun && ban[row - 1][col + 1] == aite) { ban[row - 1][col + 1] = jibun; } if (col - 2 >= 0 && ban[row][col - 2] == jibun && ban[row][col - 1] == aite) { ban[row][col - 1] = jibun; } if (col + 2 < MASU && ban[row][col + 2] == jibun && ban[row][col + 1] == aite) { ban[row][col + 1] = jibun; } if (row + 2 < MASU && col - 2 >= 0 && ban[row + 2][col - 2] == jibun && ban[row + 1][col - 1] == aite) { ban[row + 1][col - 1] = jibun; } if (row + 2 < MASU && ban[row + 2][col] == jibun && ban[row + 1][col] == aite) { ban[row + 1][col] = jibun; } if (row + 2 < MASU && col + 2 < MASU && ban[row + 2][col + 2] == jibun && ban[row + 1][col + 1] == aite) { ban[row + 1][col + 1] = jibun; } return 0; } //相手の駒が取れる位置を探します int search_pos(HANDLE hOut, char *loc) { int i, j, jibun, aite; if (compsente == 1) { jibun = 1; aite = 2; } else { jibun = 2; aite = 1; } for (i = 0; i < MASU - 2; i++) { for (j = 0; j < MASU; j++) { if (ban[i][j] == 0 && ban[i + 1][j] == aite && ban[i + 2][j] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 2; i < MASU; i++) { for (j = 0; j < MASU; j++) { if (ban[i][j] == 0 && ban[i - 1][j] == aite && ban[i - 2][j] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 0; i < MASU; i++) { for (j = 0; j < MASU - 2; j++) { if (ban[i][j] == 0 && ban[i][j + 1] == aite && ban[i][j + 2] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 0; i < MASU; i++) { for (j = 2; j < MASU; j++) { if (ban[i][j] == 0 && ban[i][j - 1] == aite && ban[i][j - 2] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 0; i < MASU - 2; i++) { for (j = 0; j < MASU - 2; j++) { if (ban[i][j] == 0 && ban[i + 1][j + 1] == aite && ban[i + 2][j + 2] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 2; i < MASU; i++) { for (j = 2; j < MASU; j++) { if (ban[i][j] == 0 && ban[i - 1][j - 1] == aite && ban[i - 2][j - 2] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 2; i < MASU; i++) { for (j = 0; j < MASU - 2; j++) { if (ban[i][j] == 0 && ban[i - 1][j + 1] == aite && ban[i - 2][j + 2] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } for (i = 0; i < MASU - 2; i++) { for (j = 2; j < MASU; j++) { if (ban[i][j] == 0 && ban[i + 1][j - 1] == aite && ban[i + 2][j -2] == jibun) { set_loc(hOut, i, j, loc); return 0; } } } return -1; } int set_loc(HANDLE hOut, int row, int col, char *loc) { char szBuf[8], szTxt[256]; loc[0] = 'A' + row; loc[1] = '\0'; _itoa(col + 1, szBuf, 10); strcat(loc, szBuf); sprintf(szTxt, "コンピュータは(%s)に駒を置いてあなたの石を取ります。何かキーを打ってください。", loc); MessageBeep(MB_OK); Locate(hOut, 0, MASU + 5); printf(szTxt); _getch(); Locate(hOut, 0, MASU + 5); printf(" "); return 0; } //4隅に駒が置けるかどうかを調べます int is_getcorner(char *loc) { if (ban[0][0] == 0) { strcpy(loc, "A1"); return 0; } if (ban[0][MASU - 1] == 0) { sprintf(loc, "A%d", MASU); return 0; } if (ban[MASU - 1][0] == 0) { sprintf(loc, "%c1", MASU + 'A' - 1); return 0; } if (ban[MASU - 1][MASU - 1] == 0) { sprintf(loc, "%c%d", MASU + 'A' - 1, MASU); return 0; } return -1; } //自分(相手)が勝てるかどうか調べます int search_win_pos(HANDLE hOut, char *loc, BOOL bCompWin) { int jibun, aite, i, j, nWinner; char szBuf[8]; if (compsente == 1) { jibun = 1; aite = 2; } else { jibun = 2; aite = 1; } if (bCompWin) nWinner = jibun; else nWinner = aite; for (i = 0; i < MASU; i++) { for (j = 0; j < MASU; j++) { if (ban[i][j] == 0) { if (bCompWin) ban[i][j] = jibun; else ban[i][j] = aite; if (judge() == nWinner) { loc[0] = 'A' + i; loc[1] = '\0'; _itoa(j + 1, szBuf, 10); strcat(loc, szBuf); ban[i][j] = 0; MessageBeep(MB_OK); Locate(hOut, 0, MASU + 6); if (bCompWin) printf("(%s)でコンピュータが勝ちます!!", loc); else printf("(%s)で人間が勝ちます!!", loc); _getch(); Locate(hOut, 0, MASU + 6); printf(" "); return 0; }else { ban[i][j] = 0; } } } } return -1; }
ファイルに書き込む関数ですが、ちょっと変更しました。 ファイルの最初にマス目の数を書き込むことにしました。 と、いうのもマス目の違うファイルを読み込むのを防がなくてはいけないからです。int record_to_file(HANDLE hOut, int tekazu) { char fname[64]; FILE *fp; int i; start: Locate(hOut, 0, MASU + 6); printf(" "); Locate(hOut, 0, MASU + 7); printf(" "); Locate(hOut, 0, MASU + 6); printf("ファイル名を指定してください-->"); gets(fname); //すでに存在するファイルかどうかを調べます fp = fopen(fname, "r"); if (fp) { //fpがNULLでなければ、そのファイルは存在します Locate(hOut, 0, MASU + 7); printf("そのファイルはすでに存在します。\n"); fclose(fp); _getch(); goto start; } fp = fopen(fname, "w"); if (fp == NULL) { printf("ファイルのオープンに失敗しました"); return -1; } fprintf(fp, "%d\n", MASU); for (i = 0; i < tekazu; i++) { fprintf(fp, "%d, %d\n", Rec[i].row, Rec[i].col); } fclose(fp); return 0; }
メニュー表示のため関数です。int menu(HANDLE hOut) { char ans[8]; cls(hOut); Locate(hOut, 0, 0); printf("****** MENU *****\n"); printf(" 1.対戦\n"); printf(" 2.記録の読込\n"); printf(" 3.終了\n"); printf("*****************\n"); printf("選択-->"); gets(ans); if (strcmp(ans, "1") == 0) { cls(hOut); return 1; } else if (strcmp(ans, "2") == 0) { return read_record(hOut); } else if (strcmp(ans, "3") == 0) { return 0; } else { printf("\n番号が違います\n"); getch(); return -1; } return 0; }
最初にcls関数を呼んで画面消去をしておきます。
main関数の所を見ればわかりますが、この関数が0を返すと、プログラムが終了します。 -1を返すとmain関数内のループの最初からやり直しとなります。番号違いなどの時は ループの最初からやり直します。
メニューから「2」を選択したときはread_record関数の戻り値がそのままmain関数に戻されます。 read_record関数(後述)は、どんな場合も-1を返すようにしてあります。
ファイルから読み込む関数です。int read_record(HANDLE hOut) { char fname[64]; int i = 0, iseof, masu; FILE *fp; cls(hOut); Locate(hOut, 0, 0); printf("ファイル名==>"); gets(fname); fp = fopen(fname, "r"); if (fp == NULL) { printf("\nファイルがオープンできません\n"); getch(); return -1; } fscanf(fp, "%d", &masu); if (masu != MASU) { printf("マスの数が違うので再現できません\n"); fclose(fp); getch(); return -1; } while(1) { iseof = fscanf(fp, "%d, %d", &Rec[i].row, &Rec[i].col); i++; if (iseof == EOF) break; } fclose(fp); replay(hOut, i - 1); return -1; }
マス目が違うとき、ファイルのオープンに失敗したときは直ちに-1を返します。
成功したら、Rec構造体配列にファイルの中身を書き込んでいきます。 そして、replay関数を呼べば対戦が再現されます。手数はi-1となることに注意してください。 replay関数が終了したら、やはりこの関数は-1を返します。
たとえば重大なエラーが発生したときなどは0を返して、このプログラム自体を終了させる ようにしておく必要があります。
画面消去の関数です。コンソール・アプリケーション以外のプログラムを作っている場合は その環境にあった関数を作ってください。昔の(MSDOSの時代)MSCには、_clearscreen関数というのが グラフィックスのライブラリの中にありました。int cls(HANDLE hOut) { COORD co; DWORD dwWritten, dwFillSize; CONSOLE_SCREEN_BUFFER_INFO ci; co.X = 0; co.Y = 0; GetConsoleScreenBufferInfo(hOut, &ci); dwFillSize = ci.dwSize.X * ci.dwSize.Y; FillConsoleOutputCharacter(hOut, ' ', dwFillSize, co, &dwWritten); return 0; }
コンソール・アプリケーションでの画面消去については第61章で解説してますので参考にしてみてください。
Update Dec/22/2001 By Y.Kumei