* 第2回 配列 [#s1a51c2a]
* 第2回 繰り返し [#q4cbb2d0]

- ''基本課題''は授業時間中に終わらせること。
- ★印の付いている問題は,当教員に見せて確認の印(またはサイン)を得ること。
- 授業時間中に終わらなかった''応用課題''は,次回(2週間後)までの宿題とする。

- [[C言語のコンパイルと実行のしかた>../C言語環境]]
-- プログラムのファイル名は,「問題番号.c」としてください。例:a1.c, ax3.c
- [[プログラムの提出のしかた>../提出]]
-- 提出URL http://vilab.org/upload/clab-upload.html
- [[C言語おすすめ情報>../C言語参考資料]]
-- 参考になるWebページや書籍の紹介。

** 基本課題 [#s5a7364f]
** 基本課題 [#b6822362]

*** 配列 [#uc9fe2a9]
*** 繰り返し [#r7ea239c]

''b1)'' 4つの要素を持つdouble型配列にキーボードから数値列を読み込み,合計と平均を求めるプログラムを作成しなさい.
>関連項目: for, while, break

>【ポイント】 たとえばint a[N];と宣言した配列は、a[0]からa[N-1]までのN個の要素を持つ。a[N]は存在しないので注意。intでなくても同じ。
 int a[10];
 a[10] = 3;  /* 誤り!! */
上記は誤りだが、通常Cコンパイラはこれを検出しない。しかし、実行時に意味不明の挙動を示す原因となるので決してしてはならない。
''b1)'' 0以上100未満の整数を,小さい順(昇順)に画面に表示するプログラムを作成しなさい.

''★b2)'' キーボードから10個の整数を読み込み,最後に入力とは逆の順番に表示して終了するプログラムを作成しなさい。ループ(forまたはwhile)を使うこと。
''★b2)'' 10個の整数をキーボードから読み込み,奇数と偶数の数を数えて出力するプログラムを作成しなさい(繰り返しを使うこと)。

''★b3)'' キーボードから10個の整数を読み込んだ後、平均以上の数値と平均未満の数値を''別々に分けて''表示する(つまり,最初に平均以上の数値だけを並べて表示し,次に平均未満の数値だけを並べて表示する)プログラムを作りなさい。
''b3)'' 2つの整数aとbを読み込み,a以上b以下の整数を''大きい順(降順)''に画面に表示するプログラムをforを用いて作成しなさい。もし,aがbより大きかったら,aとbを交換してから処理しなさい。

*** 文字列 [#w9378fb3]
''★b4)'' キーボードから自然数を順次読み込み、0を読み込んだら、それまでの最大値を表示して終了するプログラムを作成しなさい。

''b4)'' scanfを用いて,自分の出身地(都道府県など)をローマ字で読み込み,表示するプログラムを作成しなさい。
''★b5)'' 掛け算の九九の表を出力するプログラムを作成しなさい。余裕のあるひとは左下または右上半分だけ出力するプログラムを作成しなさい。以下に例を示す。(2重ループを使うこと)
|~\|~1|~2|~3|~4|~5|~6|~7|~8|~9|
|~1|1|||||||||
|~2|2|4||||||||
|~3|3|6|9|||||||
|~4|4|8|12|16||||||
|~5|5|10|15|20|25|||||
|~6|6|12|18|24|30|36||||
|~7|7|14|21|28|35|42|49|||
|~8|8|16|24|32|40|48|56|64||
|~9|9|18|27|36|45|54|63|72|81|

>【ポイント】 文字列の文字配列への読み込みには、%sを用いる。このとき,読み込める最大文字数(配列のサイズ−1)を指定するべきである。
 char str[128];
 /* 読み込める長さを指定しないやり方 */
 scanf("%s", str);
 /* 読み込める長さを指定したもっと好ましいやり方 */     
 scanf("%127s", str);

