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

Slides:



Advertisements
Similar presentations
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
Advertisements

情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
プログラミング入門2 第10回 動的な領域確保 情報工学科 篠埜 功.
配列(2) 第10回[平成15年6月26日(木)]:PN03-10.ppt 今日の内容 1 素数を求める(教科書の例):復習
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
リダイレクト パイプ 標準入出力プログラム コマンド行引数 関数 system()
16.3 関数と構造体 構造体ポインタ 地底探査ゲーム
構造体.
配列の扱い、探索 有効範囲と記憶域期間 第12回 [7月10日、H.15(‘03)] 今日のメニュー 1 前回の課題の復習
第16章 構造体 16.1 構造体の定義と構造体変数 16.2 構造体の配列.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
第16章 構造体 16.1 構造体の定義と構造体変数 16.2 構造体の配列.
第10回 プログラミングⅡ 第10回
第16章 構造体 16.1 構造体の定義と構造体変数 16.2 構造体の配列.
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング2 関数
岩村雅一 知能情報工学演習I 第11回(後半第5回) 岩村雅一
プログラミング論 ファイル入出力
関数とポインタ 値呼び出しと参照呼び出し swapのいろいろ 関数引数 数値積分
16.3 関数と構造体 ■ 乱数の発生 ■ 制御コード ■ 列挙型 ■ system関数
関数と配列とポインタ 1次元配列 2次元配列 配列を使って結果を返す 演習問題
マインスイーパの概要 マインスイーパの準備 マインスイーパの完成 マインスイーパの改良
関数の定義.
第10回関数 Ⅱ (ローカル変数とスコープ).
第11回 宿題 出題日:12月21日 締切日:1月7日(木).
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第2回 ファイル処理 情報・知能工学系 山本一公
プログラミング 4 記憶の割り付け.
リダイレクト パイプ 標準入出力プログラム コマンド行引数 関数 system()
プログラミング演習I 2003年6月25日(第10回) 木村巌.
第10章 これはかなり大変な事項!! ~ポインタ~
第17章 その他の制御文 17.1 do-while文 17.2 goto文とラベル 17.3 break文による繰返し制御
プログラミング入門2 第11回 情報工学科 篠埜 功.
今までの練習問題の復習.
第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入
プログラミング入門2 第11回 情報工学科 篠埜 功.
第7回 プログラミングⅡ 第7回
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
プログラミング論 ファイル入出力
16.3 関数と構造体 ■ 乱数の発生 ■ 制御コード ■ 列挙型 ■ system関数
第11回 プログラミングⅡ 第11回
プログラミング基礎B 文字列の扱い.
プログラムの制御構造 配列・繰り返し.
岩村雅一 知能情報工学演習I 第11回(後半第5回) 岩村雅一
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
第14章 ファイル操作 14.1 ファイルへの書き込み 14.2 ファイルからの読み込み 14.3 ファイルへの追加書き込み
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
配列変数とポインタ 静的確保と動的確保 ポインタ配列 2次元配列 時間計測 第1回レポートの課題
疑似乱数, モンテカルロ法によるシミュレーション
第14章 ファイル操作 14.1 ファイルへの書き込み 14.2 ファイルからの読み込み 14.3 ファイルへの追加書き込み
C言語 はじめに 2016年 吉田研究室.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
第13章 文字の取り扱い方 13.1 文字と文字型変数 13.2 文字列 13.3 文字型配列への文字列の代入
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
第5回 プログラミングⅡ 第5回
cp-15. 疑似乱数とシミュレーション (C プログラミング演習,Visual Studio 2019 対応)
プログラミング 4 文字列.
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
モバイルプログラミング第2回 C言語の基礎 (1).
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング入門2 第5回 配列 変数宣言、初期化について
printf・scanf・変数・四則演算
第14章 ファイル操作 14.1 ファイルへの書き込み 14.2 ファイルからの読み込み 14.3 ファイルへの追加書き込み
分岐(If-Else, Else if, Switch) ループ(While, For, Do-while)
プログラミング演習I 補講用課題
= 55 課題6-1 #define _CRT_SECURE_NO_WARNINGS
Presentation transcript:

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

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

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

16.3 関数と構造体 プログラム例 16.3.2 構造体データの引渡し #include <stdio.h> 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; } 構造体の実引数を関数に引き渡す

16.3 関数と構造体 プログラム例 16.3.2 構造体データの引渡し 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); } メンバーを用いて計算 出力はメンバーで

平均点計算用の関数 average_of_scores のプロトタイプ宣言 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); 氏 名 科目1の点数 科目2の点数 科目3の点数 平均点 grade メンバーの宣言 平均点計算用の関数 average_of_scores のプロトタイプ宣言 h_p は grade というタグがついた構造体の先頭アドレスを指すポインタ変数

16.3 関数と構造体 プログラム例 16.3.3 改 #include <stdio.h> struct grade { 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 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; } ここでは Smith という構造体変数の アドレス(=ポインタ)が実引数

16.3 関数と構造体 *h_p.average ⇔ h_p -> average プログラム例 16.3.3 改 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

16.3 関数と構造体 プログラム例 16.3.2 改 関数から結果を構造体で 受け取るもう1つの方法 16.3 関数と構造体 関数から結果を構造体で 受け取るもう1つの方法 プログラム例 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; } 戻り値が構造体であることを宣言 結果を受け取る構造体 p3 を用意 結果の構造体を関数から戻り値で受け取る

16.3 関数と構造体 プログラム例 16.3.2 改 関数から結果を構造体で 受け取るもう1つの方法 16.3 関数と構造体 関数から結果を構造体で 受け取るもう1つの方法 プログラム例 16.3.2 改 戻り値が構造体である関数の定義 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; } 結果を入れる構造体を用意 メンバーを用いて計算 結果を構造体で返す

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

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

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

サイコロ・プログラム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秒間転がる

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

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

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

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

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

地底探検ゲーム(undergnd.c) /* キー入力 */ printf("距離 %d m: ", score); 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; 左に移動 右に移動 下に移動 1段下からコピー 最下層の新規作成

マイン・スイーパー(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にしておくと初期化される

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

マイン・スイーパー(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 地雷の数だけ呼び出す ポインタを渡す

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

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

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

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

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

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

スキルアップタイム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); } else printf("ハイスコア: %d メートル\n", highscore);

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