ゲームの作成 マインスイーパの概要マインスイーパの準備マインスイーパの完成マインスイーパの改良 マインスイーパの概要 ゲームの目的 地雷が隠れているマス目を開けずに ( できるだけ早く ) すべての地雷を見つけること ゲームの勝敗 スコア 地雷を掘り出したら負け 地雷以外を全部開けたら勝ち ( 所要時間は短いほうが良い ) ゲームのやり方 マス目の座標を入力してマス目を開く 表示される数字は隣接する周囲のマス目に隠れている地雷の数を示す マイン スイーパの準備 m[5]: 5 つの地雷の位置 map[5][5]: 地雷マップ用配列 status[5][5]: 隣接地雷数情報 ( ゲームボードの状況 ) Mine m[] m[0]=4,1 m[1]=1,0 m[2]=3,4 m[3]=0,4 m[4]=0,2 map[][] status[][] A B C D E A B C D E 1 0 1 0 0 0 1-1-1-1-1-1 2 0 0 0 0 1 2-1-1-1-1-1 3 1 0 0 0 0 3-1-1-1-1-1 4 0 0 0 0 0 4-1-1-1-1-1 5 1 0 0 1 0 5-1-1-1-1-1 #include <stdlib.h> #include <string.h> #include <time.h> #define TRUE 1 #define BSIZE 5 // ゲームボードのサイズ #define MINENUM 5 // 地雷の数 // 地雷の位置座標の構造体 typedef struct int x, y; Mine; 整数値の座標で表現 Mine 型としてユーザー定義
static にしておくと初期化される // ゲームボード用データ構造 int status[bsize][bsize]; // 状況マップ ( ゲームボード ) static int map[bsize][bsize]; // 地雷マップ Mine m[minenum]; // 地雷の位置座標の配列 // ゲームボードの初期化関数 void Board_init(void) int x, y, i; // 地雷を追加する関数 void Mine_set(Mine *p) do p??x = rand() % BSIZE; p??y = rand() % BSIZE; while (map[p??y][p??x]); map[p??y][p??x] = TRUE; 乱数で 0~(BSIZE 1) を発生 すでに配置されている地雷と重複しないようチェック 地雷マップに登録 // 状況マップの初期化 for (y = 0; y < BSIZE; y++) for (x = 0; x < BSIZE; x++) status[y][x] = -1; 未探査状態は 1 // 地雷のセット for (i = 0; i < MINENUM; i++) Mine_set(&(m[i])); 地雷の数だけ呼び出す ポインタを渡す // ゲームボードを画面に表示する関数 void Board_show(int status[bsize][bsize]) int i, x, y; printf(" "); for (i = 0; i < BSIZE; i++) printf("%c", 'A' + i); for (y = 0; y < BSIZE; y++) printf("%d ", y + 1); for (x = 0; x < BSIZE; x++) if (status[y][x] < 0) printf("#"); else printf("%1d", status[y][x]); ボードの x 座標 A~E の表示 y 座標の数字表示 未探査なら # を表示 探査済みなら隣接地雷数を表示 srand(time(null) % 100); Board_init(); Board_show(map); // 乱数の初期化ゲームボードの初期化地雷マップの表示 ( 確認用 ) ゲームボードの表示
enum MSTAT MISS, NEAR, HIT ; // 地雷チェック結果 enum BSTAT ALREADY, OPEN, BOMB ; // ボードオープン結果 // 地雷に近いかどうかをチェックする関数 enum MSTAT Mine_check(int cx, int cy, Mine *p) int distx, disty; if (p??x == cx && p??y == cy) return HIT; distx = p??x - cx; // 地雷との x 方向の距離 disty = p??y - cy; // 地雷との y 方向の距離 if (-1 <= distx && distx <= 1 && -1 <= disty && disty <= 1) return NEAR; return MISS; 前ページの main の代わりに以下のプログラムを差し替え 地雷の位置と一致 地雷に隣接 周囲に地雷なし // 指定された座標を開く関数 enum BSTAT Board_open(int x, int y) int i, nearcount = 0; enum MSTAT ms; if (status[y][x] >= 0) return ALREADY; すでに開いた場所 for (i = 0; i < MINENUM; i++) ms = Mine_check(x, y, &m[i]); // 地雷チェック地雷 if (ms == HIT) return BOMB; if (ms == NEAR) nearcount++; // 隣接地雷のカウント status[y][x] = nearcount; 地雷なし return OPEN; PAD メイン 乱数シードセット Board_init() TRUE 無限ループ 判定 地雷座標の出力 Board_show(status) 座標入力 X 座標チェック Y 座標チェック bs=board_open(x,y) bs==bomb? bs==already? (bs==open?) " 地雷を踏んでしまいました!" break continue opencount++ 残り == 地雷数? ゲームボードの初期化 ゲームボードの表示 入力座標を開く " クリアしました!" Board_show(status) break int i, x, y, opencount = 0; char charx, chary, key[100]; enum BSTAT bs; srand(time(null) % 100); // 乱数シードセット Board_init(); // ゲームボードの初期化 // メインループ while (TRUE) printf(" 座標を入力してください ( 例 A1): "); fgets(key, 100, stdin); if (strlen(key) < 2) continue; // 入力された X 座標をチェックする charx = key[0]; if (charx < 'A' charx > 'A' + BSIZE-1) continue; x = charx - 'A';
// 入力された Y 座標をチェックする chary = key[1]; if (chary < '1' chary > '1' + BSIZE-1) continue; y = chary - '1'; // オープン 判定 bs = Board_open(x, y); if (bs == BOMB) printf(" a a a a 地雷を踏んでしまいました! n"); break; else if (bs == ALREADY) continue; else opencount++; if (BSIZE*BSIZE - opencount == MINENUM) printf(" a クリアしました! n"); break; // 地雷座標の出力 for (i=0; i<minenum; i++) printf(" %c%d", 'A'+m[i]?x, 1+m[i]?y); スキルアップタイム 1 をコンパイルし 地雷の位置が毎回異なることを確認せよ srand( ) の行をコメントアウトすると どうなるか 確認せよ ゲームボードのサイズや地雷の数を変えてみよ スキルアップタイム 2 を完成させ 動作を確認せよ 地雷にヒットしたときの連続ビープ音を爆発音 (EXPLODE.WAV) に変更せよ クリアしたときのビープ音をTa-dah!( ジャジャーン ) に変更せよ (C: WINDOWS Media TADA.WAV) 開始からクリアまでの時間を計測し 表示せよ
スキルアップタイム 2 のヒント WAV ファイルの再生 /* WAV ファイルの再生 */ #include <windows.h> #pragma comment(lib,"winmm") // winmm.lib をリンクする if (!PlaySound("EXPLODE.WAV", 0, SND_FILENAME)) printf("the sound didn't play."); if (!PlaySound("TADA.WAV", 0, SND_FILENAME)) printf("the sound didn't play."); P: 2010 年度後期 プログラミング入門 2 フォルダに EXPLODE.WAV がある C: WINDOWS Media フォルダに TADA.WAV がある スキルアップタイム 2 のヒント 時間計測 : 秒単位 (time1.c) #include <stdlib.h> #include <time.h> 時間関数用ヘッダ int i, N=100000; 変数宣言 int t1, t2; t1 = time(null); 計測開始 for (i=0; i<n; i++) printf(" %d", rand()%6 + 1); t2 = time(null); 計測終了 printf("%d sec n", t2-t1); 実行時間 ( 秒数 ) 計測対象 本日のパズル次のプログラムは何を出力するか? main(t,_,a) phillipps.c char *a; return!0<t?t<3?main(-79,-13,a+main(-87,1-_, main(-86, 0, a+1 )+a)):1,t<_?main(t+1, _, a ):3,main ( -94, -27+t, a )&&t == 2?_<13?main ( 2, _+1, "%s %d %d n" ):9:16:t<0?t<-72?main(_, t,"@n'+,#'/*w+/w#cdnr/+,r/*de+,/**+,/w%+,/w#q#n+,/#l,+,/nn+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,w+k w'k:'+e#';dq#'l q#'+d'k#!/ +k#;q#'rekk#w'rekknl]'/#;#q#n'))#w'))nl]'/+#n';drw' i;# )n l]!/nn#'; r#w'r ncnl]'/#l,+'k rw' ik;[nl]'/w#q# n'wk nw' iwkkknl]!/w%'l##w#' i; :nl]'/*q#'ld;r'nlwb!/*de'c ;;nl'-rw]'/+,##'*#nc,',#nw]'/+kd'+e+; #'rdq#w! nr'/ ') +rl#'n' ')# '+##(!!/") :t<-50?_==*a?putchar(a[31]):main(-65,_,a+1):main((*a == '/')+t,_,a +1 ):0<t?main ( 2, 2, "%s"):*a=='/' main(0,main(-61,*a, "!ek;dc i@bk'(q)-[w]*%n+r3#l,: nuwloca-o;m.vpbks,fxntdceghiry"),a+1);