class: title, smokescreen, shelf, no-footer # C言語入学式<br>モグラ叩き <div class=footnote> <small> </small> </div> --- class: compact # 変数とデジタル表現 <div class=footnote> <small> </small> </div> - 今回は、モグラ叩きです - モグラ叩きなので配列を利用します - モグラがいるところを1、いないところは0と決めます(ここは人為的) | 表現 | モグラ | 備考 | |------|------------|--------------------------------| | 0 | いない | C言語では0から数え始める習わし | | 1 | いる | |  | INDEX | 0 | 1 | 2 | 3 | |----------------|---|---|---|---| | モグラがいる? | 1 | 0 | 0 | 1 | --- class: compact # 配列 <div class=footnote> <small> (脚注) 配列を英語で array と言いますの </small> </div>  - たとえば配列として変数arrayを宣言した場合、たんに**同じ形の箱が連なっている**状態 - 変数の代入や読み出しの対象、 たとえばジャンケンの`jibun`や`aite`に相当するのは`array[1]`といった一つの箱の部分になります - `array[数字]` ... 数字で「何番目の箱か」を指定しています - C言語は0から数える習わしであることを思い出して! - **変数として常に`array[数字]`を使えばよい**、それだけです - この変数群には一連のシリーズを代入していくので、**たいていは繰り返し文とセット**で使うことが多いです。 そのため、繰り返し文の修行が足りないと思うなら、まずは、もっと繰り返し文に慣れましょう --- class: title, smokescreen, shelf, no-footer # C言語入学式<br>モグラ叩き(1次元,乱数なし) --- class: col-2,compact # 課題310 <div class=footnote> <small> (脚注) 繰り返しを使わないバージョンの気持ちです。面倒なら次の320へすすんでください:-) </small> </div>  - 穴が4つあるとします。 穴の下にモグラが「いる」「いない」「いない」「いる」状態を表現してください - 繰り返しを使わず、ベタに代入を4回、出力を4回書いてみましょうか (面倒なら繰り返し文を使ってもよいです:-) <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (2) 整数変数の配列 mogura を宣言 // (3) 配列に値を代入: いる、いない、いる、いない // (4) 出力: 変数の値をすべて出力してください return(0); } ``` --- class: col-2,compact # 課題320 <div class=footnote> <small> ポイント: 配列の宣言と代入(を一気に行う) </small> </div> - 4回も代入するのは面倒ですよね?いまは4回だから、ベタ書きでもいいですが、これが100回とかになると、もう... - この課題320では - 1回(つまり1行)で、配列の宣言と代入を行ってください - 出力で、ベタに出力を4回書くか?繰り返し文を使うか?そこは任せます:-) <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (2) 整数変数の配列 mogura の宣言と代入 // モグラは「いる」「いない」「いない」「いる」 // (3) 出力: 変数の値をすべて出力してください return(0); } ``` --- class: col-2,compact # 課題330 <div class=footnote> <small> ポイント: 条件文 (脚注) キーボードからの入力わかるよ!という人は飛ばして次の340へ進んでok </small> </div> - モグラ叩きの準備です。 次の問題340で、ユーザが「モグラの位置」をキーボードから入れるとatariかhazureを返すようにします - その前準備として、ここでは変数jibun(ユーザが予想した「モグラの位置」を代入する変数)を用意し、 値1を埋めこんでください - そこ(jibun)にモグラがいたら atari、いなければ hazure と表示してください (hazureと表示されるはずです) <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (2) 整数変数の配列 mogura の宣言と代入 // モグラは「いる」「いない」「いない」「いる」 // (3) 代入: jibun に 1 を代入 // (4) 条件文と出力: jibun の場所にモグラがいるか? // いれば atari、いなければ hazure と出力 return(0); } ``` --- class: col-2,compact # 課題340 <div class=footnote> <small> ポイント: 入力 </small> </div> - もっとインタラクティブなモグラたたきをしましょう - 課題330を改造し、 ユーザが予想する「モグラの位置」をキーボードから読み込んで変数jibunに代入してください <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (2) 整数変数の配列 mogura の宣言と代入 // モグラは「いる」「いない」「いない」「いる」 // (3) 入力: キーボードからjibunに値を読み込む // (4) 条件文と出力 // jibun の場所にモグラがいるか? // いれば atari、いなければ hazure と出力 return(0); } ``` --- class: col-2,compact # 課題390 <div class=footnote> <small> ポイント: 入力 </small> </div> - モグラ叩き1次元ひととおり完成です (このあと、もうすこしゲームっぽくしていきます) - 課題340を改造しキーボードから3回挑戦できるようにしてください (「モグラの位置」を3回入力できるようにする) - 課題340の(1)〜(4)のうち、1回だけ実行する部分と繰り返す部分がどこか考えましょう <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (A) 一回だけ実行する部分 for ... 省略 ... { // 繰り返し文 // (B) くりかえし実行する部分 } return(0); } ``` --- class: title, smokescreen, shelf, no-footer # C言語入学式<br>モグラ叩き(1次元,乱数あり) <div class=footnote> <small> 乱数を使い、よりゲームらしくしましょう </small> </div> --- class: col-2,compact # 課題410 <div class=footnote> <small> ポイント: 乱数 </small> </div> - 課題390を改造して、 コンピュータにモグラの位置を決めさせてください - ヒント: どういう式を書けば、 乱数でモグラの「いる」「いない」を決めることができるでしょうか? (ジャンケンの理屈を少しひねれば出来そうじゃない?) <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (2) 整数変数の配列 mogura の宣言 // (3) モグラの位置を乱数で決める // (B) 繰り返し // (4) 入力: キーボードからjibunに値を読み込む // (5) 条件文と出力 // jibun の場所にモグラがいるか? // いれば atari、いなければ hazure と出力 return(0); } ``` --- class: col-2,compact # 課題420 <div class=footnote> <small> </small> </div> - 課題410を元に、最後に「点数」(= atari の回数)を表示してください - 整数変数 tensuu を使ってください <wbr> ``` // (1) 最初の行に必ず書く呪文 int main () { // (2) 整数の変数tensuuと配列moguraを宣言 // (3) モグラの位置を乱数で決める // (B) 繰り返し // (4) 入力: キーボードからjibunに値を読み込む // (5) 条件文と出力: // jibun の場所にモグラがいるか? // いれば atari、いなければ hazure と出力 // atari の時は tensuu に加算 // (6) tensuu を表示 return(0); } ``` --- class: col-2,compact # 課題430 <div class=footnote> <small> ポイント: 定数 </small> </div> - 4とか3とか数字が色いろ出てきてソースコードが読みにくいですね - 定数を使い、課題420を綺麗にしてください。 モグラ配列の大きさは YOKO, くりかえす回数の 3 は SAIDAI という定数にしましょう - また、モグラ配列を初期化する繰り返し文では i ではなく yoko という変数名を使ってください <wbr> ``` // (1) 最初の行に必ず書く呪文 // 定数 ... 以下、420と同様 ... ``` --- class: col-2,compact # 課題490 <div class=footnote> <small> ポイント: アルゴリズム <br> (脚注) ぱっと考えつく案として (1)浮動小数点が必要なコード (2)整数だけで書けるコードの両方がある </small> </div> - 一次元モグラの総合課題です。改造テーマが2つあります - (a) 配列サイズを 9 にしてください - (b) モグラのいる割合を3割未満にしてください - 課題430がうまく出来ていれば(a)は簡単ですが、問題は(b)ですね。 これは、ジャンケンよりも少し複雑なコードが必要です (ヒント: 10面サイコロをふって目が0 1 2なら3割になるよね?) <wbr> ``` // (1) ヘッダファイル // 定数 int main () { // (2) 宣言 // (3) モグラの位置決め(乱数) // (B) 繰り返し // (4) 入力 // (5) 条件文と出力 // (6) tensuu を表示 return(0); } ``` --- class: title, smokescreen, shelf, no-footer # C言語入学式<br>モグラ叩き(2次元,乱数あり) <div class=footnote> <small> 2次元にすると、もっとモグラ叩きっぽくなります </small> </div> --- class: col-2,compact # 2次元配列 - 1次元配列は箱が横に並んでいます ``` a[0] a[1] a[2] ```` - 2次元配列はマス目つまりスプレッドシート(例:Excel)状で、 これに縦横の場所を指定する数字がつくというだけのことです ``` a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] a[2][0] a[2][1] a[2][2] ``` <wbr> - Excelのマス目はアルファベットと数字ですけど、 たてよこ両方とも数字だって慣れられそうですよね? ``` A1 B1 C1 A2 B2 C2 A3 B3 C3 ``` --- class: col-2,compact # 課題510 <div class=footnote> <small> </small> </div> - 課題490を参考に2次元モグラを作ってください。 2次元なので、もちろんユーザは毎回2つ数字を入れます - あとは、ノーヒントでトライ <wbr> ``` [実行例] $ ./mogura 1 2 atari 2 3 hazure 6 8 atari tensuu = 2 ``` --- class: col-2,compact # 課題520 - 分かりやすいように、モグラたたきの現状を二次元のマス目で表示するようにしてください - O ... atari - X ... hazure - . は、まだ叩いていない場所 <wbr> ``` [実行例 (9x9)] 7 8 atari! 1 2 3 4 5 6 7 8 9 1 . X . . . . . . . 1 2 . X . . . . . X . 2 3 . . X X X . . . . 3 4 . . . . . . . . . 4 5 . . . X . X . X . 5 6 . . . . . . . . . 6 7 . . . . . . . O . 7 8 . . . . . . . . . 8 9 . . . . . . . . . 9 1 2 3 4 5 6 7 8 9 ``` --- class: col-2,compact # 課題530 - 入力の指定を 0 スタートではなく 1〜9 で指定できるようにしてください <br> (実は前頁の図が、すでに 0 スタートでなかったね?:-) - たとえば今までモグラの位置を 0 1 などと指定していたところが 1 2 になります - どうすると最小の変更で実現できる? <wbr> ``` [実行例 (9x9)] 7 8 atari! 1 2 3 4 5 6 7 8 9 1 . X . . . . . . . 1 2 . X . . . . . X . 2 3 . . X X X . . . . 3 4 . . . . . . . . . 4 5 . . . X . X . X . 5 6 . . . . . . . . . 6 7 . . . . . . . O . 7 8 . . . . . . . . . 8 9 . . . . . . . . . 9 1 2 3 4 5 6 7 8 9 ``` --- class: title, smokescreen, shelf, no-footer # モグラ叩き<br>発展課題 --- class: compact # 発展課題INDEX <div class=footnote> <small> (脚注) <A HREF="https://pacman.com/jp/">パックマン公式(ナムコ,1980-)</A>、 <A HREF="http://www.hyperware.co.jp/software/heian/default.htm">平安京エイリアン公式(TSG,1979-)</A> </small> </div> - 0810: モグラ叩きを何回できるか?指定できるようにしてください - 0820: モグラ叩きの回数に上限なし。数字の9を入れたら終了 - 0830: [やや難] 回数制限なし。文字の q を入力したら終了にしてください - 0840: モグラには下っ端とボスがいます。下っ端とボスでは異なるアタリの点数にしてください。 たとえば下っ端は1点ですがボスは10点。点数の種類も増やしてみましょう - 0850: モグラ叩きの履歴(入力履歴と統計)をとる - 0860: [やや難]履歴をファイルに保存。次回以降、それを読み出し、総履歴を表示 - 0870: [難]履歴を分析し、ユーザが叩かない場所にモグラを出現させてください - 0880: [難]ゲームを進めるとモグラも動いていく。 実装よりルールの設計が難しい気がします。 例:どれくらい移動できる?すでに叩いた場所を越えて移動できる? - 0890: [難]上記の課題を応用して、往年の有名なゲーム「パックマン」や「平安京エイリアン」似のゲームも考えてみよう。ルールの簡略化など設計はおまかせです --- class: compact # 課題0810: モグラ叩きの回数を指定できる - 以下、課題490(1次元)もしくは510(2次元)を元に改造する想定です - (A.1)変数宣言,新変数jougenを追加 - (A.2)モグラの位置を初期化 - (A.3)キーボードからjougenに値を代入 - 繰り返しの回数はjougen回です - (B.数字)と(C.数字)は変わりません --- class: compact # 課題0820: モグラ叩きの回数制限なし,9で終了 - くりかえす部分だけの変更です - 繰り返しを無限ループにする - (B.1)部分に追加 - キーボードから叩く場所を読みこむ - 数字が9の場合ループを終了する <br> (注: 2次元モグラの場合tate,yokoいずれかが9) - (A.x)と(C.x)は変更なし --- class: compact # 課題0830: [やや難] モグラ叩きの回数制限なし,qで終了 <div class=footnote> <small> (脚注) gerchar()を使う「入力を文字としてあつかう」別解あり </small> </div> - 前頁の課題0820とほぼ同じですが(B.1)のところが抜本的に異なります。 基本戦略を変更し「いちど文字列を読みこみ、文字列判定、数字へ変換」です。 こうすれば、ここ以外は前問と同じままになります - (B.1)の前で文字列str_tateとstr_yokoを宣言(長さは最低2) - (B.1)を、こんなかんじに作り直してください - キーボードから文字列として「たて」「よこ」の情報を読みこみます - 文字列でqかどうかを比較し、qならループを終了 - 文字列を数字へ変換する(`atoi`という関数を使うと変換できます) ``` 数字 = atoi(文字列); ``` --- class: compact # 課題0840: 下っ端モグラとボスモグラ <div class=footnote> <small> (脚注1) モグラの点数を記録する2次元配列 tensuu を用意して加算時に使う別解あり <br> (脚注2) 100面サイコロで0 1 2はボス、3〜29は下っ端モグラと考えます。 種類を増やすのは簡単ですよね? </small> </div> 問題: モグラには下っ端とボスがいます。下っ端とボスでは異なるアタリの点数にしてください。 たとえば下っ端は1点ですがボスは10点。点数の種類も増やしてみましょう - 前問0830を改造元に使います。それとの差分を以下に書いておきます - (A.2)モグラの初期化 - ここでは3%程度ボスモグラがいるとします - 100面サイコロをふってください。 目が3未満のときボスなのでmogura配列には点数10を設定します。 目が30未満ならmogura配列に点数1を、それ以外では0を設定 - (B.1)〜(B.3)は変わりません <br> (B.1)キーボードから入力(B.2)qならループ終了(B.3)数値へ変換してtate,yokoへ代入 - (B.4)判定 - `mogura[tate][yoko]`の値が1以上なら種類はともかくモグラがいます - `tenshuu += mogura[tate][yoko]`つまりmogura配列の点数を加算していきます --- class: compact # 課題0850: モグラ叩きの履歴をとる(1) [設計編] - このあとの課題で使うことを考えて履歴をとる仕組みを準備したいわけですが、 何の履歴をとると利用価値が高いか?その判断が難しいですね;-) - (a)入力履歴と(b)ユーザが入力する数字の統計を取りましょうか - (a)入力履歴とは、ユーザが入力した生内容の記録 ``` tate 1 3 5 ... yoko 2 4 9 ... ``` - (b)は変数 tate yoko を問わず、ユーザが入力する数字0〜9の統計 - (a)の応用は難しいですが、このあとの課題で(a)を使うかもしれません - (b)は「ユーザは7が好きで多めに入力してくるらしい -> 7を避けてモグラを配置する」といった具合に使えるでしょう - 将来拡張を想定すると、データ構造は配列が便利ですかね?(次頁以降も参照) - 例: 整数配列 rireki_tate[N] rireki_yoko[N] nyuuryoku_toukei[10] (Nは大きな数字) --- class: compact # 課題0850: モグラ叩きの履歴をとる(2) [設計編] <div class=footnote> <small> (脚注) fscanfのfpは、stream名としての定番「ファイルへのポインタ(file pointer)」の略 </small> </div> - 最初から、 のちに、ファイルへ(a)生の入力履歴を保存する時どうなっていたら便利か?を考えておきます。 といっても**決めごと**なので、 「ファイルには数字だけ書きこむ。1列目がtate、2列目がyoko」だけ決まっていれば十分だよね? ``` 1 2 3 4 5 9 ... ``` - これを読みこむときは、すなおに ``` fscanf(fp, "%d %d", &rireki_tate[i], &rireki_yoko[i]) ``` などとすればok(ここで i は`ファイルの行番号 -1`,識別できればなんでもよいです) --- class: compact # 課題0850: モグラ叩きの履歴をとる(3) [設計編] <div class=footnote> <small> (脚注) fscanfのfpは、stream名としての定番「ファイルへのポインタ(file pointer)」の略 </small> </div> - (b)のほうは簡単で、 0~9の入力回数を、そのまま縦に出力しておきます。 横方向ではないです。ループで読みやすいように縦方向。 ``` 1 3 2 ... ``` - これを読みこむときは、すなおに ``` for (me = 0; me < 9; me++) { fscanf(fp, "%d %d", &nyuuryoku_toukei[me]); } ``` などとすればok(ここで me は0〜9で、ユーザの入れる数字です) --- class: compact # 課題0850: モグラ叩きの履歴をとる(4) [実装編] - mainの前に、定数RIREKIを宣言(大きな数) - (A.1)mogura配列を宣言 - (A.2)履歴を取るための変数群を宣言(new): rireki_tate[RIREKI] rireki_yoko[RIREKI] nyuuryoku_toukei[10] あと履歴の合計行数を数える rireki_goukei も必要です - (A.3)乱数でmoguraの位置を決めます。前問と同じです - (B.1)叩く位置を入力(B.2)qなら終了(B.3)文字列を数字へ変換 - (B.4)ここで履歴を取ります(new)。次の配列群を使います: rireki_tate rireki_yoko nyuuryoku_toukei - (B.5)あたり/はずれの判定(前問までと同じ) - (C.1)結果を出力(前問までと同じ) - (C.2)最初はデバッグとして、履歴がきちんと取れているか?出力してみるとよいです --- class: col-2,compact # 課題0850: モグラ叩きの履歴をとる(5) [実行例] ``` 3 2 hazue 4 3 hazue 1 2 hazue q q tensuu = 0 (右へつづく) ``` ``` [rireki] 3 2 4 3 1 2 [nyuuryoku toukei] 0 0 1 1 2 2 3 2 4 1 5 0 6 0 7 0 8 0 9 0 ``` --- class: compact # 課題0860: [やや難]履歴を保存、総履歴を表示 - 変数とファイルフォーマットは前問で検討したとおりです - ループが始まる前、 (ワークシートにはない)(A.4)で、ファイルから配列群に前回までの履歴を読みこみます。 変数群: rireki_tate rireki_yoko nyuuryoku_toukei - 配列の情報は前回までのすべての対戦履歴を反映したものになっています - ループで対戦した今回分の履歴も配列に追加されていきます - (C.1)では、今回の対戦結果表示に加え、履歴配列群のデータも表示しましょう - (C.2)では配列の内容をファイルに保存します。 保存しているデータは、すべての対戦履歴を反映したものです - [拡張(発展)] 勝敗結果もファイルに保存し、(C.1)で全対戦結果も表示してください --- class: compact # 課題0870: [難]履歴を分析し、モグラ出現場所を最適化(1) - 前問までで履歴の保存ができていて、分析する準備は整っています。 このあとはデータ分析になってしまうので、いきなり難易度があがりますが、 そのかわりネタはたくさんあると思うので、そこはおまかせします - 「配列nyuuryoku_toukeiを用いて叩かれにくく」する簡単な例だけを考えましょう - (A.3)を改造し (a)ファイルからnyuuryoku_toukeiの値を読みこんだ後 (b)moguraの位置を決めることにします - 配列 nyuuryoku_toukei を見て、 入力回数の最も多い数字ひとつだけを使います。 ここでは仮に7とし、これを変数kaihiに代入しておきます - mogura配列を決めるループの中で、 tateとkaihi、もしくはyokoとkaihiが同じ場合は0つまりモグラがいないようにします --- class: compact # 課題0870: [難]履歴を分析し、モグラ出現場所を最適化(2) <div class=footnote> <small> (脚注) 91*0.3/64 = 42.65 </small> </div> - 前問の改造方法は簡単ですが、 そのぶんモグラの出現確率が減るので、どこかで補正しないといけません - たとえば本来3割モグラがいるはずのゲームだとします。 「マス目が9x9で、7のところはモグラがいない(つねに0)」とすると、 全81ヶ所(81=9x9)のうち17ヶ所(17=8+9)にはモグラを配置しません。 - 残り64(=81-17)箇所のうち3割にモグラがいるようにしなければなりません - さて、どうすると実現できるでしょうか? --- class: compact # 課題0870: [難]履歴を分析し、モグラ出現場所を最適化(3) - 他にも色々かんがえられるでしょう。たとえば - 配列nyuuryoku_toukeiの数字で回数の多いものベスト3に対策してみる - 履歴を参考にユーザは、そろそろ次に叩きそうな位置を予測してみるとか? - ただ今のモグラ叩きは初期化後おなじところにいるので、 予測位置から逃げられませんね。 これは、もうモグラも動かすしかありません -> 次の課題へつづく --- class: compact # 課題0880: [難]ゲームを進めるとモグラも動いていく(1) - 改造案その1は簡単にできます。 くりかえしの中でモグラの位置を再初期化してしまいます。 たとえば(シートにないけど)(B.0)でmoguraを再初期化します。 - 注意: 乱数の初期化は一回だけするものです。 (A.2)で`srandom(time(NULL))`を実行するコードは、そのまま! - 毎回うごいちゃうの?というのもありますが、 すでに叩いたことのある場所にモグラが移動したときは? それはどういうあつかいになるの?と考えると難しいです --- class: compact # 課題0880: [難]ゲームを進めるとモグラも動いていく(2) <div class=footnote> <small> (脚注) ここまで来たら、 <A HREF="https://pacman.com/jp/">パックマン公式(ナムコ,1980-)</A>、 <A HREF="http://www.hyperware.co.jp/software/heian/default.htm">平安京エイリアン公式(TSG,1979-)</A> も書けそうです。 <br> もちろん見栄えはイマイチですが自分で作れると面白いとおもいますよ (見栄えはその手の人にまかせよう) </small> </div> - まず、どういう移動ルールなのかを設計してください。 たぶん、これが難しい。 ルール次第で実装の難易度がずいぶん変わるでしょう - 移動ルールの例。もう単純な再初期化は出来ません。 けっこう長いコードになります - 移動は今いる位置の前後左右のみ - すでに叩かれた場所を避けて移動しなければならない - 動き方にバリエーションあり(将棋か?) <br> 下っ端は上下左右のみ、 ボスは8方向すべて可 - ボスは叩かれた場所の下を掘って迂回できるので2つ先までなら行けるとかもアリ? - 逆にユーザにヒントが出るのもいいよね? - モグラが叩かれた場所を迂回したときは穴が崩れるから、 この穴の周辺はモグラがいるっぽいと教えてくれるとか - フィーバータイムがある? 時間? それより、 ボスが一人倒されたらヒントが出るフィーバータイム突入とかのほうがいいかも?