第 16 章 構造体 ■ ゲーム作成

31
16 16 第第第第第第第第第 第第第第第 16.3 第第第第第第第第第第 第第第第第 第第第 system 第第

Upload: syshe

Post on 28-Jan-2016

72 views

Category:

Documents


0 download

DESCRIPTION

第 16 章 構造体 ■ ゲーム作成. 16.3 関数と構造体 ■ 乱数の発生 ■ 制御コード ■ 列挙型 ■ system 関数. 今日のポイント. 構造体 、 タグ名 、 メンバー とそれらの  使い方を思い出す! 構造体 のデータを 関数 間でやりとり する場合の仮引数・実引数の使い方 関数値 を 構造体 で戻す方法. x 座標 y 座標. point. 16.3  関数と構造体. 構造体 、 タグ名 、 メンバー とは何だったか ?. プログラム例 16.3.2 から. タグ名. struct point { - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第 16 章 構造体 ■ ゲーム作成

第第 1616 章 構造体章 構造体■ ゲーム作成■ ゲーム作成

16.3 関数と構造体 ■ 乱数の発生 ■ 制御コード ■ 列挙型 ■ system 関数

Page 2: 第 16 章 構造体 ■ ゲーム作成

今日のポイント

構造体、タグ名、メンバーとそれらの 使い方を思い出す!構造体のデータを関数間でやりとり

する場合の仮引数・実引数の使い方関数値を構造体で戻す方法

Page 3: 第 16 章 構造体 ■ ゲーム作成

中点を求めて出力する関数 center_point のプロトタイプ宣言

16.3  関数と構造体構造体、タグ名、メンバーとは何だったか ?

struct point { double x; double y; };

void center_point(struct point p1, struct point p2);

タグ名

メンバーの宣言

p1, p2 は point というタグがついた構造体変数

プログラム例 16.3.2 から

「2つの double 型から成る構造体に point という名前のブランドタグをつけよう」      という感じ

x 座標y 座標

Page 4: 第 16 章 構造体 ■ ゲーム作成

16.3  関数と構造体プログラム例 16.3.2

#include <stdio.h>struct point { double x; double y;};void center_point(struct point p1, struct point p2);

int main(void){ struct point p1, p2; p1.x = 3.8; p1.y = 5.6; p2.x = 8.4; p2.y = 18.2; center_point(p1, p2); return 0;}

構造体の実引数を関数に引き渡す

構造体データの引渡し

Page 5: 第 16 章 構造体 ■ ゲーム作成

16.3  関数と構造体プログラム例 16.3.2

void center_point(struct point p1, struct point p2){ double xm, ym;

xm = (p1.x + p2.x) / 2.; ym = (p1.y + p2.y) / 2.; printf("(%.2f, %.2f) と (%.2f, %.2f) の " " 中点の座標は (%.2f, %.2f)\n", p1.x, p1.y, p2.x, p2.y, xm, ym);}

仮引数が構造体である関数の定義

メンバーを用いて計算

出力はメンバーで

構造体データの引渡し

Page 6: 第 16 章 構造体 ■ ゲーム作成

平均点計算用の関数 average_of_scores のプロトタイプ宣言

16.3  関数と構造体関数から結果を受け取る場合 → ポインタで

struct grade { char *name; int subject1; int subject2; int subject3; double average;

};void average_of_scores(struct grade *h_p);

タグ名

メンバーの宣言

氏 名科目 1 の点数科目 2 の点数科目 3 の点数平均点

h_p は grade というタグがついた構造体の先頭アドレスを指すポインタ変数

プログラム例 16.3.3 から

Page 7: 第 16 章 構造体 ■ ゲーム作成

#include <stdio.h>struct grade {

char *name; int subject1; int subject2; int subject3; double average;

};void average_of_scores(struct grade *h_p);

int main(void){ struct grade Smith = {"John Smith", 90, 80, 35, 0}; average_of_scores(&Smith); printf("name: %s\n", Smith.name); printf(" 科目 1: %d 科目 2: %d 科目 3: %d\n", Smith.subject1, Smith.subject2, Smith.subject3); printf(" 平均 : %f\n", Smith.average); return 0;}

プログラム例 16.3.3 改 平均点計算用の関数のプロトタイプ宣言h_p は grade というタグがついた構造体の先頭を指すポインタ変数

16.3  関数と構造体