''★b5)'' 文字型の配列を自分の名前のローマ字で初期化し(変数の宣言時に代入し)、その配列中の文字の文字コード(文字番号)を縦に並べて1文字分づつ画面に表示するプログラムを作成しなさい。
** 応用課題 [#nffbf184]

>【ポイント】 文字列の最後は、ヌル文字('\0': 終端文字)で終わる。よって、文字列を格納するためには、その文字列の長さ+1文字分の領域(charの配列)が必要である。
 char str[] = "Tamagawa";
の場合、下記のように9文字分の領域を取る。
          0   1   2   3   4   5   6   7   8
 str[]  ['T'|'a'|'m'|'a'|'g'|'a'|'w'|'a'|'\0']
''bx1)'' 2つの整数aとbを読み込み,aからbまでの和a + (a+1) + … + (b-1) + b を計算して出力するプログラムをfor文を用いて作成しなさい。ただし,aがbより大きい場合には,aとbを交換して計算しなさい。

>また,C言語における1文字の実態は整数の文字コード(要するに「文字番号」)である。通常使われるASCIIコードでは,A〜Z,a〜zに,それぞれ連続した番号が割り当てられている。例えば上記の例を文字コードで示すと以下のようになる。
          0   1   2   3   4   5   6   7   8
 str[]  [ 84| 97|109| 97|103| 97|119| 97| 0 ]
''bx2)'' 正の整数を順次キーボードから読み込んで合計を計算し,ゼロまたは負数が入力された時点で結果を表示して終了するプログラムを作成しなさい。このとき,正数のみの合計を表示して終了すること。

** 応用課題 [#p151e608]
''bx3)'' 複利の年利x%で毎月利息がつく銀行預金に,毎月y円ずつ積み立てる。たとえば,x=12%(1月あたり1%), y=1000円なら,積み立てを始めてから1ヵ月後には1000×1.01+1000=2010円,2ヵ月後に2010×1.01+1000=3030円になっている計算である(小数点以下は切り捨て)。では,何年何ヶ月後に100万円を越えるか計算するプログラムを作成しなさい。
- 日本では慣習および法律で,複利の場合も「月利=年利÷12」と決まっている。

''bx1)'' キーボードから、double型の配列v[2]およびw[2]を読み込み、これらを2次元ベクトルと考えて、和と内積を表示するプログラムを作成しなさい。
- 参考(ベクトルの加算) http://shigihara.hp.infoseek.co.jp/vect12.htm
- 参考(ベクトルの内積) http://shigihara.hp.infoseek.co.jp/vect16.htm
''bx4)'' キーボードから読み込みこんだ整数n (ただしn≧2)を1辺の長さとするアスタリスク(*)の正方形を描画するC言語のプログラムを作成せよ。たとえば、n=2,3,4のときは、それぞれ以下のような図形が出力される。
     **    ***    ****
     **    * *    *  *
           ***    *  *
                  ****

*** 線形探索法 [#g5c27069]
''bx5)'' 2+3+5のように,合計が10になるような3つの整数の組合せを列挙するプログラムを作成しなさい。

''bx2)'' キーボードから、10個の整数を要素とする配列aと、整数xを読み込み、xがaに含まれているかどうか,aの先頭から順に調べて結果を表示するプログラムを作成しなさい。
       i→                      →|    iを0から9まで変化
       0  1  2  3  4  5  6  7  8  9
 a[]  [__|__|__|__|__|__|__|__|__|__]
 
           a[i]とxを比較
** 発展課題 [#o4a1d004]

''bx3)'' キーボードからアルファベットからなる文字列を読み込み,先頭文字と同じ文字の個数を数えるプログラムを作成しなさい。たとえば,「application」ならばaが2個あるので2が結果となる。
*** RPGの戦闘プログラム(続き) [#mac0825a]

*** ROT13暗号 [#l809c884]
この課題は,配列の知識が必要である。

''bx4)'' ROT13暗号とは,欧文に含まれる文字を1文字ずつ,下記の図に示すようにアルファベット順で13文字後ろの文字に“ずらす”ことによって,文章を暗号化するものである。
 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G ...
 A→→→→→→→→→→→→ N
               H→→→→→→→→→→→→ U
                           N→→→→→→→→→→→→ A
                                 Q→→→→→→→→→→→→ D
Aの13文字後はN,HはU,NはA,QはDへと変換され,例えば「HELLO」は「URYYB」と暗号化される。すると,「ABCDEFGHIJKLM」の各文字は「NOPQRSTUVWXYZ」の各文字と''交換''されることがわかる(小文字は小文字同士で交換)。
''bz1)'' ''az1)''の戦闘プログラムを改良して,複数対複数で戦えるようにしなさい。とはいっても,いきなり“乱戦”を実現するのは難しいので,柔道の団体戦のように1人ずつ順番に対戦する勝ち抜き戦を実現しなさい。

