1 / 31

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

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

syshe
Download Presentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第16章 構造体■ ゲーム作成 16.3 関数と構造体 ■ 乱数の発生 ■ 制御コード ■ 列挙型 ■ system関数

  2. 今日のポイント • 構造体、タグ名、メンバーとそれらの  使い方を思い出す! • 構造体のデータを関数間でやりとり する場合の仮引数・実引数の使い方 • 関数値を構造体で戻す方法

  3. x 座標 y座標 point 16.3 関数と構造体 構造体、タグ名、メンバーとは何だったか? プログラム例 16.3.2 から タグ名 structpoint{ double x; double y; }; void center_point(structpointp1, structpointp2); 「2つのdouble型から成る構造体に pointという名前のブランドタグをつけよう」      という感じ メンバーの宣言 中点を求めて出力する関数 center_pointのプロトタイプ宣言 p1, p2はpointというタグがついた構造体変数

  4. #include <stdio.h> structpoint { double x; double y; }; void center_point(structpointp1, structpointp2); int main(void) { struct pointp1, p2; p1.x = 3.8; p1.y = 5.6; p2.x = 8.4; p2.y = 18.2; center_point(p1, p2); return 0; } 16.3 関数と構造体 構造体データの引渡し プログラム例 16.3.2 構造体の実引数を関数に引き渡す

  5. void center_point(structpointp1, structpointp2) { 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); } 16.3 関数と構造体 構造体データの引渡し プログラム例 16.3.2 仮引数が構造体である関数の定義 メンバーを用いて計算 出力はメンバーで

  6. 氏 名 科目1の点数 科目2の点数 科目3の点数 平均点 grade 16.3 関数と構造体 関数から結果を受け取る場合 → ポインタで タグ名 プログラム例16.3.3から • struct grade { • char *name; • int subject1; • int subject2; • int subject3; • double average; • }; • void average_of_scores(struct grade *h_p); メンバーの宣言 平均点計算用の関数 average_of_scoresのプロトタイプ宣言 h_pはgradeというタグがついた構造体の先頭アドレスを指すポインタ変数

  7. 16.3 関数と構造体 3科目の平均点を出すプログラム プログラム例 16.3.3改 平均点計算用の関数のプロトタイプ宣言 h_pは gradeというタグがついた 構造体の先頭を指すポインタ変数 • #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 gradeSmith = {"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; • } ここでは Smithという構造体変数の アドレス(=ポインタ)が実引数

  8. 16.3 関数と構造体 プログラム例 16.3.3改 3科目の平均点を出すプログラム 平均点計算用の関数の定義 h_pは gradeというタグがついた 構造体の先頭を指すポインタ変数 void average_of_scores(struct grade*h_p) { h_p -> average = (h_p -> subject1 + h_p -> subject2 + h_p -> subject3) / 3.; } ポインタの場合 選択演算子"->"でメンバーを指定する *h_p.average ⇔ h_p->average

  9. #include <stdio.h> structpoint {double x; double y;}; struct point center_point(structpointp1, structpointp2); int main(void) { struct pointp1, 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; } 16.3 関数と構造体 関数から結果を構造体で 受け取るもう1つの方法 プログラム例 16.3.2改 戻り値が構造体であることを宣言 結果を受け取る構造体 p3を用意 結果の構造体を関数から戻り値で受け取る

  10. struct point center_point(structpointp1, structpointp2) { struct pointp; p.x = (p1.x + p2.x) / 2.; p.y = (p1.y + p2.y) / 2.; return p; } 16.3 関数と構造体 関数から結果を構造体で 受け取るもう1つの方法 プログラム例 16.3.2改 戻り値が構造体である関数の定義 結果を入れる構造体を用意 メンバーを用いて計算 結果を構造体で返す

  11. ■ 乱数の発生 p.183 • int rand(void) • 疑似乱数の発生 • 範囲:0~RAN_MAX(=32767) • <stdlib.h>内で宣言 • void srand(unsigned n) • 疑似乱数のシード(種)の指定・変更 • <stdlib.h>内で宣言 • time_t time(time_t *timer) • 経過時間を秒単位で表した数値 • 引数は NULL (空ポインタ)でよい • <time.h>内で宣言

  12. サイコロ・プログラム(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; }

  13. ■ 制御コード • 教科書 p.180, 表A.2: 制御コード • \aベル・警告音の出力 • \r行頭に戻る(キャリッジ・リターン) • \fそのままの位置で行送り(ライン・フィード) • \n改行(\r\f) • \t水平タブ • 使用例 • putchar('\a'); • printf("現在 %d 回目\r\a", i);

  14. サイコロ・プログラム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 ~ 5 m = n+8 ~ n+17 1回の alert に約200 ms かかる → このサイコロは1.6 ~3.4秒間転がる

  15. ■ 列挙型 • 名前付き定数をゼロからの整数に対応させる • enumタグ{列挙子リスト}; • 例: 実体→ 0, 1, ..., 6 • enum DAYS{ • SUNDAY, MONDAY, ..., SATURDAY • };

  16. ■ system関数 • OS(コマンドプロンプト)のコマンドをプログラムの中から実行できる。 • system("cls") で画面クリア • #include <stdlib.h>が必要

  17. #include <stdio.h> #include <stdlib.h> #include <string.h> /* 地面を格納する構造体の宣言 */ struct ground { int block[10]; // 地下情報を格納する配列の宣言 char blockStr[21]; // 地下ブロックパターン }; 地底探検ゲーム(undergnd.c) P:ドライブ-2007年度後期-プログラミング入門2 乱数発生に必要 g[0].block = {1, 0, 0, 1, 0, 0, 0, 1, 0, 1} g[0].blockstr ■__■___■_■ 2バイト文字なので倍のサイズが必要 + \0 →21バイト

  18. 地底探検ゲーム(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 対応する位置にブロックを置く "■"

  19. 地底探検ゲーム(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層分の地面を描画 次回に☆印を残さないための準備

  20. /* キー入力 */ 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段下からコピー 最下層の新規作成

  21. マイン・スイーパー(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]; 整数値の座標で表現 Mine 型として定義 地雷の探査結果用の列挙型MSTATの定義 地雷マップ用の配列 staticにしておくと初期化される

  22. マイン・スイーパー(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を出力

  23. マイン・スイーパー(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])); } ボードの状況用配列 地雷用配列を確保 ボードの状況記述用の 列挙型BSTATの定義 未探査状態は -1 地雷の数だけ呼び出す ポインタを渡す

  24. マイン・スイーパー(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 を出力 未探査ならチェック 結果がHITならBOMBを出力 結果がNEARならカウンタを1つ増して次の地雷をチェック NEARの数をボードに記載 全部の地雷についてチェックして NEARばかりならOPENを出力

  25. マイン・スイーパー(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"); } } ボードの x 座標 A~E の表示 y 座標の数字表示 未探査なら # を表示 探査済みなら NEARの数を表示

  26. マイン・スイーパー(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文字未満ならやりなおし ボードの範囲外ならやりなおし

  27. マイン・スイーパー(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; } } 地雷を踏んだら強制終了 探査済みならやりなおし 探査済み数=全座標数-地雷数 ならクリア 最終結果を表示

  28. マイン・スイーパー(mineswpr.c) printf("\n"); for (i=0; i<MINENUM; i++) printf(" %c%d",'A'+m[i].x,1+m[i].y); return 0; } 正解(地雷の座標)の表示 status[][] map[][] A B C D E 1 0 1 0 0 0 2 0 0 0 0 1 3 1 0 0 0 0 4 0 0 0 0 0 5 1 0 0 1 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 Mine m[] m[0]={4,1} m[1]={1,0} m[2]={3,4} m[3]={0,4} m[4]={0,2}

  29. スキルアップタイム1 • 地底探検ゲーム(undergnd.c)をコンパイルし、動作を確認せよ • srand(…)の行をコメントアウトすると、どうなるか、確認せよ • ファイル入出力関数の利用により、「ハイスコアの表示・保存機能」を付け足せ(保存ファイル名はhighscr.txt)

  30. スキルアップタイム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);

  31. スキルアップタイム2 • マイン・スイーパー(mineswpr.c)をコンパイルし、動作を確認せよ • ゲームボードのサイズや地雷の数を変えてみよ • srand(…)の行をコメントアウトすると、どうなるか、確認せよ • 盤面を表示するたびに、実行ウィンドウの上部から書き直すように改良せよ

More Related