ここでは Smith という構造体変数のアドレス(=ポインタ)が実引数

3科目の平均点を出すプログラム

Page 8: 第 16 章 構造体 ■ ゲーム作成

void average_of_scores(struct grade *h_p){ h_p -> average = (h_p -> subject1 + h_p -> subject2 + h_p -> subject3) / 3.;}

プログラム例 16.3.3 改16.3  関数と構造体

ポインタの場合選択演算子 "->" でメンバーを指定する

*h_p.average ⇔ h_p   ->   average

平均点計算用の関数の定義h_p は grade というタグがついた構造体の先頭を指すポインタ変数

3科目の平均点を出すプログラム

Page 9: 第 16 章 構造体 ■ ゲーム作成

16.3  関数と構造体プログラム例 16.3.2 改#include <stdio.h>struct point {double x; double y;};struct point center_point(struct point p1, struct point p2);

int main(void){ struct point p1, p2, p3; p1.x = 3.8; p1.y = 5.6; p2.x = 8.4; p2.y = 18.2; p3 = center_point(p1, p2); printf("(%.2f, %.2f) と (%.2f, %.2f) の " " 中点の座標は (%.2f, %.2f)\n", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); return 0;}

結果の構造体を関数から戻り値で受け取る

関数から結果を構造体で受け取るもう 1 つの方法

戻り値が構造体であることを宣言

結果を受け取る構造体 p3 を用意

Page 10: 第 16 章 構造体 ■ ゲーム作成

struct point center_point(struct point p1, struct point p2){ struct point p;

p.x = (p1.x + p2.x) / 2.; p.y = (p1.y + p2.y) / 2.; return p;}

16.3  関数と構造体プログラム例 16.3.2 改

戻り値が構造体である関数の定義

メンバーを用いて計算

関数から結果を構造体で受け取るもう 1 つの方法

結果を入れる構造体を用意

結果を構造体で返す

Page 11: 第 16 章 構造体 ■ ゲーム作成

■ 乱数の発生 int rand(void)

疑似乱数の発生 範囲: 0 ~ RAN_MAX ( =32767 ) <stdlib.h> 内で宣言

void srand(unsigned n) 疑似乱数のシード(種)の指定・変更 <stdlib.h> 内で宣言

time_t time(time_t *timer) 経過時間を秒単位で表した数値 引数は NULL ( 空ポインタ ) でよい <time.h> 内で宣言

p.183

Page 12: 第 16 章 構造体 ■ ゲーム作成

サイコロ・プログラム (dice.c)#include <stdio.h>#include <stdlib.h>#include <time.h>

