関 数 と ファイルの 保 存 読 出 し 構 造 体 前 々 回 の RPG(ドラゴンクエストもどき)は 結 構 みんな 頑 張 って いたようでしたので 今 回 はその 発 展 形 を 題 材 にすることにしまし た (と 昨 年 書 きました ) 皆 さん ずいぶん 逞 しく 成 長 して 来 ていると 思 います エラー 対 応 は 慣 れ が 大 切 ですが 面 白 さを 覚 えて 慣 れる ま でに 疲 れて 面 倒 くさくなってしまうのが 通 例 なので エラー に 悩 まされている 時 は 遠 慮 しないで 手 を 挙 げてください 頑 張 った 内 容 は スキル として 自 分 の 能 力 アップにつながってい ます 自 信 を 持 ってください 多 くの 人 がかなり 実 力 をつけていま す さて 今 日 の 教 材 は ほとんど 初 心 者 向 け の 配 慮 をせずに 使 える 機 能 は 普 通 に 使 う ようにして 書 きました 複 数 の 人 間 が 分 割 して 作 成 する という 処 理 はしていませんので 全 部 が 一 つのプ ログラムファイルに 収 まっていますが それ 以 外 は 実 践 レベルの 内 容 です ( 変 数 のスコープとか 組 み 込 んでいない 要 素 も 多 数 あり ますが ) ということは 完 璧 に 理 解 して 修 正 を 行 う というよりも まず 読 んで 目 を 慣 らして 雰 囲 気 をつかむのが 主 眼 です 長 文 読 解 の 教 材 ですが 雰 囲 気 がわかれば OK です ある 程 度 雰 囲 気 がつかめたら そこから 課 題 の 修 正 に 挑 戦 して みてください 最 初 は 関 数 について 学 習 しますが ファイルの 保 存 と 構 造 体 につ いては 読 み 方 書 き 方 のコツをつかんで 下 さい どちらも.(ピリオド)や *(ポインタ 演 算 子 )など 記 号 文 字 の 使 い 方 書 き 方 などが 重 要 です 1
関 数 実 は 関 数 は 初 登 場 ではありません これまで どんな 関 数 が 出 て 来 たかと 言 うと printf() scanf() rand() srand() _getch() などがあります printf()は 関 数 ですが ( 二 重 引 用 符 号 )で 囲 まれた 範 囲 を 基 本 として 表 示 しながら %d, %s, %c などを 設 定 すると その 書 式 に 従 って 数 字 や 文 字 などを 表 示 できました その 書 き 方 の 決 まり や 約 束 事 などは 関 数 ごとに 異 なっています 標 準 関 数 ではなく 自 分 で 定 義 した ユーザ 関 数 も 既 に 何 度 も 使 ってしま っています 教 科 書 を 使 って おさらいします それが 前 々 回 の 以 下 のプログラムです (438 行 目 から 461 行 目 付 近 ) /* * 攻 撃 力 を 計 算 する */ int calc_attack( int attack_power ) { int p = rand()%31 + 70; // 70% 100% int attack = attack_power * p / 100; switch( your_weapon ){ case 0: default: return attack; case 1: return attack * 3 / 2; // 1.5 倍 : ナイフ case 2: return attack * 2; // 2 倍 : 竹 やり case 3: return attack * 3; // 3 倍 : 胴 の 剣 case 4: return attack * 5; // 5 倍 : 勇 者 の 剣 // ================================================================ 2
関 数 の 名 前 は calc_attack です 引 数 は 一 つだけ 持 っています メインのプログラムから attack_power という 変 数 の 値 を 渡 されて それに 対 して 攻 撃 力 を 計 算 しています この 関 数 の 目 的 は パワー から 攻 撃 力 を 計 算 することで 2 段 階 で 計 算 しています 第 1 段 階 では 乱 数 で attack_power の 70 100% の 値 を 計 算 します 第 2 段 階 では 武 器 ( your_weapon )の 値 に 応 じて 攻 撃 力 を 倍 加 して います そうして 計 算 された 値 は 以 前 のバージョンでは your_attack という 変 数 に 代 入 しました 今 回 は 普 通 の 関 数 の 書 き 方 で return 文 で 戻 しています int calc_attack( int attack_power ) と 書 いてあります この 関 数 は int 型 なので int で 結 果 を 戻 して 本 体 側 で 受 け 取 っています 前 回 のプログラムでは この 関 数 内 で your_attack に 代 入 していました なぜ 関 数 を 使 うのでしょうか? 関 数 を 使 うことで 本 体 部 分 がスッキリとして メインのプログラムの 流 れ を 理 解 しやすくなる ということがあります 今 回 のプログラムは 以 前 のプログラムに 対 して 若 干 パワーアップ してい ます ステージを5 段 階 に 分 けた ステージごとに 登 場 するモンスターを 分 けた 武 器 や 防 具 の 種 類 を 増 やした ゲームを 保 存 したり 保 存 したゲームを 読 み 出 せるようにした などです 約 800 行 のプログラムになりました プログラムの 全 体 構 造 198 行 目 から 408 行 目 が メインの 最 も 大 きなループです ここでは 竜 の 王 冠 を 手 に 入 れるまで 反 復 処 理 を 行 います ループの 入 り 口 でメニューを 表 示 させて メニューによって 処 理 が 分 かれます その switch / case が 208 行 目 から 始 まり メインのループ 全 体 にかぶさ 3
っています ここで 処 理 番 号 1 5は それぞれが 関 数 として 実 行 されるように 構 成 されています 10 行 目 から 19 行 目 は プロトタイプ と 呼 ばれ その 関 数 の 型 や 引 数 の 種 類 を 指 定 します 以 下 の 関 数 について 簡 単 に 説 明 しましょう extern int menu( void ); extern int set_monster( int ); extern void save_game( void ); extern void go_shop( int ); 複 数 の 引 数 を 渡 すことができますが ここでは 上 記 のような4 種 類 の 関 数 を 使 いました 関 数 menu では 引 数 は 渡 さず( void は 型 がないことを 意 味 します) 選 択 されたメニューの 番 号 を return しています 521 行 目 から 536 行 目 までで メニューの 表 示 と 数 値 の 処 理 を 行 って 返 しています 関 数 set_monster では 登 場 させるモンスターを 乱 数 で 決 めています ステージごとに 登 場 するモンスターに 違 いがあります この どのステージに どのモンスターを 登 場 させるかを stage_monster と いう 二 次 元 配 列 で 設 定 してあります 使 っているのは 593 行 目 ですが 定 義 は 52 行 目 にあります ここでは 引 数 として int でステージ 番 号 を 手 渡 し そのステージで 登 場 できるモンスター 番 号 のリストを 作 成 し 次 に その 作 成 されたリストの 中 から 乱 数 で 登 場 させるモンスターを 選 んでいます stage_monster[モンスター 番 号 ][ステージ 番 号 ]が true なら そのモンスター がそのステージに 登 場 することを 意 味 し false なら 登 場 しないことを 意 味 しま す 関 数 save_game では 引 数 も 戻 り 値 もなく ゲーム 全 体 を 制 御 するパラメ ータを 保 存 しています 通 常 は こうした 関 数 では 保 存 成 功 なら true を 保 存 失 敗 なら false を 返 すようにプログラムしますが 今 回 は 失 敗 してもそのまま 続 行 しています 関 数 go_shop では ステージごとに 購 入 可 能 な 品 物 の 種 類 を 変 えています 126 137 行 目 で 定 義 している stage_item という 二 次 元 配 列 で この 組 み 合 わせを 制 御 しています 但 し 何 かを 購 入 した 処 理 はすぐにその 場 で 処 理 4
されているため return で 値 を 戻 すということはしていません void 型 です こうした 関 数 で メニュー 分 岐 の 個 別 の 処 理 を 行 い 全 体 構 造 を 見 やすくして います ステージ 選 択 では 持 っているアイテム に 応 じて 選 択 できるステージを 分 け ています 258 行 目 から 404 行 目 までは それぞれの ステージ ごとのループで 鍵 を 手 に 入 れるまでは 抜 けることができません このゲームプログラムでは ほとんどの 変 数 を グローバル 変 数 として 定 義 しています 実 は グローバル 変 数 はあまり 多 用 するべきではなく 本 格 的 に プログラミングを 学 ぶならば 変 数 のスコープ を 意 識 しなければならず 関 数 の 引 数 はもっと 増 える 場 合 が 多 いのです 但 し 皆 さんは 臨 床 工 学 なので スコープについては 学 びません 授 業 では 扱 わないけれど ステップアップすると 少 し 書 き 方 が 変 わると 理 解 し て 下 さい 報 告 課 題 (1) それぞれのステージで これ 以 上 続 けられない と 思 った 時 に そのステージ から 脱 出 するためには プログラムのどの 行 をどのように 書 き 換 えたらい いでしょうか? ヒント ステージごとのループ(258 行 目 405 行 目 )では stage_play( 最 もレベルの 高 いステージに 進 むのではなく 以 前 クリア して 鍵 を 持 っているステージを 選 んでプレイしているモード)の 場 合 と 鍵 を 持 っていない 間 は 反 復 するようにしています ここで stage_play の 時 だけ 投 げている 質 問 を いつでも 確 認 するよ うに 書 き 直 せば 修 正 完 了 です 278 行 目 で 逃 げるかどうか 確 認 していますから ここで 脱 出 を 決 めてもいいでしょう 体 力 が 落 ちている 場 合 は 287 行 目 でドリンクを 飲 めるようにしました この 場 合 は 戦 闘 メニューの 表 示 を 変 えて 入 力 を 処 理 するという 修 正 になるでしょう 何 行 目 をどのように 修 正 したら ステージから 脱 出 出 来 るようになるか 考 え 方 を 説 明 し 修 正 前 と 修 正 後 のプログラムを 示 し 確 認 表 示 が 出 て 選 択 した 結 果 でループを 抜 けた 実 行 画 面 を 添 付 して 下 さい ( 配 点 :3 点 ) 5
戦 闘 は 290 行 目 から 391 行 目 のループです 相 手 の 攻 撃 は 294 行 目 から 315 行 目 ( 倒 された 場 合 も 含 む) あなたの 攻 撃 は 317 行 目 から 324 行 目 相 手 を 倒 した 場 合 の 処 理 が 326 390 行 目 です ステージごとに 倒 したモンスターによって 手 に 入 れられるものが 変 化 して います その 点 については プログラムを 読 んでください 構 造 体 今 回 初 めて 使 われたのは 構 造 体 です モンスターの 定 義 に 構 造 体 を 使 いまし た 22 行 目 から 39 行 目 です モンスターの 名 前 プログラム 上 の 識 別 番 号 攻 撃 力 防 御 力 倒 した 時 に 得 るゴールドの 値 倒 した 時 に 得 る 経 験 値 などがモンスターごとに 定 義 されて います 複 数 のデータを 一 度 に 組 み 合 わせるため 構 造 体 は 使 われます 配 列 をたくさん 用 意 する という 方 法 もありますが その 場 合 だと 追 加 や 削 除 があった 時 に 修 正 漏 れがおきて 順 番 がずれるなどのバグの 原 因 になりや すいので 構 造 体 を 用 います struct 構 造 体 名 { 変 数 の 型 変 数 名 ; 変 数 の 型 変 数 名 ; ; : という 書 き 方 が 基 本 形 です 発 展 形 として bit ごとの 定 義 がありますが 組 込 み 系 プログラムや 機 器 制 御 など 以 外 にはあまり 使 いません この 例 では 構 造 体 の 定 義 と データの 配 列 の 設 定 を 一 度 に 行 いました 6
// モンスター 定 義 構 造 体 struct Monster { int id; // 識 別 番 号 char *name; // 名 前 int min_attack; // 攻 撃 力 初 期 値 最 小 値 int max_attack; // 攻 撃 力 初 期 値 最 大 値 int defense; // 防 御 力 初 期 値 int gold; // ゴールド 所 持 初 期 値 int experience; // 取 得 可 能 な 経 験 値 この 構 造 体 は 上 から 順 に id, name, min_attack, max_attack, defense, gold, experience となっています 41 行 目 から 48 行 目 まで 8 種 類 のモンスターが 定 義 されています 先 頭 のスライムは 0, "スライム", 10, 15, 15, 3, 2 となていますが これは プログラム 中 での ID が 0, 名 称 がスライム このモンスターの 戦 闘 能 力 は 10 から 15 の 間 で このモンスターを 倒 すと 3 ゴールドが 手 に 入 り こ のモンスターを 倒 した 時 の 経 験 値 は 2 増 加 する ということを 意 味 しています 今 回 の 例 では int と char * 型 だけでしたが float や double などが 使 われる こともあります これが 使 われている 部 分 の 例 は 267 269 行 目 にあります monster_power = rand() % (monster[monster_id].max_attackmonster[monster_id].min_attack+1) + monster[monster_id].min_attack; このモンスターのパワーは 乱 数 で 最 小 値 から 最 大 値 の 間 になるように 計 算 し ています ( 前 回 のバージョンでは プログラム 中 に 数 字 で 書 いていた 部 分 を 構 造 体 定 義 の 一 覧 表 の 部 分 に 移 動 した 形 になっています ) なんだか 難 しく 感 じるかも 知 れませんが 難 しく 考 えずに セットメニュー で 今 日 のおかず 今 日 のみそ 汁 今 日 のごはん/パン 今 日 の 漬 け 物 など が4つ1セットとして 定 義 され それが 毎 日 入 れ 替 わるけれども その 入 れ 物 は 変 わらない それが 構 造 体 だと 思 ってもらえればいいでしょう 7
ファイルへの 保 存 と 読 み 出 し 今 回 は ゲーム 仕 立 て です ですので ファイルへの 保 存 は ゲームの 中 間 経 過 を 保 存 する という 作 業 をどうプログラムするかで 教 材 としました 同 様 にして ファイルからの 読 み 出 しも ゲームを 途 中 から 再 開 する という 形 になっています ゲームの 保 存 は 以 下 のようになります /* * ゲームの 保 存 */ void save_game(void) { int i; FILE *fp; int errno; errno = fopen_s( &fp, "gemesave.gsv", "w"); if ( errno!=noerror ) return; fprintf(fp, "%d,%d,%d,%d n", your_hitpoint, your_power, your_max_hitpoint, your_max_power); for (i = 0; i<num_stages; i++){ fprintf(fp, "%d", (have_pass[i]? 1 : 0)); if (i<num_stages - 1) fprintf(fp, ","); else fprintf(fp, " n"); fclose(fp); printf("ゲームを 保 存 しました n"); Sleep(1000); // ================================================================ 最 初 にファイルを 開 きます ここで 書 き 込 みモード ( w )か 読 み 出 し モード ( r )かを 指 定 しますが save は 保 存 なので 書 込 み です 書 き 込 みモード でファイルが 存 在 しない 場 合 には 新 規 作 成 されます 548 行 目 の fopen_s と 557 行 目 の fclose までが 書 き 込 みのためのプロ グラムです fopen_s の 第 1パラメータは ファイルポインタで アドレス 渡 し 指 定 をし ています そのため &がついていますが ここはこういう 書 き 方 だと 思 ってい て 下 さい 第 2 パラメータはファイル 名 です プログラムでファイルを 指 定 す る 時 は 拡 張 子 まで 省 略 せずに 記 載 しなければなりません 第 3 パラメータ の w は 書 き 込 み を 意 味 します 8
書 き 込 みでは fprintf が 使 われますが どこかで 見 たことがありませんか? 画 面 に 文 字 列 を 表 示 する printf に f がついた 関 数 です 関 数 名 に f がつい ていて 第 一 パラメータが fp (file pointer)である 以 外 は printf と 同 じ 使 い 方 ができます 保 存 を 行 っている 最 初 の 文 は fprintf( fp, "%d,%d,%d,%d\n", your_hitpoint, your_power, your_max_hitpoint, your_max_power ); です %d が4 回 (10 進 整 数 が4つ)カンマで 区 切 られながら 並 んでいます your_hitpoint, your_power, your_max_hitpoint, your_max_power の 値 が それぞれ 何 を 意 味 しているかわかりますか? 問 題 は この 次 からです for( i=0; i<num_stages; i++ ){ fprintf( fp, "%d", (have_pass[i]? 1: 0) ); if( i<num_stages-1 ) fprintf( fp, "," ); else fprintf( fp, "\n" ); この2 行 目 (have_pass[i]? 1 : 0) という 記 述 があります この 行 は?と:がワンセットになって 一 つの 演 算 子 になっています 条 件 式? ( 条 件 が 真 の 場 合 の 値 ) : ( 条 件 が 偽 の 場 合 の 値 ) ですから have_pass[i]が もし true なら1が false ならば 0 が 書 き 込 まれ ます 1や0は integer なので %d ( 十 進 整 数 )としてファイルに 書 き 込 ん でいます 次 の if 文 では i が 最 後 のステージでない 場 合 には, (カンマ)を 最 後 のス テージの 場 合 には \n( 改 行 )を 書 き 込 んで 1 行 になるようにしています こうして 保 存 された 内 容 は 例 えば 以 下 のようになります 9
ファイルの 読 み 出 しも 保 存 の 場 合 とよく 似 ています /* * ゲームの 読 出 */ void { load_game(void) FILE int int *fp; a, i; errno; errno = fopen_s(&fp,"gemesave.gsv", "r"); fscanf_s(fp, "%d,%d,%d,%d n", &your_hitpoint, &your_power, &your_max_hitpoint, &your_max_power); for (i = 0; i<num_stages; i++){ fscanf_s(fp, "%d", &a); if (i<num_stages - 1) fscanf_s(fp, ","); else fscanf_s(fp, " n"); if (a == 1) have_pass[i] = true; else have_pass[i] = false; fclose(fp); printf("ゲームを 読 み 出 しました n"); Sleep(1000); // ================================================================ まず 最 初 の fopen のファイル 名 の 次 のパラメータが r になっています これは read( 読 み 込 み)でファイルを 開 くことを 意 味 します 書 き 出 し( 保 存 )は fprintf でしたが 読 み 込 みは fscanf_s で 実 行 できます 使 い 方 は scanf と 同 じで 書 式 指 定 を 行 ってデータを 読 み 込 ます 読 み 込 んでいる 変 数 名 には&をつけてアドレス 指 定 をしています こ こを 間 違 える 人 が 多 いので 特 に 気 をつけて 下 さい そして 読 み 込 む 順 番 は 書 き 出 した 順 番 と 全 く 同 じにします 順 番 が 狂 うと 数 字 の 意 味 が 変 わってしまいますので 気 をつけて 下 さい 最 後 の have_pass は 読 み 込 んだ 値 が 1 ならば true 0 ならば false とな るように 読 んでいます 10
Visual Studio の 警 告 で fscanf は 危 険 だから fscanf_s を 使 え というメッセージがありました 警 告 がうっとうしいので 素 直 にその 指 示 に 従 っています 教 科 書 的 には scanf_s は scanf で fscanf_s は fscanf です 本 当 に きちんとファイルに 保 存 し きちんとファイルから 読 み 出 せているの でしょうか?それを 確 認 するために menu の5 番 目 に 確 認 というコマン ドを 入 れました RPG では 今 現 在 の HP(ヒットポイント)がどの 程 度 か 把 握 していないと 簡 単 に 倒 されてしまいます ですので どんな 装 備 で ドリンクはあといくつ 残 っていて などという 項 目 は 時 々 確 認 したくなります そのためのコマン ドです このコマンドを 実 行 すると 画 面 に 次 のようなメッセージが 表 示 されます 報 告 課 題 (2) 保 存 していないパラメータもあります 武 器 や 防 具 を 買 っても その 情 報 は 保 存 されないので ゲームを 終 了 して 読 み 込 むと また 何 もない 状 態 から 始 まります また ゴールドや ユンケルも 保 存 されないために 初 期 値 に リセットされます (ゴールドは 100 ドリンクは 1 になります ) この 情 報 を 保 存 するためには プログラムをどう 修 正 したらいいでしょうか? プログラムが 書 かれていて 動 作 の 検 証 方 法 の 報 告 があって プログラムの 説 明 があって 配 点 5 点 とします 11
文 法 についての 説 明 は 以 上 で 終 わりです 報 告 課 題 (3) 二 次 元 配 列 に 関 する 課 題 です こうしたゲームでは シナリオによって 様 々な ステージ や モンスター アイテム を 登 場 させています ステージを 増 やす 際 には モンスターを 増 やす 際 には また アイテムを 増 やすためには どんなプログラム 上 の 注 意 が 必 要 か プログラム 構 造 を 読 んで 注 意 点 や 方 法 をまとめ 可 能 ならば 実 際 に ステージや モンスター アイテムなどの 追 加 を 行 って 動 作 検 証 まで 行 って ください (モンスター ステージ アイテムそれぞれの 追 加 ごとに 最 大 3 点 まで 加 点 します ) NUM_MONSTERS など 配 列 のサイズを 指 定 している 部 分 に 注 目 し 配 列 の 個 々のデータの 設 定 を 矛 盾 がないように 設 定 する というこ とが 大 切 になります 手 順 としては 変 数 名 で 検 索 し 使 われている 場 所 の 行 番 号 を 控 え 追 加 に 漏 れ がないように 書 いて 下 さい 漏 れがあった 場 合 には 減 点 しますが 挑 戦 していたら1 点 は 出 します 発 展 課 題 プログラム 構 造 の 変 化 を 伴 う 改 造 を 行 ってください (モンスターの 名 前 を 変 えただけ ヒットポイントや 経 験 値 ゴールドの 値 アイテムの 値 段 などの 数 字 を 変 えただけ というのは ダメです ) この 部 分 をこう 変 えたら 宿 屋 に 宿 泊 して ヒットポイントが 全 回 復 できる ようになった とか 全 面 的 にこういう 改 造 をしたら 魔 法 攻 撃 ができるよ うになった とか ある 局 面 では 一 定 の 確 率 で 武 器 を 紛 失 することになっ た とか プログラムの 構 造 ( 論 理 )の 改 変 を 伴 う 改 造 を 行 ってください こうしたいと 思 った プログラムをこう 変 更 したら こうなった それは こうやって 検 証 した この 三 つを 明 らかにして 報 告 してください 難 易 度 に 応 じて 加 点 を 行 います 12
前 回 に RPG のゲームを 素 材 とした 時 と 同 様 に 今 回 も 多 くの 発 展 形 が 考 え られます 長 文 読 解 がベースですが このプログラムを 普 通 に 読 める ようになったら あとは 文 法 書 で 確 認 しながら どんなプログラムでもどんどんと 読 んでいける でしょう そうしたら こんなことをやってみたい という 自 分 で 見 つけたテーマをプロ グラムするのにいつでもできるようになります 入 門 編 の 卒 業 試 験 だと 思 って 取 り 組 んでみてください 頑 張 った 人 には それだけの 実 力 がついていますので 自 信 を 持 っ てください 来 週 から アルゴリズム を 中 心 として プログラミングの 考 え 方 の 方 向 に 流 れを 変 えて 行 きます なお 次 回 からの 教 材 では プログラムのサンプル を 一 切 配 布 しません ま た 配 布 教 材 にもヒントは 書 いてあってもプログラム 自 体 はほとんど 書 きませ ん この 先 は ごく 普 通 の プログラミング 演 習 になります 内 容 的 には 逆 に 退 屈 になるかも 知 れませんが 基 礎 体 力 がついていますので 丁 寧 に 行 えば 確 実 に 課 題 報 告 ができると 思 います これまで どんな 処 理 をする 時 に どんなプログラムが 書 かれてい たか? を 思 い 出 してみてください 13