第3回 関数

基本課題

関数

★c1) 半径rを引数として,円周の長さを表示する関数を作成しなさい。その関数の動作を確認するプログラムを作成しなさい。以下にプロトタイプ宣言の例を示す。

void print_circle(double r);

【ポイント】 関数プロトタイプ宣言は,関数の型(使い方)をコンパイラに教えるためのもので,関数の定義(関数の中身を実際に書くこと)とは別である。関数を定義より前に使用する場合には,プロトタイプ宣言が必ず必要である。

/* プロトタイプ宣言:行末にセミコロンが必要 */
void print_circle(double r);

int main(void)
{
    /* 関数の使用例 */
    print_circle(10.0);
}

/* 関数の中身の定義:{}の中に実行内容を書く */
void print_circle(double r)
{
    /* 関数の実行内容 */
}

c2) 関数f(x)=x+1をC言語で表し、整数nをキーボードから読み込み、f(0)+f(1)+f(2)+...+f(n)の値を表示するプログラムを作成しなさい。

★c3) 2つの整数を引数として,値の小さい方を返す関数min2を作成しなさい。これを利用して,10個の整数を読み込み,その最小値を求めるプログラムを作成しなさい。

int min2(int n, int m);

ライブラリ関数

★c4) 座標平面上の点 (x, y) から原点までの距離を計算するC言語のプログラムを作成し,コンパイル・実行させたい.キーボードからdouble型の数値xおよびyを読み込み,距離を計算して画面に表示するプログラムを作成しなさい.平方根を計算するライブラリ関数sqrtのマニュアルには、以下のように記述されている。

#include <math.h>
double sqrt(double x);

【ポイント】 C言語の処理系(OS)によっては,数学関係の標準ライブラリを使ったプログラムをコンパイルするために数学ライブラリをリンクする必要がある。たとえば,gcc(UNIX)ではコンパイル時のオプションで-lmと指定する。

gcc -lm ファイル名.c

c5) 自分の名前(ローマ字)をキーボードから読み込み、標準ライブラリ関数toupperを用いて1文字ずつ大文字に変換した後、画面に表示するプログラムを作成しなさい。

#include <ctype.h>
int toupper(int c);

応用課題

cx1) 西暦を引数とし,その年の1月1日の元号が平成(0)・昭和(1)・大正(2)・明治(3)・それ以外(-1)のいずれであるかを返す関数を作成しなさい。その関数の動作を確認するプログラムを作成しなさい。以下にプロトタイプ宣言の例を示す。

int gengo(int seireki);

cx2) キーボードから読み込みこんだ整数n (ただしn≧2)を1辺の長さとするアスタリスク(*)の四角形を描画するC言語の関数を作成し,その関数の動作を確認するプログラムを作成しなさい。たとえば、n=2,3,4のときは、それぞれ以下のような図形が出力される。

n=2 **    n=3 ***    n=4 ****
    **        * *        *  *
              ***        *  *
                         ****

cx3) 標準ライブラリ関数rand()は,0〜RAND_MAXの範囲の擬似乱数(でたらめな数)を返す関数である。この関数を用いて,0以上10未満の乱数を100個発生させ,画面に表示するプログラムを作成しなさい。

#include <stdlib.h>
int rand(void);

cx4) 三角形の2辺の長さとそれらの挟む角を引数とし、面積を返す関数を作成しなさい。ただし、入力する角度の単位は“度”とする。 ※関数sinを利用するが、度からラジアンへの変換が必要である。

cx5) 前回の線形探索のプログラム bx2) を改造して,整数配列a,その要素数n,探したい整数xを引数に取り,見つかった場所(添字)を返す関数linear_searchを作成しなさい。ただし,見つからなかった場合には,-1を返すようにしなさい。

int linear_search(int a[], int n, int x);

発展課題

発展課題には提出期限を設けないので,前回までの発展課題でやっていないものがあるなら,自分の面白いと思うものをやってみましょう。

ダンジョンRPG

