今まで堅苦しいプログラムばかり作ってきましたが、ちょっと楽しいプログラムを作ってみましょう。そう、ゲームのプログラムです。コンピュータゲームというと「ゲームばかりやってないで勉強しなさい」とお父さんお母さんに叱られたとかオンラインゲームで課金し過ぎてお金がなくなったとかあまりいい話を聞きませんがそれはゲームで遊ぶ側の話です。そのゲームのプログラムを作るということになると話は違ってきます。ゲームのプログラム1つ作る過程でプログラミングの高度なテクニックや様々なアルゴリズムを学ぶことができます。筆者はゲームを作りながらプログラミングの勉強をすることは否定しません。ということでこのカテゴリーでも実際にゲームのプログラムを作ってみたいと思います。とはいっても 残念ながらTiny BASIC for Windowsでテキスト画面のみでは、画面いっぱいに敵戦闘機が飛び回るとか、クエストクリアしたら美少女が微笑むようなゲームは無理ですが、ストリングと計算式のみでも知育ゲームやアドベンチャーゲームのようなものはできます(なんか美少女のたとえが多くね?・・・汗)。
[乱数]
ゲームで必須なものは乱数です。乱数とは、規則性のない数の並びのことです。たとえば2、3、4、6なら12の約数を小さい順に並べたものとわかりますが、4、7、2、9だとまったく規則性がありません。ゲームでは敵がどこから襲ってくるかわからない、どこに何が隠れているかわからないなど、ランダムに何かが出てくるという場があります。これを実現かるのが乱数であり、Tiny BASICにはこの乱数を発生させる機能があります。それが次のRND(*)です。次のプログラムを実行してみてください。
FOR I=0 TO 10
R=RND
PRINT R
NEXT I
実行結果は図13-1のようになります。
図13_1.乱数を発生する
RNDは0以上1未満の乱数を発生する関数(後述)です。ただしひとつ注意することがあります。上のプログラムを何回か実行してみてください。おわかりと思いますが乱数とはいってもコンピュータで発生させる場合ある計算式にしたがって発生させるので、同じプログラムを繰り返し実行すると、発生する数の順番はいつも同じになってしまいます。これではたとえばシューティングゲームに使うにしても、敵が出てくる場所と順番がいつも同じになってしまい、面白みが半減ですね。これを回避するには、次のようにRANDOMIZE(*)という一文をRNDの前に入れます。これはコンピュータの内部時計を用いて、乱数発生の計算式の初期化を行なうものです。
RANDOMIZE
FOR I=0 TO 10
R=RND
PRINT R
NEXT I
これで数回実行してみましょう。今度は発生する数の順番はバラバラになったはずです。では、これで1から10までの整数をランダムに発生させる方法を考えてみましょう。RNDで発生する数は0以上1未満ですから
RANDOMIZE
R=RND*10+1
で 1 から 10 までの数がランダムで得られます。さらに
RANDOMIZE
R=INT(RND*10)+1
とすると、発生した数の小数点以下が切り捨てられ1から10までの整数がランダムで得られます。INTはInteger(整数の意味)の省略形でINT(数値)で( )内の数値の小数点以下を切り捨てて整数化する関数です。次のプログラムで試してみてください。結果は図13_2のようになります。
RANDOMIZE
FOR I=1 TO 10
R=INT(RND*10+1)
PRINT R
NEXT I
END
図13_2.1から10の整数をランダムに発生させる
(*)RANDOMIZEとRNDの正式な書き方は
RANDOMIZE 整数(-32768~32767の範囲)
RND(整数)
ですが、Tiny Basicでは、どちらも整数は省略可です。省略しても不規則な数を発生します(図13-3)。普通のゲームプログラムならこれで問題ないと思います。
図13_3.1から100の整数をランダムに発生させる
[数当てゲームのプログラム]
それでは、ここまでの知識を使って簡単なゲームを作ってみましょう。コンピュータが隠した 1 から 100 までの整数を当てるゲームです。図13-4のプログラムを見てください。
1:CLS ' 画面をクリア
2:RANDOMIZE ' 乱数系列の初期化
3:PRINT "***** 数当てゲーム *****"
4:PRINT "今からコンピュータが隠した数を当ててください"
5:PRINT "数は 1 から 100 の整数のみです"
6:' 乱数を発生させる
7:R=INT(RND*100)+1
8:' 数を当てる
9:' 正解が出るまで以下の処理を繰り返す
10:DATAOK=0
11:WHILE DATAOK=0
12: PRINT
13:' 1~100の整数が入力されるまで繰り返す
14: NUMOK=0
15: WHILE NUMOK=0
16: INPUT " 1~100で数を当ててください",NUM
17: PRINT
18: IF NUM=>1 AND NUM<=100 THEN
19: NUMOK=1
20: END IF
21: WEND ' 1~100の整数が入力されるまで繰り返しここまで
22:
23:' 正解判定
24: IF NUM=R THEN
25:' 正解のとき
26: PRINT "正解です。おめでとうございます"
27: DATAOK=1
28: ELSE
29:' 正解ではないとき
30: IF NUM
32: PRINT "小さすぎます"
33: ELSE
34:' 大きすぎるとき
35: PRINT "大きすぎます"
36: END IF
37: END IF
38:WEND ' 正解が出るまでの繰り返しここまで
39:END
図13-4.数当てゲームのプログラム
プログラムスタート部分の1行目のCLSはCLear Screenの省略形で実行画面をすべて消去する命令です。表示が消去されるだけで記憶されているプログラムやデータが消去されるわけではありません(黒板消しのようなものです)。
2行目は今、説明した乱数の初期化です。3~5行目はプログラムと遊び方の説明です。
7行目がコンピュータが隠す数を発生させるところですが、ここでは1から100までの数を発生させることにします。
プログラム全体の流れとしては、最初にコンピュータが数を隠した後、正解が出るまで答えの入力を繰り返し実行されるようになっています。そしてプレイヤーが答えた数が1~100以外の数以外のときは入力がやり直しになるようになっています。それは11、38行目のWWHILE~WENDと15行、20行目のWHILE~WENDのネスティングで実現しています。この手法は実務プログラムではオペレータの入力ミスを防ぐ対策としてよく使われるものです。覚えておいて損はありません。
19、29行目のIF~END IFは正解不正解の判定です。正解ならこのプログラムを終了するように、不正解なら回答した数が大き過ぎか小さ過ぎかを表示するようにしています。
図13_5.数当てゲームの実行画面
【いろいろなエラー】
ここまでいろいろなプログラムを作ってきた皆さんは、いろいろなエラーを出してきたと思います。エラー(error)とは「誤り」とか「失敗」という意味です。何回もエラーを出して「俺にはプログラミングの才能なんてないんだ」と落ち込んでいる人はいませんか。気にする必要はありません。勉強中はむしろどんどんエラーを出してください(*)。筆者の経験で言えば、エラーを出してそれを修正してゆく中で学べるものはたくさんあります。
(*)Perl、PHP、C言語、アセンブリ言語などハードウェア、サーバーを直接操作するプログラミング言語の場合、パソコンをローカル環境(ネットワークから切り離したスタンド・アローンな状態)にして勉強してください。
プログラミングで発生するエラーは大別すると次の3つになります。
①文法エラー(Syntax error)
文法エラーというより構文エラー(書き方の誤り)です。たとえば次のプログラムを実行しようとすると、図14-6のような状態でコンピュータは止まってしまいます。
1:GOKEI=0
2:NINZU=0
3:BANGO$=""
4:WHILE BANGO$<>"//"
5:READ BANGO$,NAMAE$
6: IF BANGO$<>"//" THEN
7: PRIMT BANGO$;" ";NAMAE$;" ";
8: INPUT "得点="・,TEN
9: GOKEI=GOKEI+TEN
10: NINZU=NINZU+1
11: END IF
12:WEND
13:NINZU=NINZU-1
14:HEIKIN=GOKEI/NIN
15:PRINT USING "受験者 ##人";NINZU
16:PRINT USING "平均点###.#";HEIKIN
17:END
18:DATA "001","相沢 恭平"
19:DATA "002","小川 順子"
(中略)
54:DATA "037","渡部 浩司"
55:DATA "//",""
図13-6.文法エラー
プログラムの7行目を見ると、PRINTと書くべきところがPRIMTになっています。Tiny Basicにこんな命令はないので(変数とみなされてしまうため)、その後に続く内容と矛盾が生じてしまい、コンピュータが???状態になってしまったわけです。
②実行エラー(execute error/running error)
文法(書き方)の誤りはなくても、プログラム実行中に発生するエラーがあります。次の例は処理不可能なデータに当たってしまったコンピュータが「こんなのやってらんねぇ」となってしまった例です。
上のプログラムを修正して次のようになりました。これを実行してみたところ図13-7のようになりました。
1:GOKEI=0
2:NINZU=0
3:BANGO$=""
4:WHILE BANGO$<>"//"
5:READ BANGO$,NAMAE$
6: IF BANGO$<>"//" THEN
7: PRINT BANGO$;" ";NAMAE$;" ";
8: INPUT "点数 ",TEN
9: GOKEI=GOKEI+TEN
10: NINZU=NINZU+1
11: END IF
12:WEND
13:NINZU=NINZU-1
14:HEIKIN=GOKEI/NIN
15:PRINT USING "受験者 ##人";NINZU
16:PRINT USING "平均点###.#";HEIKIN
17:END
18:DATA "001","相沢 恭平"
19:DATA "002","小川 順子"
(中略)
54:DATA "037","渡部 浩司"
55:DATA "//",""
図13-7.実行エラー
プログラム14行目は得点の合計を受験者数で割って平均点を計算するところですが、除数になる変数名がNINZUであるべきところNINになっています。この場合、プログラムの書き方としては誤っていないので文法エラーにはならずプログラムは実行開始されます。しかし変数NINの値は 0 なので14行目で処理不可能となり止まってしまいました(除数が0の割り算は不可能なのは算数でもコンピュータでも同じです)。
➂論理エラー(logical error)
プログラムの文法上の誤りも入ってくるデータにも誤りはないのに、処理内容の誤りにより正しい処理結果が得られないエラーです。簡単な例で説明します。
次の例はキーボードから単価と売上個数を入力し売上金額を計算するプログラムなのですが、単価100円、個数5と入力したところ金額が500円となるところが105円になってしまいました。
1:INPUT "単価=",TANKA
2:INPUT "個数=",KOSU
3:KINGAKU=TANKA+KOSU
4:PRINT "金額=";KINGAKU
5:END
図13-8.論理エラー
誤り箇所はプログラム3行目です。
KINGAKU=TANKA*KOSU
とするべきところが
KINGAKU=TANKA+KOSU
になっています。この場合、プログラムの書き方に誤りはなく、データも変数TANKAの値が500、変数KOSUの値が5なので、KINGAKU=TANKA+KOSUの計算は問題なくできます。なのでプログラムは途中で止まらず最後まで実行されます。しかし得られた結果は求めているものではない、ということになります。
このように論理エラーは途中でコンピュータが止まってしまうことがないので発見と修正が最もやっかいなエラーといえます。ゆえにこのような誤りのことを、みつけにくいという意味でバグ(bug、ダニのような小さな虫)、バグをみつけて修正することをデバッグといいます。
コンピュータはたとえて言えば「忠実だけど融通がきかない部下」です。
「課長、この指示はこれでいいんですか?」
「おぅ、ありがとう。ここは間違い、こうしてくれ」
とはなりません。指示書が間違っていようが渡されたデータに誤りがあろうが、そのとおりに実行しようとします。たとえその結果、会社が倒産(システムがダウン)するとしてもです。コンピュータの計算(処理結果)が正確なのは正しいプログラムとデータが与えられていれば、の話です。
【この章のまとめ】
ゲームの主役は乱数である。
RNDは乱数を発生する関数である。
RANDOMIZEは乱数系列を初期化する。
プログラミング上のエラーには文法エラー実行エラー、論理エラーがある。
プログラムのミスをバグ、バグをみつけて修正することをデバッグという。