int main(void){ int i;/* 実行するたびに違う値が得られるように、 * 現在の時刻値を使って乱数ジェネレータを初期化 */ srand((unsigned)time(NULL));/* サイコロを 10 回振る */ for (i = 0; i < 10; i++) printf("%d ", (rand() % 6) + 1); // 6 の剰余系 + 1 printf("\n"); return 0;}

Page 13: 第 16 章 構造体 ■ ゲーム作成

■ 制御コード 教科書 p.180, 表 A.2: 制御コード

\a ベル・警告音の出力 \r 行頭に戻る ( キャリッジ・リターン) \f そのままの位置で行送り(ライン・フィード) \n 改行 (\r\f) \t 水平タブ

使用例 putchar('\a'); printf(" 現在 %d 回目 \r\a", i);

Page 14: 第 16 章 構造体 ■ ゲーム作成

サイコロ・プログラム 2(dice2.c)#include <stdlib.h>#include <stdio.h>#include <time.h>

int main(void){ int i, n, m;/* 現在の時刻値を使って乱数ジェネレータを初期化 */ srand((unsigned)time(NULL)); printf(" ---\n"); n = rand() % 6; m = n + 8 + rand() % 10; for (i = n; i < m; i++) printf("\r| %d |\a", i % 6 + 1); printf("\n ---\n"); return 0;}

n = 0 ~ 5m = n+8 ~ n+17

1 回の alert に約 200 ms かかる→ このサイコロは 1.6 ~ 3.4 秒間転がる

Page 15: 第 16 章 構造体 ■ ゲーム作成

■ 列挙型 名前付き定数をゼロからの整数に対応させ

る enum タグ { 列挙子リスト }; 例:

実体→ 0, 1, ..., 6

enum DAYS { SUNDAY, MONDAY, ..., SATURDAY };

Page 16: 第 16 章 構造体 ■ ゲーム作成

■ system 関数 OS (コマンドプロンプト)のコマンドをプ

ログラムの中から実行できる。 system("cls")  で画面クリア #include <stdlib.h> が必要

Page 17: 第 16 章 構造体 ■ ゲーム作成

地底探検ゲーム( undergnd.c )

P: ドライブ 2007 年度後期プログラミング入門 2

g[0].block = {1, 0, 0, 1, 0, 0, 0, 1, 0, 1}

■ __■___■_■g[0].blockstr

2 バイト文字なので倍のサイズが必要 + \0 →   21 バイト

#include <stdio.h>#include <stdlib.h>#include <string.h>

/* 地面を格納する構造体の宣言 */struct ground { int block[10]; // 地下情報を格納する配列の宣言 char blockStr[21]; // 地下ブロックパターン};

乱数発生に必要

Page 18: 第 16 章 構造体 ■ ゲーム作成

地底探検ゲーム( undergnd.c )/* 地面を作成する関数 */void makeGround(struct ground *p){ int i; char *kabe[] = { "   ", "■" };

/* 左の壁 */ strcpy(p->blockStr, kabe[1]); p->block[0] = 1;/* 中央のブロックパターン */ for (i = 1; i <= 8; i++) { p->block[i] = rand() % 4 ? 0 : 1; // 確率 1/4 でブロック発生 strcat(p->blockStr, kabe[p->block[i]]); }/* 右の壁 */ strcat(p->blockStr, kabe[1]); p->block[9] = 1;}

3 バイトの文字列2つの配列

"■"

"■"

4 で割り切れたら偽→ 1, 割り切れなければ真→ 0

対応する位置にブロックを置く

Page 19: 第 16 章 構造体 ■ ゲーム作成

地底探検ゲーム( undergnd.c )int main(void){ int i, score = 0, x = 4, key = 1; struct ground g[4]; // 地面の構造体配列 (4 層分 )

/* 最初の地面の作成 */ srand(time(NULL) % 100); // 乱数シード:現在時刻の 100 の剰余 for (i = 0; i < 4; i++) makeGround(g + i);

/* メインループ */ while (key) { /* 画面表示 */ strncpy(g[2].blockStr + x * 2, " ", 2);☆ for (i = 0; i < 4; i++) printf("%s\n", g[i].blockStr); strncpy(g[2].blockStr + x * 2, "   ", 2);

ギブアップチェック用フラグ

次回に☆印を残さないための準備

現在地マークの埋め込み

構造体配列のポインタで引渡

4 層分の地面を描画

現在地記憶用変数

Page 20: 第 16 章 構造体 ■ ゲーム作成

/* キー入力 */ printf(" 距離 %d m: ", score); printf("4←→6 ↓=2 ギブアップ =0: "); scanf("%d", &key); /* 移動 */ if (key == 4 && g[2].block[x - 1] == 0) x--; else if (key == 6 && g[2].block[x + 1] == 0) x++; else if (key == 2 && g[3].block[x] == 0) { for (i = 0; i < 3; i++) g[i] = g[i + 1]; makeGround(g + 3); score++; } }/* 終了処理 */ printf(" スコア : %d メートル! ", score); return 0;}

地底探検ゲーム( undergnd.c )

左に移動

最下層の新規作成

右に移動

下に移動

1 段下からコピー

Page 21: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( mineswpr.c )#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>

#define TRUE 1#define BSIZE 5 // ゲームボードのサイズ#define MINENUM 5 // 地雷の数

// 地雷を表現する構造体typedef struct { int x, y;} Mine;

enum MSTAT { MISS, NEAR, HIT };static char map[BSIZE][BSIZE];

地雷の探査結果用の列挙型MSTAT の定義

整数値の座標で表現

Mine 型として定義

地雷マップ用の配列static にしておくと初期化される

Page 22: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( mineswpr.c )// 地雷を追加する関数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;}// 地雷に近いかどうかをチェックする関数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 && distX <= 1 && -1 <= distY && distY <= 1) return NEAR; return MISS;}

地雷を踏んだら HIT を出力

すでに配置されている地雷と重複しないようチェック

地雷の隣なら NEAR を出力

それ以外なら MISS を出力

地雷マップに登録

Page 23: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( mineswpr.c )// ゲームボード用データ構造int status[BSIZE][BSIZE];Mine m[MINENUM];enum BSTAT { ALREADY, OPEN, BOMB };

// ゲームボードの初期化関数void Board_init(void){ int x, y, i;// 状態の初期化 for (y = 0; y < BSIZE; y++) for (x = 0; x < BSIZE; x++) status[y][x] = -1;// 地雷セット for (i = 0; i < MINENUM; i++) Mine_set(&(m[i]));}

未探査状態は 1

ボードの状況用配列

地雷の数だけ呼び出す

地雷用配列を確保

ボードの状況記述用の列挙型 BSTAT の定義

ポインタを渡す

Page 24: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( 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;}

探査済みなら 0 以上

→ALREADY を出力未探査ならチェック

地雷の探査結果用

全部の地雷についてチェックして

NEAR ばかりなら OPEN を出力

結果が HIT なら BOMB を出力結果が NEAR ならカウンタを

1つ増して次の地雷をチェックNEAR の数をボードに記載

Page 25: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( mineswpr.c )// ゲームボードを画面に表示する関数void Board_show(void){ int i, x, y;

printf(" "); for (i = 0; i < BSIZE; i++) putchar('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 座標の数字表示

ボードの x 座標 A ~ E の表示

未探査なら # を表示

探査済みならNEAR の数を表示

Page 26: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( 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(); 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';

ゲームボードの表示

ゲームボードの状況報告用

キーボードから 1 行入力

2 文字未満ならやりなおし

ボードの範囲外ならやりなおし

ゲームボードの初期化

Page 27: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( mineswpr.c )

// 入力された Y 座標をチェックする charY = key[1]; if (charY < '1' || charY > '1' + BSIZE-1) continue; y = charY - '1'; // 判 定 if ((bs = Board_open(x, y)) == BOMB) { printf("\a\a\a\a 地雷を踏んでしまいました! \n"); break; } if (bs == ALREADY) continue; if (++openCount == BSIZE*BSIZE - MINENUM) { printf("\a クリアしました! \n"); Board_show(); break; } }

地雷を踏んだら強制終了

ボードの範囲外ならやりなおし

探査済み数=全座標数-地雷数 ならクリア最終結果を表示

探査済みならやりなおし

Page 28: 第 16 章 構造体 ■ ゲーム作成

マイン・スイーパー( mineswpr.c )

   printf("\n"); for (i=0; i<MINENUM; i++)    printf(" %c%d",'A'+m[i].x,1+m[i].y); return 0;} 正解(地雷の座標)の表

A B C D E1 -1-1-1-1-12 -1-1-1-1-13 -1-1-1-1-14 -1-1-1-1-15 -1-1-1-1-1

status[][]

A B C D E1 0 1 0 0 02 0 0 0 0 13 1 0 0 0 04 0 0 0 0 05 1 0 0 1 0

map[][]

m[0]={4,1}m[1]={1,0}m[2]={3,4}m[3]={0,4}m[4]={0,2}

Mine m[]

Page 29: 第 16 章 構造体 ■ ゲーム作成

スキルアップタイム1 地底探検ゲーム (undergnd.c) をコンパイ

ルし、動作を確認せよ srand(…) の行をコメントアウトすると、

どうなるか、確認せよ ファイル入出力関数の利用により、「ハイ

スコアの表示・保存機能」を付け足せ(保存ファイル名は highscr.txt )

Page 30: 第 16 章 構造体 ■ ゲーム作成

スキルアップタイム 1 のヒント int highscore=0; FILE *fp;

...

if ((fp = fopen("highscr.txt", "r")) != NULL) { fscanf(fp, "%d", &highscore); fclose(fp); } if (score > highscore) { printf(" ハイスコアです! \n"); fp = fopen("highscr.txt", "w"); fprintf(fp, "%d\n", score); fclose(fp); } else printf(" ハイスコア: %d メートル \n", highscore);

Page 31: 第 16 章 構造体 ■ ゲーム作成

スキルアップタイム2 マイン・スイーパー (mineswpr.c) をコン

パイルし、動作を確認せよ ゲームボードのサイズや地雷の数を変えて

みよ srand(…) の行をコメントアウトすると、

どうなるか、確認せよ 盤面を表示するたびに、実行ウィンドウの

上部から書き直すように改良せよ