Transcript PPT

ゲームの作成
マインスイーパの概要
マインスイーパの準備
マインスイーパの完成
マインスイーパの改良
マインスイーパの概要

ゲームの目的


ゲームの勝敗・スコア




地雷が隠れているマス目を開けずに、(できるだけ早
く)すべての地雷を見つけること
地雷を掘り出したら負け
地雷以外を全部開けたら勝ち
(所要時間は短いほうが良い)
ゲームのやり方


マス目の座標を入力してマス目を開く
表示される数字は隣接する周囲のマス目に隠れてい
る地雷の数を示す
マイン・スイーパの準備



m[5]: 5つの地雷の位置
map[5][5]: 地雷マップ用配列
status[5][5]: 隣接地雷数情報(ゲームボードの状況)
status[][]
map[][]
Mine m[]
m[0]={4,1}
m[1]={1,0}
m[2]={3,4}
m[3]={0,4}
m[4]={0,2}
1
2
3
4
5
A
0
0
1
0
1
B
1
0
0
0
0
C
0
0
0
0
0
D
0
0
0
0
1
E
0
1
0
0
0
A B C D E
1 -1-1-1-1-1
2 -1-1-1-1-1
3 -1-1-1-1-1
4 -1-1-1-1-1
5 -1-1-1-1-1
マイン・スイーパの準備(mines0.c)
#include
#include
#include
#include
<stdio.h>
<stdlib.h>
<string.h>
<time.h>
#define TRUE 1
#define BSIZE 5
// ゲームボードのサイズ
#define MINENUM 5 // 地雷の数
// 地雷の位置座標の構造体
typedef struct {
int x, y;
} Mine;
整数値の座標で表現
Mine 型としてユーザー定義
マイン・スイーパの準備(mines0.c)
static にしておくと初期化される
// ゲームボード用データ構造
int status[BSIZE][BSIZE];
// 状況マップ(ゲームボード)
static int map[BSIZE][BSIZE]; // 地雷マップ
Mine m[MINENUM];
// 地雷の位置座標の配列
// 地雷を追加する関数
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)を発生
すでに配置されている地雷と
重複しないようチェック
地雷マップに登録
マイン・スイーパの準備(mines0.c)
// ゲームボードの初期化関数
void Board_init(void)
{
int x, y, i;
// 状況マップの初期化
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]));
}
地雷の数だけ呼び出す
ポインタを渡す
マイン・スイーパの準備(mines0.c)
// ゲームボードを画面に表示する関数
void Board_show(int status[BSIZE][BSIZE])
{
int i, x, y;
ボードの x 座標 A~E の表示
printf(" ");
for (i = 0; i < BSIZE; i++) printf("%c", 'A' + i);
printf("\n");
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]);
}
printf("\n");
}
}
y 座標の数字表示
未探査なら # を表示
探査済みなら
隣接地雷数を表示
マイン・スイーパの準備(mines0.c)
int main(void)
{
srand(time(NULL) % 100);
Board_init();
Board_show(map);
Board_show(status);
return 0;
}
// 乱数の初期化
ゲームボードの初期化
地雷マップの表示(確認用)
ゲームボードの表示
前ページの main の代わりに以下のプログラムを差し替え
マイン・スイーパ(mineswpr.c)
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;
distY = p??y - cy;
if (-1 <= distX &&
-1 <= distY &&
return MISS;
}
// 地雷とのx方向の距離
// 地雷とのy方向の距離
distX <= 1 &&
distY <= 1) return NEAR;
地雷の位置と一致
地雷に隣接
周囲に地雷なし
マイン・スイーパ(mineswpr.c)
// 指定された座標を開く関数
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()
Board_show(status)
ゲームボードの表示
座標入力
X座標チェック
Y座標チェック
メイン
TRUE
bs=Board_open(x,y)
無限ループ
判定
入力座標を開く
"地雷を踏んでしまいました!"
bs==BOMB?
bs==ALREADY?
(bs==OPEN?)
地雷座標の出力
break
continue
openCount++
残り==地雷数?
"クリアしました!"
Board_show(status)
break
マイン・スイーパ(mineswpr.c)
int main(void)
{
int i, x, y, openCount = 0;
char charX, charY, key[100];
enum BSTAT bs;
srand(time(NULL) % 100); // 乱数シードセット
Board_init();
// ゲームボードの初期化
// メインループ
while (TRUE) {
Board_show(status);
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';
マイン・スイーパ(mineswpr.c)
// 入力された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");
Board_show(status);
break;
}
}
}
マイン・スイーパ(mineswpr.c)
// 地雷座標の出力
printf("\n");
for (i=0; i<MINENUM; i++)
printf(" %c%d", 'A'+m[i]?x, 1+m[i]?y);
return 0;
}
スキルアップタイム1



マイン・スイーパの準備(mines0.c)をコンパイル
し、地雷の位置が毎回異なることを確認せよ
srand(…)の行をコメントアウトすると、どうなる
か、確認せよ
ゲームボードのサイズや地雷の数を変えてみよ
スキルアップタイム2




マイン・スイーパ(mineswpr.c)を完成させ、動作を
確認せよ
地雷にヒットしたときの連続ビープ音を爆発音
(EXPLODE.WAV)に変更せよ
クリアしたときのビープ音をTa-dah!(ジャジャー
ン)に変更せよ
(C:\WINDOWS\Media\TADA.WAV)
開始からクリアまでの時間を計測し、表示せよ
スキルアップタイム2のヒント
WAVファイルの再生
/* WAVファイルの再生 */
#include <windows.h>
#pragma comment(lib,"winmm") // winmm.lib をリンクする
int main(void)
{
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.");
return 0;
}
P:\2010年度後期\プログラミング入門2 フォルダに EXPLODE.WAV がある。
C:\WINDOWS\Media フォルダに TADA.WAV がある。
スキルアップタイム2のヒント
時間計測:秒単位(time1.c)
#include <stdio.h>
#include <stdlib.h>
時間関数用ヘッダ
#include <time.h>
int main(void)
{
int i, N=100000;
変数宣言
int t1, t2;
計測対象
計測開始
t1 = time(NULL);
for (i=0; i<N; i++) printf(" %d", rand()%6 + 1);
printf("\n");
t2 = time(NULL);
計測終了
printf("%d sec\n", t2-t1);
return 0;
実行時間(秒数)
}
本日のパズル
次のプログラムは何を出力するか?
#include <stdio.h>
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,+,/n{n+\
,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/\
+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){n\
l]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#\
n'wk nw' iwk{KK{nl]!/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);}