cz1) RPGシリーズということで,今度はダンジョン(地下迷宮)の地図を作り,プレイヤーがその中を探検できるようにしてみよう。今回はやることが多いので説明はかなり簡略されている。不明な点は自分で考えて適切に設計しながらチャレンジしてほしい。

主人公たちは村外れにたつ小さな古城にたどり着いた。言い伝えでは,この地下は昔牢獄として使われ,今では恐ろしい吸血鬼や悪霊が住み着く迷宮となっているという。村人のためにも,吸血鬼を退治しなければ…。

ダンジョンの地図は2次元配列として用意し,主人公の現在位置は変数(x, y)で表すとよい。その際,初期位置は上り階段(map[1][1])のある場所なので,x=1, y=1となる。

int map[7][7] = {
    { 9, 9, 9, 9, 9, 9, 9 },
    { 9, 1, 9, 0, 9, 5, 9 },
    { 9, 0, 9, 0, 2, 0, 9 },
    { 9, 0, 9, 0, 9, 9, 9 },
    { 9, 0, 0, 0, 2, 0, 9 },
    { 9, 0, 9, 0, 9, 7, 9 },
    { 9, 9, 9, 9, 9, 9, 9 } };

ここで,0〜9はそれぞれ次のものを表す(ここらへんは自由にアレンジしてよい)。

また,主人公が地図上のそれぞれの記号を訪れた場合の関数を作成する。壁は移動することができないので作らなくてよい。

void passage(int x, int y) /* 通路 */
{
    /* 乱数で(雑魚)モンスターに遭遇することがある */
    /* モンスターに遭遇した場合,戦闘関数を呼び出して闘う */
    /* モンスターとの遭遇確率は適当に決めてよい */ 
}

void stairway(int x, int y) /* 階段 */
{
    /* 地上に脱出するかどうか質問する */
    /* 吸血鬼を倒してから脱出すれば,ハッピーエンドである */
}

void door(int x, int y) /* 扉 */
{
    /* 門番のモンスター(中ボス)と闘うことになる */
}

void cross(int x, int y) /* 十字架 */
{
    /* 十字架を拾う。拾った跡地は通路にする */
}

void vampire(int x, int y) /* 吸血鬼 */
{
     /* ダンジョンの主,吸血鬼と最後の死闘 */
     /* 勝てば財宝と食料を手に入れ,体力が回復する */
}

ユーザに,主人公を東西南北(上下左右)どちらの方向に進ませるか尋ね,入力された方向に壁がなければ,x,yを適切に変化させてその場所の記号に対応する関数を呼び出す。

printf("どちらの方向に進みますか?(6:東 4:西 2:南 8:北)");
scanf("%d", &dir);
switch (dir) {
case 6:
    if (map[x+1][y] != 9) x++;
    break;
case 4:
    if (map[x-1][y] != 9) x--;
    break;
case 2:
    if (map[x][y+1] != 9) y++;
    break;
case 8:
    if (map[x][y-1] != 9) y--;
    break;
default
    /* エラー処理 */
}

switch (map[x][y]) {
case 0:
    passage(x, y);
    break;
case 1:
    stairway(x, y);
    break;
case 2:
    door(x, y);
    break;
case 5:
    cross(x, y);
    break;
case 7:
    vampire(x, y);
    break;
}

画面には,主人公の周囲の状況を表示するとよい。一例としては,最初階段にいるときに以下のよう表示させる。どんな文字や表示方法を使うか,どこまでの範囲を表示させるかは自由である。

■■■
■↑■
■ ■

戦闘システムは関数として独立させておき,passage(), door(), vampire()から必要に応じて呼び出せるようにしておく。また,主人公が十字架を持っているかどうかを示す変数を用意し,戦闘のとき武器の代わりに十字架を使うと,吸血鬼に大きなダメージを与えられるようにする。

吸血鬼に負けてしまった場合は,主人公も吸血鬼になってゲームオーバーである。同じような冒険者を生き血を求めて,このダンジョンに住み続けることになる…。


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