キーボードから読み込んだ文字列を,ROT13暗号によって暗号化するプログラムを作成しなさい。また,暗号化された文字列を復号化(元に戻すこと)して,ちゃんと暗号化されていたことを確認しなさい。

>【ポイント】 スペースを含む文字列を1行読み込む関数にはgets()があるが,近年ではこれは使わないほうがよい。かわりに,scanf()の書式 %[^\n] を用いるとよい。
 char str[128];
 scanf("%127[^\n]", str);

*** スタックの実装 [#ab262f77]

''bx5)'' スタックとは、''先入れ後出し方式''のデータ構造である。データを上に積み重ねていくような形式のデータ構造であり、一番最後に積んだデータを一番最初に取り出すことができる。一番最初に積んだデータは、後から積んだデータを全部取り出さないと、取り出すことができない。
以下のような動作をするプログラムを作成しなさい。
+ スタックとスタックポインタを表す変数を用意する。
-- int stack[100]; 100個の整数を保存しておくための配列。
-- int sp = 0; スタックの中の''次の空き''を示すための変数(初期値はゼロ)。
+ キーボードから整数xを読み込む。
+ xの値を判定し:
-- x>0ならば、xをstackに積んで2.に戻る(push)。言い換えると、stack[sp]の位置にxを代入し、spを1増やす。
                       ● xを積む(push)

        0  1  2  3  4  5  6  7  8  9
 stack [●|●|●|●|●|__|__|__|__|__|....

                       次の空き(sp)
 
        0  1  2  3  4  5  6  7  8  9
 stack [●|●|●|●|●|●|__|__|__|__|....

                          次の空き(1増えたsp)
-- x<0ならば、stackに最後に積んだ値を表示して、それをstackから取り除き、2.に戻る(pop)。言い換えると、spを1減らし、stack[sp]の位置から値を取り出す。
                          次の空き(sp)

        0  1  2  3  4  5  6  7  8  9
 stack [●|●|●|●|●|●|__|__|__|__|....

                       取り出す(pop)
 
                       次の空き(1減ったsp)

        0  1  2  3  4  5  6  7  8  9
 stack [●|●|●|●|●|__|__|__|__|__|....


-- x=0ならば、stackの中身を逆順にすべて表示して、プログラムを終了する。言い換えると、stack[sp-1]からstack[0]までを表示する。
+ 2.に戻って繰り返す。

なお、スタックにデータをしまう操作をpushといい、データを取り出す操作をpopという。また、スタックの中の次の空き位置(または現在の最後の位置)を保存する変数spをスタックポインタという。

** 発展課題 [#i1c81cf5]

以下の''bz1)''と''bz2)''の好きなほうを解きなさい。もちろん両方解いてもよい。

*** ブラックジャック [#t5869ee0]

''bz1)'' トランプゲームのブラックジャック(簡略版)を作成したい。以下の手順で動作するプログラムを作成しなさい。

''準備''
+ 以下のような変数を用意しておくとよい。
 int cards[52];   /* トランプのカード52枚を格納する配列 */
 int player[10];  /* プレイヤーの手札 */
 int dealer[10];  /* コンピュータの手札 */
 int n_drawn;     /* 既に束から引いたカードの枚数 */
 int n_player;    /* プレイヤーの手札の枚数 */
 int n_dealer;    /* コンピュータの手札の枚数 */
+ 配列cards[]に,スペードの「A」からクラブの「K」までの全カードを1枚ずつ格納させる。しかし,このブラックジャックではカードのスーツ(スペード等)は関係ないので,1から13までの整数を4個ずつ格納すればよい。
 n = 0;
 for (i = 1; i <= 13; i++) {
     cards[n++] = i;
     cards[n++] = i;
     cards[n++] = i;
     cards[n++] = i;
 }
+ カードの束をよくシャッフルする。よくある方法では,配列card[]の要素からランダム2枚を選び交換する作業を適当な回数(以下の例では300回)繰り返す。なお,rand()を使う場合には''az1)''を参考にして発生前に乱数をかき混ぜておく。
 /* rand()使用前に1度だけ実行 */
 srand((unsigned) time(NULL))
 
 for (i = 0; i < 300; i++) {
     n = rand() % 52;
     m = rand() % 52;
     temp = cards[n];
     cards[n] = cards[m];
     cards[m] = temp;
 }

''ゲーム''
+ 先頭のカードを2枚取り出し,コンピュータの手札とする。このうち1枚目だけを画面に表示する。
-- 配列cards[]の先頭の整数2つを,配列dealer[]にコピーすればよい。
-- 変数n_drawnとn_dealerも適切な値になるように。
+ 同様に,次のカードを2枚取り出し,プレイヤーの手札とする。これは両方とも画面に表示する。
-- 次の整数2つを,配列player[]にコピーすればよい。
-- 変数n_drawnとn_playerも適切な値になるように。
+ プレイヤーはカードの数字の合計が21に近くなるように,カードを1枚ずつ引くことができる。
-- 例えば「次のカードを引きますか?(Y/N)」という表示を出して,「Y」である間カードを引かせて配列player[]に追加していく。
-- 「J」「Q」「K」は,10として計算する。「A」は1または11の好きなほうで数えてよい。
-- カードの合計が21を超えてしまった場合は,その時点で負けである。
+ プレイヤーが終わったら,コンピュータの番である。
-- コンピュータは,カードの数字の合計が16以下だった場合には1枚ずつ引いて手札に加える。
-- もし,17以上になったらそのカードで引くのを終わりにする。
-- このとき「A」が含まれていたら,まず11として計算して17以上21以下ならそこで終わりとし,そうでなければ1として計算することにする。
-- プレイヤーと同様に21を超えてしまったら,その時点で負けになる。
+ 最後に,プレイヤーとコンピュータを比較して,21を超えずに21に近いほうが勝ちとなる。

*** RPGの戦闘プログラム(続き) [#t864cccf]

''bz2)'' ''az1)''の戦闘プログラムを改良して,複数対複数で戦えるようにしなさい。とはいっても,いきなり“乱戦”を実現するのは難しいので,柔道の団体戦のように1人ずつ順番に対戦する勝ち抜き戦を実現しなさい。

 ... 味方2 味方1 味方0 →×← モンスター0 モンスター1 モンスター2 ...

主人公たちは,狭い洞窟の通路を,たいまつを灯しながら1列になって進んでいる。そのとき,前方から咆哮が聞こえ,凶暴なモンスターのグループが襲ってきた!
+ 主人公側,モンスター側ともに,メンバーに0から番号を振っておく。これが対戦の順番になる。
-- 技術点や体力点を保存するのには配列を利用する。たとえば,味方の3人目の技術点はskill[2],体力点はstamina[2]などとする。
+ まず,主人公側の1人目とモンスター側の1匹目が対戦する。これは1対1なので''az1)''の方法で闘えばよい。
-- ここで,体力がゼロになるまで戦わず,ある値(例えば5)以下になったら列の一番後ろに退却することにすると,よりリアリティが増すかもしれない。
+ 勝った側は勝ち抜きとなり,次の相手とそのまま対戦する。負けた側は,交代に次のメンバーが前に進み出て闘う。
+ 以下同様に闘い,どちらかが全滅したら戦闘終了である。

前回同様,乱戦に対応させるなど,これも改良自由である。


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS