第二章 応用・中級編


【1】第一章の補足から


さて、ようやく第二章が始まりました。
一つ注意をしておきますが、ここを読んでいる方は第一章を全て理解していることを前提に書いていきます。
しっかりと理解していない人は、もう一度第一章に戻って理解してからこっちを読みましょう。
先走っても、余計わからなくなるだけですからね。

では本題。
次のプログラムは最初に書いたプログラムと同じですが、一行だけ追加してあります。

10 REM 最初のプログラム
20 CLS
30 PRINT "初めてのプログラミング講座"
40 END

新しく追加されているのはREMという命令ですね。この命令の後には好きなコメントを書くことができます。

また、REMと書く代わりに '(シングルクォーテーション) でも同じ効果を得られます。
ほとんどの人はこちらの形式を使っていますね。簡単で短いですし、私もこちらをおすすめします。

なお、行頭に書く必要はなくて、

10 A=A^2 'Aを2乗する

という使い方もできます。
ただし、REM文以降は全てコメントとして扱われるので、命令がある行に書く場合には、必ず行の最後に書いてくださいね。

さてここで、また「誰でも読みやすいプログラム」のことが出てきます。
他人が書いたプログラムを読むときに一番有効なのは、コメントを読むことです。
「この行は何をしているのか」が説明してあれば、負担は大幅に減りますよね。
その都度コメントを書くのは面倒かもしれませんが、後々の自分のためにもなりますから、要所にはコメントを書くようにしましょう。

次はコメントの書き方の一例です。

10 'コメントの書き方の一例
20 CLS
30 PRINT "九九を表示します"
40 FOR J=1 TO 9
50   FOR I=1 TO 9
60     PRINT I;"*";J;"=";I*J '九九を計算して表示
70   NEXT
80 NEXT
90 END

ではでは、次の節から本格的に第二章が始まりますので、お楽しみに!

▲▲▲▲▲▲ ここまでは 2000/09/09 に書いています ▲▲▲▲▲▲

さて、ここでもう1つ補足しておきます。
第一章で変数について軽く説明しましたが、数値と文字の変数の2つしか出てきませんでしたね。
ここでは、その他の重要な変数の型を説明します。

まず、変数の型には次の種類があります。括弧の中の記号は、変数名の後に付けるものです。文字ならでしたね。

・整数型(%)
・単精度浮動小数点数型(!)
・倍精度浮動小数点数型(#)
・文字型($)

以上の4種類です。

実は「数値の変数」と言っていたのは、上から3つ分を全てまとめたものでした。
今まで変数名の後に何も付けなかったものは、互換BASICが、その変数が持っている値から自動的に型を判断していたんです。
整数が入っていたら整数型、小数点がある数値なら単精度浮動小数点数型というようにね。

何だか難しそうな名前が並んでいますけど、要するに何が違うんでしょうか。

変数の型

整数型 -32767〜+32768の整数(計算速度が上がるおまけ付き)
単精度浮動小数点数型 有効桁数7桁で-1.70141E+38〜1.70141E+38
倍精度浮動小数点数型 有効桁数16桁で-1.701411834604692D+38〜1.701411834604692D+38
文字型 可変長の文字

資料提供:きび太郎さん/N88BASICで食べるperlらいす

補足)複雑な数字の書き方がありますけど、単精度なら7桁の小数で、倍精度なら16桁の小数と思っていればいいでしょう。
なお、小数の桁数の数え方は整数と違って、0.000・・・とゼロが続いている間は数に入れませんのであしからず。

もし、範囲外の値を代入すると、正しい値が記憶されません
しかも、後から、その値が正しいのか正しくないのかを判断する方法はないんです、困ったことに。
なので、計算の途中で1度でも範囲外になってしまわないように気を付ける必要があります。

(別編 覚えておけば役に立つ 【2】どうして正しく記憶される範囲なんてのがあるのか)

さてさて、ここでまとめなんですが。
まず、互換BASICでは小数型は1つに統一されているようで、実数型としてしか扱えないようです。
その時の精度は、倍精度と同じ16桁になります。
もう1つ、せっかく変数の型について説明してきたんですけど、実際には今まで通りの「数値の変数」と、「文字の変数」だけでいいでしょう。
「なんじゃそりゃー!?」って思う方もいるかもしれませんが、どうしても他の型を使わなきゃいけない場合なんて、滅多にないと思います。
特にBASICで作るゲームの分野においては、ね。
ただし、それでもBASICの基礎知識なので、しっかり覚えておいてくださいね。

▲▲▲▲▲▲ ここまでは 2000/12/19 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【2】ゲームでよく使う命令たち


注)この節は必要に応じて加筆します

まずはPRINT文をパワーアップさせる命令を紹介します。

今まで、いろいろな文字や数字を表示してきましたね。
でも、PRINT文だけでは表示場所を決められませんでした。
それを解決する命令が LOCATE 命令です。

文法

LOCATE X座標Y座標
動作 次にPRINT文で表示する位置を(X座標,Y座標)に設定する。
補足 なし

なお、LOCATE文に与える座標は数字を使っても変数を使っても構いません。

LOCATE 10,15
LOCATE X1,Y1

次のサンプルは、前の節の九九を表示するプログラムに LOCATE を加えました。
55行に追加していますね。
この命令を使えば、簡単なレイアウトができます。

10 'LOCATEを加えたら?
20 CLS
30 PRINT "九九を表示します"
40 FOR J=1 TO 9
50   FOR I=1 TO 9
55     LOCATE (I-1)*9,J*2
60     PRINT I;"*";J;"=";I*J '九九を計算して表示
70   NEXT
80 NEXT
90 END

LOCATE文のが少し複雑なので、補足しておきましょうか。
X座標(I-1)*9 となっているのは、変数 I が1から始まっているからです。
I*9 だけだと最初のX座標が9になってしまい、左に空間ができてしまいます。
Y座標J*2 となっているのは、2行置きに表示したいからです。

この命令がないとBASICではゲームが作れないってぐらいよく使うので絶対に覚えましょうね。

▲▲▲▲▲▲ ここまでは 2000/09/11 に書いています ▲▲▲▲▲▲

次の命令もPRINT文をパワーアップさせる命令です。

今まで表示していた文字はどれも白一色でしたが、これを使えば8つの色を付けることができます。

文法

COLOR 文字色
動作

表示する文字の色を下の8色の中から設定する。
0=黒、1=青、2=赤、3=紫、4=緑、5=水色、6=黄色、7=白

補足 この命令はいろいろな効果があるので、それが必要になったときに追加説明します。

こんな風に項目名とデータで色をわければ見栄えも良くなりますね。

10 'COLOR
20 CLS
30 COLOR 4:PRINT "九九を表示します"
40 FOR J=1 TO 9
50   FOR I=1 TO 9
55     LOCATE (I-1)*9,J*2
60     COLOR 7:PRINT I;"*";J;"="; '九九を計算して表示
65     COLOR 4:PRINT I*J
70   NEXT
80 NEXT
90 END

▲▲▲▲▲▲ ここまでは 2000/11/05 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【3】キーボードから入力しよう(2)


第一章でキーボードから入力する命令として、INPUT命令を説明しましたね。
ただし、この命令ではゲームでよく使われているキャラクタの移動に使うことはできません。
なぜならば、その都度処理が止まってしまうからです。

そこでこの節では、リアルタイムに入力できる命令、ではなくて関数を説明します。
(「関数って何?」「命令とどう違うの?」って人は第一章の【7】数当てゲームを作ろうで説明していますので、そこを参照してください)

では、INKEY$関数の説明を。

文法

INKEY$

動作 キーボードから入力された文字を一文字だけ得る。
補足 INPUTとは違い、入力された文字は画面には表示されません。
何も入力していないときは””(空文字列)が与えられます。
具体的な動作を説明すると、キーボードバッファに蓄えられた文字を先に入力した順に取り出しています。

INKEY$を使って、キャラクタに見立てた文字「*」をカーソルで動かしてみましょう。
カーソルは「2」を下、「4」を左、「6」を右、「8」を上とします。

10 'INKEY$の使用例
20 X=40:Y=10            '初期位置を指定
30 '---------------------------------------- キー入力
40 I$=INKEY$            '変数I$に入力キーを代入
50 IF I$="2" THEN Y=Y+1 '入力キー判定
60 IF I$="4" THEN X=X-1
70 IF I$="6" THEN X=X+1
80 IF I$="8" THEN Y=Y-1
90 '---------------------------------------- 表示
100 CLS                 '移動跡を消す
110 LOCATE X,Y          '位置を指定
120 PRINT "*"           'キャラクタ表示
130 GOTO 40

後でプログラムだけを見たときにわかりやすいように、コメントをたくさん付けておきました。

いやー、ここまで来ると本格的になってきましたね。
(何が本格的なのか、というのも実は重要なことなんですけど・・・わかるかな?)
多分ちょっと見ただけではわからないでしょうから、順に説明していきましょうか。

まずは20行の意味から。

キャラクタを表示する位置が毎回変わるので、位置を記憶するための変数がいります。
このプログラムでは、その変数を X と Y にしています。
最初はどちらも0ですが、それだと左上端に表示されてしまうので、画面中心辺りに設定してみました。
これがキャラクタの初期位置です。

そして CLS がいつもと違ってプログラムの最初にない理由とは。
まあ、最初にCLSを置いても問題ないんですけどね。どうせ移動跡を消すときに画面を消すんだからと思って1つだけにしました。

今までは画面を消して掃除するために、一番最初に実行していました。
しかし、今回は初期位置を設定していますよね。
また、このプログラムは同じところをループしています(130行)
つまり、このループの中に初期位置設定があるとキャラクタが動かなくなってしまいます
そうならないために、ループに入る前に初期設定を置き、ループの中に CLS を置いているわけですね。
で、ループの中ならどこでもいいんですけど、表示関係はまとめた方がいいので100行にしました。
(ついでに書くと、ここに置くとちらつきが軽く押さえられるんですけど、ま、それはまた別のお話、と)

次に50〜80行について。

Y=Y+1 と、ありますけど、これの意味がわかりますか?
まず、わかりやすくするために色を付けてみると、 Y=Y+1 となります。
そして、イコールの前と後ろで分けて考えてください。
後ろを一つのとして見ると、 Y+1 とは「変数 Y に1を加えた数」になりますね。
その計算で出た値を、変数 Y に代入しています。
要するに新しい Y と古い Y が一緒の式に入っているんですけど、これでわかりますか?
前に新入部員に説明したときは、いくらやってもわかってもらえなかったもんで(汗)

さてさて、それでは実際に実行してみて、いろいろと試してみてください。
CLS の行をコメントアウトしてみると、「移動跡を消す」という意味がわかっていいかと。

最後に一つ付け加えておくと、このプログラムは不完全です。
何がどう不完全なんでしょうね?(にやり)。
デバッグの練習ということで、いろいろと操作をしてみてください。
そして、自分で考えて、バグが起こらない(これヒントですね)ように追加してみてください。

注)互換BASICではエラーが表示されてプログラムが止まりましたが、他のソフトではそうならないかもしれません。そうしたら見つけるのは厄介ですけどね。

さて、あなたはすぐに気が付くかな?

▲▲▲▲▲▲ ここまでは 2000/09/11 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【4】サブルーチンで簡潔に!


この節ではサブルーチンというものを説明します。
サブルーチンはどんなプログラム言語にも関わってきますから是非覚えましょう。

まずは次のプログラムを見てください。

10 'サブルーチンを使わないと……
20 CLS
30 '
40 PRINT "--------MENU--------"
50 PRINT "1.SAVE"
60 PRINT "2.LOAD"
70 PRINT "3.EXIT"
80 INPUT "何をしますか";A
90 '
100 IF A=1 THEN 200
110 IF A=2 THEN 300
120 IF A=3 THEN 400
130 PRINT "番号が不正です"     '入力エラーの場合
140 GOTO 40
200 '
210 PRINT ":NOW SAVING...OK"
220 GOTO 40
300 '
310 PRINT ":NOW LOADING...OK"
320 GOTO 40
400 '
410 PRINT ":EXIT"
420 END

注)このプログラムでは行番号が途中で飛んでいますが、全く問題ありませんのであしからず。機能ごとにわざとキリの良い数字にしているだけです。

では、何をしているか説明しておきましょうか。

メニューを表示してユーザに選択・入力してもらい(40〜80行)その機能の場所へ処理を移して(100〜140行)選んだ処理を実行する(200行〜)

100行からの判定に引っかからなかった場合は130行、140行を実行するので、何もせずにやり直しになります。
実際には、分岐した先でも何もしていないんですけどね(苦笑)。まあサンプルですからこんなもんで。

200行以降は機能ごとに2行ずつのまとまりになっています。
そして、それぞれの最後に、もう一度メインの選択処理に戻るために

GOTO 40

としていますが、これって不便なんです。
なぜなら、どこへ戻らなきゃいけないのかをプログラマが把握していないとダメだからです。
どこへ戻るのか指定しなくてもいい、つまり行番号を調べ直さなくてもよくするためにサブルーチンの出番です。

10 'サブルーチンを使うと!
20 CLS
30 '
40 PRINT "--------MENU--------"
50 PRINT "1.SAVE"
60 PRINT "2.LOAD"
70 PRINT "3.EXIT"
80 INPUT "何をしますか";A
90 '
100 IF A=1 THEN GOSUB 200
110 IF A=2 THEN GOSUB 300
120 IF A=3 THEN GOSUB 400 : END
130 IF A<>1 AND A<>2 AND A<>3 THEN PRINT "番号が不正です"
140 GOTO 40
200 '
210 PRINT ":NOW SAVING...OK"
220 RETURN
300 '
310 PRINT ":NOW LOADING...OK"
320 RETURN
400 '
410 PRINT ":EXIT"
420 RETURN

さあ、GOSUB と RETURN という命令が新しく出てきましたね。
では文法説明から。

文法

GOSUB 行番号
動作 行番号へ飛ぶ。
補足 GOTOと違い、どこから飛んだのかを記憶している。

さらにセットで。

文法

RETURN
動作 一番最近に飛んだGOSUBの次の命令へ飛ぶ。次の命令がないときは次の行へ。
補足 GOSUBを使った後しか使えません。

この二つはいつでも一緒に使います。

言葉だけではわかりにくいでしょうから、次の図に処理順序を示しておきますね。
サブルーチンの処理順序

次のがこのプログラムの表示結果です。

MAIN1
SUB
MAIN2

ここで言葉の定義。
サブルーチンとはある一つの機能を持ったプログラムの集まりのことです。
それに対して全体の中心処理をメインルーチンといいます。
ここでは40〜140行がメインルーチンで、210〜220行、310〜320行、410〜420行がサブルーチンになります。

こうしてサブルーチンを使うと、飛んだ先で用が終わったら必ず戻ってきますから、機能ごとに分割してプログラムを読めるようになります。
飛んだ先のプログラムを、それぞれ個別に作り込んでいけますね。
本来はもっと大きなプログラムのときに威力を発揮するので、今回のサンプルでは煩わしいかもしれません。
第二章の最後では大きめのプログラムを作るつもりなので、そこでは本来の姿が見られると思います。

では説明を加えておきます。

130行が変わっていますが、これは RETURN で戻ってくるからです。
もし改造前と同じにしたらどうなるかというと、

100 IF A=1 THEN GOSUB 200
110 IF A=2 THEN GOSUB 300
120 IF A=3 THEN GOSUB 400
130 PRINT "番号が不正です"
140 GOTO 40

100〜120行をすり抜けた場合にエラー表示をするのはいいんです。
でもこれだと、どこに分岐してもGOSUBの次の命令へ戻ってくるので、毎回エラー表示をしてしまいますね。
だから、最後の判定を

130 IF A<>1 AND A<>2 AND A<>3 THEN PRINT "番号が不正です"

として、この行だけで、全てをすり抜けてきた場合と同じ条件にしています。

 

さてさて、それではここで、サブルーチンを使った場合に特に便利なことを。

今までは GOTO や GOSUB で行番号を指定するとき、そのまま行番号を書いていましたけど、それ以外にもっといい方法があります。
実は行には名前を付けられるんです。
例えば、

100 *MAIN
200 *SAVE
300 *LOAD
400 *EXIT

こういう風に*(アスタリスク)を行番号の次に書くと、その後に名前を付けられます。
これをラベルといい、行番号の代わりに

GOTO *MAIN
GOSUB *SAVE

とできます。ラベルは数字とアルファベットなどを組み合わせて、変数のように名前を付けられます。

ラベルを使った方が、行番号を気にしなくても良くなるというサブルーチンの特徴を活かせますね。
GOSUBで飛ぶ先はまとまった処理になっていることが多いですから、そこに名前を付けておけば、飛ぶときにも行番号を調べ直さなくてもよくなります。

便利だからといってGOTO文で使い過ぎるのはよくないのですが、GOSUB文で使うならいくらでもいいでしょう。どんどん利用してください。

次のはラベルを使ったサンプルです。

10 'ラベルを使ってみよう
20 CLS
30 '
40 *MAIN
50 PRINT "--------MENU--------"
60 PRINT "1.SAVE"
70 PRINT "2.LOAD"
80 PRINT "3.EXIT"
90 INPUT "何をしますか";A
100 '
110 IF A=1 THEN GOSUB *SAVE
120 IF A=2 THEN GOSUB *LOAD
130 IF A=3 THEN GOSUB *EXIT : END
140 IF A<>1 AND A<>2 AND A<>3 THEN PRINT "番号が不正です"
150 GOTO *MAIN
200 '
210 *SAVE
220 PRINT ":NOW SAVING...OK"
230 RETURN
300 '
310 *LOAD
320 PRINT ":NOW LOADING...OK"
330 RETURN
400 '
410 *EXIT
420 PRINT ":EXIT"
430 RETURN

▲▲▲▲▲▲ ここまでは 2000/09/15 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【5】配列を覚えよう(1)


ここはかなり重要なので長くなると思います。前の節も長かったですけどそれ以上に(苦笑)。
順を追って説明していきますから、しっかりと読んでくださいね。

さてさて、まずこの配列というのは何でしょうね?
とりあえずサンプルを見てもらって、配列の素晴らしさを実感してもらいましょうか。

10 '配列がないと?
20 CLS
30 INPUT "NAME:";N1$
40 INPUT "TEL:";T1$
50 INPUT "NAME:";N2$
60 INPUT "TEL:";T2$
70 INPUT "NAME:";N3$
80 INPUT "TEL:";T3$
90 '
100 CLS
110 PRINT "1."
120 PRINT "NAME:";N1$
130 PRINT "TEL:";T1$
140 PRINT "2."
150 PRINT "NAME:";N2$
160 PRINT "TEL:";T2$
170 PRINT "3."
180 PRINT "NAME:";N3$
190 PRINT "TEL:";T3$
200 END

このプログラムは3人分の名前と電話番号を入力して、まとめて表示するものです。
パッと見てもわかるように、同じような処理が繰り返されていますね。
30〜80行にFOR文を使いたいところですが、全て変数が違うので使えません。

ここで配列を使ってさっぱりさせてみましょう。

10 '配列って素晴らしい
20 CLS
30 DIM N$(3),T$(3)     '配列を宣言
40 FOR I=1 TO 3
50   INPUT "NAME:";N$(I)
60   INPUT "TEL:";T$(I)
70 NEXT I
80 '
90 CLS
100 FOR A=1 TO 3
110   PRINT A;"."
120   PRINT "NAME:";N$(A)
130   PRINT "TEL:";T$(A)
140 NEXT A
150 END

どうですかお客さーん!(笑)
変数の後ろに括弧と番号が付いていますよね。この変数が配列変数です。
配列変数に付けた番号は、数値でも変数でも使えます。

ここで用語説明。
配列変数に付けられた番号を添え字と呼びます。括弧の中の数字のことです。

この添え字に変数が使えるのが配列変数の良い所なんですね。
なぜなら、このサンプルのように、変数が違うけど他は同じになっているときに配列変数を使うと、FOR文が使えるからです。配列とFOR文はとても相性がいいんですよね。

さて、配列を使うとどう素晴らしいのかが少しは感じ取ってもらえたでしょうか?
それでは、今度は配列がどういうものなのかを図を使って説明していきます。

配列変数のイメージ

この図には、0〜5番の6つの箱が並んでいます。
まず、1つの箱が1つの変数だと考えてください。1つの箱の中には1つだけ数値を入れることができます。

X=999

とすると、Xという箱の中に999を入れることになります。ここまでは今までやってきたことですね。

配列変数というのは、この箱がいくつも並べてあるということです。
図のように、箱には0から順番に番号が振ってあって、番号を指定すれば、どの箱から出し入れするのか選択できます。

この説明だけで配列の使い方が思いつきますか?  俺は思いつきませんでした(苦笑)
だから最初にサンプルを提示して、配列のいいところを示したわけです。

さてさて、実際に配列を使うときには約束事があります。
今まではいつどこでどんな変数を使っても良かったんですが、この配列変数はそうはいきません。
実際に使う前に、箱の大きさを決めてやらなければいけないんです。
サンプル「配列って素晴らしい」の30行を見てください。

30 DIM N$(3),T$(3) '配列を宣言

このDIM文を使って大きさを決めます。

文法

DIM 変数名(添え字)[変数名(添え字)]...
動作 配列を宣言する。
補足 番号は標準で0から付けられるから、(添え字+1)個の配列が用意される。

図のような配列変数Aを宣言して、図と同じようにそれぞれに代入してみると、

DIM A(5)
A(0)=10
A(1)=20
A(2)=30
A(3)=40

となります。4番目と5番目には何も入れていないので、0が入っています。

また、変数には文字も代入できましたよね。もちろん文字変数も配列にできます。

DIM NAME$(10)

文字変数の配列の時はを忘れないようにしましょう。

ここで重要な注意があります。配列変数の宣言を

DIM S(5)

として、6個の変数を作ったとします。それなのに、

A=S(6)

として配列の大きさ以上の添え字を与えると、エラーが起こってプログラムは止まってしまいます。
添え字に変数を使う場合は、実際の数値が見えないので特に注意しましょう。

 

恒例のサンプルは思いついたら書き加えたいと思います。

 

注)ここからのことは互換BASICでは対応していません。

標準の設定では、配列の添え字は0から付けられます。
ですが、サンプルのような場合には0を使わずに1から使う方が便利ですし、間違いも少なくなりますよね。
こういうのは実際よくあるんです。
今回は配列変数の種類が少ないからいいですが、これが100種類ぐらいになると、それだけでも変数100個分ものメモリを無駄に消費していることになりますね。
そういうとき、無駄を減らしたいというあなたの為にこの命令があります。

文法

OPTION BASE
動作 配列宣言のときの、添え字を付け始める番号を変更する。には0か1だけが使えます。
補足 標準では0です。変数ごとには設定できません。

おそらく誰も知らないんじゃないかな。
こういう誰も使わないような命令を知っているのも、リファレンスマニュアルを読んで勉強したおかげかもね。

というわけで、配列関係の失われた技術をもう1つ。

文法

ERASE 変数名
動作 宣言した配列変数を消去し、再度、宣言可能にする。
補足 なし

普通は一度宣言してしまった配列変数は大きさを変えられません。
しかし、どうしても変えたい場合には、この命令を使って消去した後で、もう一度宣言します。

▲▲▲▲▲▲ ここまでは 2000/10/09 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【6】配列を覚えよう(2)


あまりに長くなるので、2つの節に分けました。

前の節では括弧の中に1つしか添え字がありませんでしたが、ここでは2つの添え字がでてきます。
添え字が1つだけの配列を1次元配列といい、2つの配列を2次元配列といいます。
この節では、多次元配列を説明します。

まずは2次元配列の宣言の仕方を書きますね。

文法

DIM 変数名(添え字1添え字2)
動作 2次元配列を宣言する。
補足 添え字1+1)×(添え字2+1)個の配列が用意される。

実際には

DIM S(10,5)
S(5,2)=70
S(X,Y)=120

という風に書きます。

前の節で説明した配列は1次元配列でしたよね。1次元、つまり線形の配列だったんです。
ですが今度は2次元になるので、線ではなく平面になります。
次の図を見てください。

2次元配列のイメージ

2次元配列はこのような「表」で表すことができます。
それでは次のプログラムを見てください。

10 '2次元配列
20 CLS
30 DIM KUKU(9,9)
40 FOR J=1 TO 9
50   FOR I=1 TO 9
60     KUKU(I,J)=I*J
70   NEXT I
80 NEXT J
90 '
100 INPUT "A,B:",A,B
110 PRINT KUKU(A,B)
120 END

上のプログラムは何をしているのか説明しましょう。
まず、KUKUという配列変数を宣言して、そのKUKUの1つである(I,J)の中にI×Jの値を代入しています。
そして、ユーザーに2つの数字を入力してもらい、(A,B)の中身を表示しています。

さて、このプログラム、無駄なことをやっている気がしませんか?
だって、わざわざ先にかけ算の答えを計算しておいて、後でそれを取り出す為だけに配列を使っていますもんね。
ところが、実はこうして計算結果を先に出しておいて配列に入れておく技術をテーブルといいます。
配列というものは番号を与えるとすぐに数値が帰ってくるので、計算がとても複雑で、リアルタイムに計算していると長い時間がかかってしまうような場合にはテーブルとして配列を使います。
そのほかにもいろいろと応用でき、利用価値のある技なので覚えておいてください。

このプログラムでは計算はかけ算だけなので、本当は使う必要はないです。
もっともっと複雑な、例えば sincos が入っているような式の場合に使われることが多いです。

実際のゲームではどういう使われ方をしているか少し説明しましょうか。
2次元配列は表として使えるので、表形式のデータの代表とも言えるマップの記憶によく使います。
他に将棋や囲碁の盤などがあります。
少し気づきにくい例としては、テトリスやぷよぷよといった落ちゲーです。これも2次元配列が使われています。

この講座でもマップの表現方法は説明しますのでお楽しみに。
配列のサンプルはそちらで詳しく載せます。

 

さてさて、それでは3次元配列について簡単に説明します。もうたいして変わりませんからね。

宣言も今までと変わりません。次元の数だけ添え字が増えるだけです。

文法

DIM 変数名(添え字1添え字2添え字3)
動作 3次元配列を宣言する。
補足 添え字1+1)×(添え字2+1)×(添え字3+1)個の配列が用意される。

そして、次の図が3次元配列のイメージです。

3次元配列のイメージ

そんなに重要な図でもないので、「こんなもんか」って思っておいてください(苦笑)。

XYZの座標軸は、勝手に決めても構いません。
ただ、(X,Y,Z) の対応が変わるだけなので、自分で好きなように変えることができます。
このことは、2次元配列のときでも同じです。

上の図では、3次元ということで立体で表現していますが、表をいくつか並べても同じ意味です。
デパートの案内地図のように、建物を階ごとにバラバラにしているのと同じですからね。

3次元以上になるとややこしくなるので、そんなに使う場面もないと思います。
でも、 「3次元配列を使えば丁度良いなー」てな場合の為に、使い方ぐらいは覚えておきましょう。

▲▲▲▲▲▲ ここまでは 2000/10/15 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【7】データをまとめて処理しよう


最近のゲームはデータの量が半端じゃないですね。
以前はデータと言えば敵キャラのステータスとか、画像とかだったんですが、最近はムービーというとんでもなく大容量のデータを扱っています。
個人で作るゲームには、そんなに大量のデータは大抵は必要ありませんが、それでも数100個ぐらいのデータを扱うことがあります。
もし、それを1つずつ変数に代入していったとしたら、その手間はとんでもないものになってしまいます。
だって、配列変数にしたとしても

D(0)=10:D(1)=12:D(2)=230:D(3)=352

こういう代入文を数100個も書かなくてはいけません。これじゃあ作業効率が悪くてしょうがないですよね。
そこで、こういったデータを一括に扱える、この命令たちを使います。

文法

DATA データ1[データ2][データ3]...
動作 データだけをまとめて記述する。
補足 なし。

そして対になる命令。

文法

READ 変数名1[変数名2][変数名3]...
動作 DATA文で記述したデータを、前から順に変数名に代入する。
補足 どこまでのデータを読み込んだのかを記憶しているので、何回に分けて実行しても結果は同じ。

それでは実際の使い方を見ながら説明していきましょう。次のプログラムを見てください。

10 'DATA-READ-1
20 CLS
30 READ A,B,C,D,E
40 PRINT A,B,C,D,E
50 END
60 DATA 10,20,30,40,50

このプログラムは、AからEの変数にそれぞれ10から50のデータを代入して表示しています。
READDATAの関係を調べるために、次のプログラムも見てください。

10 'DATA-READ-2
20 CLS
30 READ A,B,C
40 READ D,E
50 PRINT A,B,C,D,E
60 END
70 DATA 10,20,30,40,50

このように、READが2回に分かれたとしても結果は同じです。
BASIC本体はREADがどこまでのDATAを読み込んだのかを記憶していますから、何回に分けて読み込もうが同じことなんです。
次のようにDATA文がいくつかに分かれていても同じことが言えます。

10 'DATA-READ-3
20 CLS
30 READ A,B,C,D,E
40 PRINT A,B,C,D,E
50 END
60 DATA 10
70 DATA 20
80 DATA 30
90 DATA 40
100 DATA 50

▲▲▲▲▲▲ ここまでは 2000/10/16 に書いています ▲▲▲▲▲▲

さて、それでは前節で約束した「マップの表現方法」について説明しますね。
これを説明するために配列とDATAREADを使います。
次の図を見てください。

このように海を0、草原を1、山岳を2として配列に入れると、マップが表現できる。

マップを配列で表現するには、この図のように地形の種類ごとに番号を付けて、それを配列に代入します。
そして、表示や計算をしたい座標(X,Y)のマップをM(X,Y)のようにして求めれば、番号が返ってきますから地形がわかりますね。

では、次のプログラムの説明を。
マップ・データをDATAでまとめて入力して、それをREADで読み取って配列Mに代入し、INPUT文で入力した座標のマップ・データを表示します。

10 'やっぱ配列はマップでしょ
20 CLS:DIM M(10,10)
30 FOR J=1 TO 10
40   FOR I=1 TO 10
50     READ M(I,J)
60   NEXT I
70 NEXT J
80 '
90 INPUT "1から10で、X,Yと入力してください:",X,Y
100 PRINT "(";X;",";Y;")座標のマップ・データは";M(X,Y);"です"
110 END
120 '
130 DATA 1,1,1,1,1,1,1,1,1,1
140 DATA 1,0,0,0,0,0,0,1,0,1
150 DATA 1,0,0,0,1,1,1,1,0,1
160 DATA 1,0,0,0,1,0,0,0,0,1
170 DATA 1,0,0,0,1,0,0,0,1,1
180 DATA 1,1,1,0,1,0,0,0,1,1
190 DATA 1,0,0,0,1,1,1,0,1,1
200 DATA 1,0,1,0,0,0,0,0,0,1
210 DATA 1,0,1,0,0,0,0,0,0,1
220 DATA 1,1,1,1,1,1,1,1,1,1

今回のマップ・データは簡単に1を壁、0を空白にして、ダンジョン風にしてみました。
つまりM(X,Y)が0ならそこへは移動できて、1なら移動できないということです。
ゲーム中でキャラクタが移動していき、次の移動先が壁の中になった場合には、その移動を取り消して現在の位置に戻すようにします。
そうすれば、キャラクタが壁にぶつかって進めないように見えます(サンプル1)。

このプログラムで戸惑う人がいるらしいので、実際に使う場合のちょっとした注意を。
データは実際に表示される方向に書いてありますよね。つまり、データの上方向は画面の上方向だってことです。
ところが、30〜70行にある

30 FOR J=1 TO 10
40  FOR I=1 TO 10
50   READ M(I,J)
60  NEXT I
70 NEXT J

IJのループを逆にして

30 FOR I=1 TO 10
40  FOR J=1 TO 10
50   READ M(I,J)
60  NEXT J
70 NEXT I

とすると、画面が横倒しになってしまいます。やってみればすぐにわかると思います。
このままちゃんと表示されるようにしようとすると、今度はデータを横倒しに書くか、表示のときにXYを入れ替えなくてはいけないんです。
これじゃあデータは読みづらいし書きにくいし、表示の座標は(Y,X)になっちゃってわかりにくいですよね。

なんでこうなるかと言うと、使い慣れた数学っぽく、M(I,J)IX座標に、JY座標に対応させているからです。
データを正しい方向に書くと、READ文は横1列、つまりX座標を先に読み込みますよね。
横1列を読み込み終わったら、次の行を読むためにY座標に1を加えて、1行下げるんですから。
だから、X座標を変えていくループを内側に置かないと、読み込む順番とデータの関係がおかしくなってしまうんですね。

▲▲▲▲▲▲ ここまでは 2000/10/19 に書いています ▲▲▲▲▲▲

それでは、DATAREADに関係するもう1つの命令を。

文法

RESTORE 行番号
動作 次にREAD文で読み込むDATA文がある行番号を指定する。
補足 なし。

何かの理由でDATAを読み込む順番を変えたいときに使います。
例えば、同じデータを複数回読み込むときには、この命令を使えば無駄なデータを書かなくて済みますね。
では、実際の使用例を書いておきます。

10 'RESTOREの使用例
20 CLS
30 DIM D1(9),D2(9)
40 '
50 RESTORE 170
60 FOR I=0 TO 9
70   READ D2(I) : PRINT D2(I)
80 NEXT I
90 '
100 RESTORE 160
110 FOR I=0 TO 9
120   READ D1(I) : PRINT D1(I)
130 NEXT I
140 END
150 '
160 DATA 0,1,2,3,4,5,6,7,8,9
170 DATA 10,11,12,13,14,15,16,17,18,19

 

それではサンプルと解説です。

10 'サンプル1
20 CLS
30 DIM M(10,10) : X=2 : Y=2
40 '----------------------------------[READ MAP]
50 FOR J=1 TO 10
60   FOR I=1 TO 10
70     READ M(I,J)
80   NEXT I
90 NEXT J
100 '---------------------------------[WRITE MAP]
110 FOR J=1 TO 10
120   FOR I=1 TO 10
130     LOCATE I,J
140     IF M(I,J)=1 THEN PRINT "@"
150   NEXT I
160 NEXT J
170 '----------------------------------[MAIN]
180 *MAIN
190 X2=X : Y2=Y '現在地を記憶
200 I$=INKEY$
210 IF I$="2" THEN Y=Y+1
220 IF I$="4" THEN X=X-1
230 IF I$="6" THEN X=X+1
240 IF I$="8" THEN Y=Y-1
250 IF X<1 THEN X=1
260 IF X>10 THEN X=10
270 IF Y<1 THEN Y=1
280 IF Y>10 THEN Y=10
290 IF M(X,Y)=1 THEN X=X2:Y=Y2 '移動先が壁なら戻す
300 '
310 LOCATE X2,Y2 : PRINT " "
320 LOCATE X,Y : PRINT "*"
330 GOTO *MAIN
340 '----------------------------------[DATA]
350 DATA 1,1,1,1,1,1,1,1,1,1
360 DATA 1,0,0,0,0,0,0,1,0,1
370 DATA 1,0,0,0,1,1,1,1,0,1
380 DATA 1,0,0,0,1,0,0,0,0,1
390 DATA 1,0,0,0,1,0,0,0,1,1
400 DATA 1,1,1,0,1,0,0,0,1,1
500 DATA 1,0,0,0,1,1,1,0,1,1
510 DATA 1,0,1,0,0,0,0,0,0,1
520 DATA 1,0,1,0,0,0,0,0,0,1
530 DATA 1,1,1,1,1,1,1,1,1,1

長いので難しいと感じるかもしれませんが、今までやってきたことの組み合わせですから、ちゃんと読めばわかるはずです。
今までのことを思い出し、 じっくり読んで理解しましょう。
と、教科書には書かれていることが多いですけど、だからってわかるかってーの(苦笑)。
では、横線の区切りごとに解説していきます。

50〜90行は一番下に書いてあるデータを配列 M に読み込んでいます。
110〜160行はマップ表示です。もしデータが1だったら「@」を表示して壁を表現しています。0では何も表示しません。

180〜330行はプレイヤーキャラクターの移動が中心ですが、細かく分かれているので順に説明しますね。
まず、現在地を変数 X2 と Y2 に記憶しておきます(190行)。
次にキー入力です(200行)。
入力したキーごとに4方向に移動します(210〜240行)。
もし現在地が、データで使っている範囲(1〜10,1〜10)の外に移動しかけたら、その境界を越えないように修正します(250〜280行)。

注)【5】配列を覚えよう(1)でも書きましたが、配列の範囲外の添え字を与えるとエラーで止まってしまうので、この修正は重要です。このプログラムのデータは外枠を壁で囲んでいるのでエラーは起きませんが、もし外枠に穴が空いていた場合は、そこがバグへの抜け道になってしまいます。

もし移動先が壁なら、現在地をさっき記憶しておいた場所に戻します(290行)。
さっきまでいた位置の「*」を消し、新しい位置に「*」を書いて、移動したように見せます(310〜320行)。
これを繰り返して(330行)、ダンジョンを移動するプログラムを表現しています。

このプログラムは自分でいろいろ改造してみて欲しいんですが、データを変えるときにはキャラクターの初期位置に注意しましょう。
最初は(2,2)に設定してありますので、その場所に壁を置かないようにしてくださいね。エラーなく移動できますけど、壁が消えてしまいますから。

このプログラムを完全に自分のものにできれば、パックマン風のゲームに発展させられるかも。

▲▲▲▲▲▲ ここまでは 2000/10/22 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【8】ファイル操作でスコアを記録


それでは、セーブやスコアランキングなどに使えるファイル操作について説明していきます。
ファイル形式にはシーケンシャルファイルとランダムファイルの2種類があるのですが、ここではシーケンシャルファイルについて説明します。今のところ、ランダムファイルを知る必要はないでしょう。
ファイル操作に使う命令は、基本的なものだけなら4つあります。
ファイルを開く命令ファイルに書き込む命令ファイルから読み込む命令ファイルを閉じる命令の4つです。

まずはファイルを開く命令の文法を。

文法

OPEN "ファイル名" FOR モード AS #ファイル番号
動作

指定したファイルを開く。

ファイル名には、これから作業をするファイルの名前を指定する。ドライブ名付きでも構わない。

モードには次のいずれかを指定する。
INPUT
入力モードで開く(INPUT#命令)。ファイルが存在しない場合はエラーになる。
OUTPUT
出力モードで開く(PRINT#命令)。常に新規ファイルが作成される。
APPEND
追加出力モードで開く(PRINT#命令)。指定ファイルの最後尾に追加で書き込みされる。ファイルが存在しない場合は新規作成される。

ファイル番号は複数のファイルを開いたときに、どのファイルに対して作業するのかを区別するために付ける番号。1から順に付けておきましょう。

補足

注)これは基本動作についてしか書いていません。完全版はうろ覚えリファレンスマニュアルを見てください。

少しだけ例を書いておきますね。

OPEN "SCORE.TXT" FOR OUTPUT AS #1
OPEN "C:\BASICFILES\SAVE001.DAT" FOR INPUT AS #1
OPEN "DATABASE.DAT" FOR APPEND AS #1

さて、ここで2つの注意があります。
OUTPUTモードで開いたときは、毎回ファイルが破壊されてしまうので注意しましょう。
あと、APPENDモードがわかりにくいかもしれないので、どういうものか説明しておきましょうか。
まず、こういうファイルがあるとします。

賞金稼ぎ,000-000-0000,京都

このファイルにAPPENDモードで追加書き込みすると、

賞金稼ぎ,000-000-0000,京都,日本

という風になります。これを使えば、以前の内容に手を加えずに簡単に追加できますね

さて、ファイルに対してどういう操作をするのか決めたら、次は実際に読み書きしましょう。
まずはファイルに書き込む命令から。

文法

PRINT #ファイル番号出力フォーマット[]
または
PRINT #ファイル番号出力フォーマット[]
動作

ファイルへ書き込む。

出力フォーマットは、PRINT文で出力するときと同じ書き方で、が集まったもの。つまり、セミコロンかコンマで区切って並べる。セミコロンで区切った場合はの間に空白が入らないが、コンマで区切ると半角14桁ごとにが出力される。

ファイル番号で指定したファイルは、先にOUTPUTモードかAPPENDモードででオープンしておかないといけない。

補足

最後にセミコロンかコンマを付けると、ファイルの中で改行されないので、次のPRINT#文で続けて出力できる。

注)これは基本動作についてしか書いていません。完全版はうろ覚えリファレンスマニュアルを見てください。

同じく例です。

PRINT #1,A,B,C,D,E,F
PRINT #1,NAME$;TEL
PRINT #1,"NO.";NO,"NAME:";NAME$

それでは次にファイルから読み込む命令です。

文法

INPUT #ファイル番号変数[変数]...
動作

ファイルから変数に読み込む。

変数が数値変数の場合、ファイル中の空白・コンマ・改行文字がデータの区切りになる。文字変数の場合、コンマ、改行文字がデータの区切りになる。

ファイル番号で指定したファイルは、先にINPUTモードでオープンしておかないといけない。

補足 注)これは基本動作についてしか書いていません。完全版はうろ覚えリファレンスマニュアルを見てください。

同じく、使い方の例を書いておきます。

INPUT #1,A,B,C,D,E,F
INPUT #1,NAME$,TEL,ADDRESS$

最後に、ファイルを閉じる命令です。
この命令は必ず、ファイルを使い終わったら実行してくださいね。そうしないとファイルが開きっぱなしになってしまいます。

文法

CLOSE [#ファイル番号[,#ファイル番号]...]
動作

ファイル番号で指定したファイルを閉じる。指定を省略した場合は、そのときに開いている全てのファイルを閉じる。

補足

END命令は全ての使用中ファイルを閉じる隠れた効果があるので、必ずENDで終了させていればファイルを開きっぱなしにすることを防げる。

注)これは基本動作についてしか書いていません。完全版はうろ覚えリファレンスマニュアルを見てください。

それでは実際の使い方をつかむために、次のプログラムを見てください。
このプログラムを実行すると、TOP10.TXTというファイルが作られます。

10 'ファイル操作1
20 CLS
30 OPEN "TOP10.TXT" FOR OUTPUT AS #1
40   FOR I=0 TO 9
50     READ NAME$,SCORE
60     PRINT #1,NAME$;",";SCORE
70   NEXT
80 CLOSE
90 END
100 '
110 DATA AAA,10000
120 DATA BBB,9000
130 DATA CCC,8000
140 DATA DDD,6000
150 DATA EEE,4000
160 DATA FFF,3000
170 DATA GGG,1500
180 DATA HHH,1000
190 DATA III,500
200 DATA JJJ,200

次はTOP10.TXTの内容です。

AAA,10000
BBB,9000
CCC,8000
DDD,6000
EEE,4000
FFF,3000
GGG,1500
HHH,1000
III,500
JJJ,200

さらに作ったファイルを読み込んで表示するプログラムです。

10 'ファイル操作2
20 CLS : DIM NAME$(9),SCORE(9)
30 OPEN "TOP10.TXT" FOR INPUT AS #1
40   FOR I=0 TO 9
50     INPUT #1,NAME$(I),SCORE(I)
60   NEXT
70 CLOSE
80 '
90 FOR I=0 TO 9
100   PRINT "LANK ";I;" ";NAME$(I);" ";SCORE(I)
110 NEXT
120 END

 

それではサンプルです。

100 'サンプル1
110 CLS
120 OPEN "COPY_MOTO.TXT" FOR INPUT AS #1
130   OPEN "COPY_SAKI.TXT" FOR OUTPUT AS #2
140     FOR I=0 TO 9
150       INPUT #1,DATA '読み込んで
160       PRINT #2,DATA '書き写す
170     NEXT I
180   CLOSE #2
200 CLOSE #1
210 END

COPY_MOTO.TXT から COPY_SAKI.TXT へと内容を移すプログラムです。
ファイル番号の使い方の参考にしてください。

▲▲▲▲▲▲ ここまでは 2000/10/28 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


【9】ちょっとだけ高度なゲームを作ろう(1)


さてさて、とうとう第二章の最後まで来ました。
ここからは第一章と第二章の命令を全て使ったゲームを作っていきます。
わからない命令があれば、その説明がある節に戻ってもう一度読み返してみましょう。
それでは始めます……けど、全てを使って何ができるのか、どんなゲームが向いているのか考えてみます。

LOCATEPRINTでの表示 → テキストでのシンプルな画面が限界
INKEY$による入力 → アクション性あり
・ファイル操作による記録 → スコアランキング可能
・配列 → 表形式のデータを表現できる

となると……「モグラ叩き」なんて簡単で良いんじゃないでしょうか? 誰でも知っているシステムですし。よし、これで決定。

さて、普通は多くの実験をしながら少しずつ作っていくので、一緒にその流れを追いながらやっていきましょうね。
では、どこから作っていきましょうか。
僕の場合は先に画面関係を作ってしまいます。その方がイメージしやすいし、作っていて飽きないと思っているからです。
じゃあ画面表示をするには、どうすればいいのか?
と、いきなり考えていっても面白くないですね。まずは紙と鉛筆を用意して、どんな画面にするか描いてみましょう。

テンキーの位置に対応した穴からモグラが顔を出している

何でも良いんですが、試しに僕が描いたものを載せてみました。
これを見ると、「モグラ」、「LEVEL(難易度)」、「SCORE(叩いた回数)」、「MISS(失敗数)」が描かれていますね。

「これからがどうしたらいいのかわからないよー」なんて言わないでくださいね。実はこれを描いた時点で多くのことが決まっているんですから。なんなら、いくつか挙げてみましょうか。

・テンキーの位置に対応した9個の穴があり、そこからモグラが顔を出す。
・対応したキーを押して叩く。
・叩くと点数が上がる。
・何かの条件(例えば叩いた回数)でレベルが上がり、モグラが顔を出す時間が短くなる。
・時間内に叩けなければ、失敗数が増える。
・失敗数がいくらかになるとゲームオーバー。

という風に、この絵を描くために、どういうゲームが作りたいのかを既に考えているんですよね。そうじゃないと描けませんし。
この絵が再現できれば作りたいゲームは完成するわけですから、まずはこうして基準を立てましょう

では実際にプログラムを作り始めるとして、まずは、主役である「もぐら」の表示の仕方を考えてみましょうか。
本当は画像を使えればカッコイイですけど、今のところはPRINTしかないので文字で表現します。

'モグラ
PRINT " ┌───┐ "
PRINT " │○ ○│ "
PRINT " │   │ "
PRINT " │>○<│ "
PRINT " │   │ "
PRINT "─┴───┴─"

そして、叩いたときの絵と

'叩いたとき
PRINT "       "
PRINT " ┌───┐ "
PRINT " │× ×│ "
PRINT " │   │ "
PRINT " │>○<│ "
PRINT "─┴───┴─"

顔を出していないときの絵も作っておきます。

'穴だけ
PRINT "       "
PRINT "       "
PRINT "       "
PRINT "       "
PRINT "       "
PRINT "───────"

注)2バイト文字を使っているので、もしかしたらWindows以外では文字化けしてしまって、ちゃんと表示されないかもしれません。

モグラの状況に応じて絵を使い分けて表示すればいいですね。

▲▲▲▲▲▲ ここまでは 2000/11/02 に書いています ▲▲▲▲▲▲

【ちょっとした戯言】
今頃になって気が付いたけど、ここまで詳しく説明するのって第四章でやるべきことなのでは(苦笑)。もっと適当に省略しても良かったかもなぁ。まあ、そっちではグラフィックも使えるし、その違いということで納得するか……。今日、実際に完成させてみたけど、あれを全部説明するとなると、こりゃ骨だわ(汗)。がんばろ。

これでモグラの絵は表示できましたね。あとで実際に使うときには、LOCATEを加えて位置を調節します。
次に、9個の穴の状態をどうやって表現するか考えてみました。ここで言う状態とは、「モグラが顔を出しているかどうか」のことです。
あなたなら9つの変数を作って、その1つ1つに状態を持たせますか?
いやいや、せっかく表の形なんですから配列を使いましょうか。穴(モグラ)の状況に応じて、値を変化させます。丁度3種類の絵だったので、次のような対応表を作ってみました。

配列の値と状況の関係
マイナス

叩かれているモグラを表示。この値の大きさで、表示している時間もカウントする。

ゼロ

穴だけを表示。

プラス 普通のモグラを表示。この値の大きさで、顔を出している時間をカウントし、カウントした値が一定に達すると頭を引っ込めることにする。

これを絵で表現するとこうなります。

もう1つ配列を作って、頭を出している時間をカウントしても良かったんですけど、せっかく1つでも表現できるんだからと思って1つにまとめました。
変数の名前を、配列をHOLE(2,2)、レベルをLV、点数をSCORE、失敗数をMISSと決めると表示部分はこうなります。

'------------------------------------------画面表示
FOR J=0 TO 2
  FOR I=0 TO 2
    IF HOLE(I,J)=0 THEN GOSUB *HOLE
    IF HOLE(I,J)>0 THEN GOSUB *MOGURA
    IF HOLE(I,J)<0 THEN GOSUB *HIT
  NEXT I
NEXT J
LOCATE 60,5:PRINT "LEVEL":LOCATE 60,6:PRINT LV
LOCATE 60,9:PRINT "SCORE":LOCATE 60,10:PRINT SCORE
LOCATE 60,13:PRINT "MISS":LOCATE 60,14:PRINT MISS

GOSUBで飛んだ先に、先ほどのモグラの絵を表示するコードがあります。では、そのモグラの動きを考えてみます。

'------------------------------------------モグラの動き
FOR J=0 TO 2
  FOR I=0 TO 2
    IF HOLE(I,J)<>0 THEN HOLE(I,J)=HOLE(I,J)+1
    IF HOLE(I,J)=0 AND INT(RND(1)*35)=0 THEN HOLE(I,J)=1
    IF HOLE(I,J)>14-LV THEN HOLE(I,J)=0:MISS=MISS+1:IF MISS>10 THEN MISS=10
  NEXT I
NEXT J

最初のIF文で、穴の値が0でなければ配列変数HOLEに1を加えています。これはプラスとマイナスの両方のときに1加えるということで、要するにモグラが頭を出しているときに、その時間をカウントしているわけです。
次のIF文では、穴の値が0のときに1/35の確率で値を1にしています。0から1に変わるわけですから、頭を出し始めたということですね。
最後のIF文は、穴の値が(14−LV)を越えたら、まず値を0にして失敗数を増やしています。つまり、レベルに応じて顔を出している時間が変わり(レベルが上がれば短くなる)、制限時間を超えたら、頭を引っ込めて失敗数を増やしています。また、失敗数が上限の10を越えないように修正をしています。この文がないと、バグが出てしまいました。原因は、失敗数9のときに同時に頭を引っ込めたモグラがいた場合、一度に2を加えてしまうので11になったようです。10回の失敗でゲームオーバーにするので、これではおかしいですよね。

これでモグラが頭を出したり引っ込めたりするようになりました。今度は叩くのを作りましょうか。

'------------------------------------------入力
I$=INKEY$ : A=0 : B=0
IF I$="1" THEN A=0:B=2:GOSUB *CHECK
IF I$="2" THEN A=1:B=2:GOSUB *CHECK
IF I$="3" THEN A=2:B=2:GOSUB *CHECK
IF I$="4" THEN A=0:B=1:GOSUB *CHECK
IF I$="5" THEN A=1:B=1:GOSUB *CHECK
IF I$="6" THEN A=2:B=1:GOSUB *CHECK
IF I$="7" THEN A=0:B=0:GOSUB *CHECK
IF I$="8" THEN A=1:B=0:GOSUB *CHECK
IF I$="9" THEN A=2:B=0:GOSUB *CHECK

'---------------------------------------------合否判定
*CHECK
IF HOLE(A,B)=0 THEN MISS=MISS+1
IF HOLE(A,B)>0 THEN HOLE(A,B)=-2:SCORE=SCORE+1:COUNT=COUNT+1
IF COUNT=10 THEN COUNT=0:LV=LV+1
RETURN

入力と、そこからのサブルーチンを一緒に書いています。
さて、入力キーに応じてABに何やら意味不明な値が代入されていますね。これは、そのテンキーがどの穴に対応しているかです。対応表を載せておくので、コードと比べてみてください。

 

A=0 A=1 A=2
B=0

7

8 9
B=1 4 5 6
B=2 1 2 3

そして、叩いた場所の状態によって点数を増やしたり失敗数を増やしたりします。「合否判定」のサブルーチンを見てください。
最初のIF文で、穴が0だったら、つまりモグラがいなければ失敗数を増やしています。
次のは、値がプラスだったらなので、ちゃんとモグラを叩いたらですね。穴の値を−2にして、叩かれたモグラの絵が表示されるようにしています。そして点数を増やしていますね。ここでCOUNTという変数がありますけど、これは10点ごとにレベルを1上げるために、SCORELVの間にもう1つ変数を挟んで段階を踏んで、LVの上がる速度を調節しています。下の行も一緒に見てください。

さてさて、ここまででゲームを遊べるようにはなりましたので、全てをまとめて1つにしたものを載せておきます。まだランキングの処理を作っていないので完成ではないですが、まずは動かしてみましょう。
ただ、いきなりスタートしては問題なのでタイトル画面を付けておきました(50〜110行)。
それとゲームオーバー、ゲームクリアの処理も付けておきました(430〜440行)。

10 CLS:WIDTH 80,25:RANDOMIZE
20 DIM HOLE(2,2)
30 LV=1
40 '------------------------------------------タイトル画面
50 LOCATE 32, 8:PRINT "− モグラ叩き −"
60 LOCATE 27,12:PRINT "スペース/ゲームスタート"
70 LOCATE 27,14:PRINT "   Q/ランキング初期化(制作中)"
80 I$=INKEY$
90 'IF I$="Q" OR I$="q" THEN *NEWGAME
100 IF I$<>" " THEN 80
110 CLS
120 '------------------------------------------入力
130 I$=INKEY$ : A=0 : B=0
140 IF I$="1" THEN A=0:B=2:GOSUB *CHECK
150 IF I$="2" THEN A=1:B=2:GOSUB *CHECK
160 IF I$="3" THEN A=2:B=2:GOSUB *CHECK
170 IF I$="4" THEN A=0:B=1:GOSUB *CHECK
180 IF I$="5" THEN A=1:B=1:GOSUB *CHECK
190 IF I$="6" THEN A=2:B=1:GOSUB *CHECK
200 IF I$="7" THEN A=0:B=0:GOSUB *CHECK
210 IF I$="8" THEN A=1:B=0:GOSUB *CHECK
220 IF I$="9" THEN A=2:B=0:GOSUB *CHECK
230 '------------------------------------------モグラの動き
240 FOR J=0 TO 2
250   FOR I=0 TO 2
260     IF HOLE(I,J)<>0 THEN HOLE(I,J)=HOLE(I,J)+1
270     IF HOLE(I,J)=0 AND INT(RND(1)*35)=0 THEN HOLE(I,J)=1
280     IF HOLE(I,J)>14-LV THEN HOLE(I,J)=0:MISS=MISS+1:IF MISS>10 THEN MISS=10
290   NEXT I
300 NEXT J
310 '------------------------------------------画面表示
320 FOR J=0 TO 2
330   FOR I=0 TO 2
340     IF HOLE(I,J)=0 THEN GOSUB *HOLE
350     IF HOLE(I,J)>0 THEN GOSUB *MOGURA
360     IF HOLE(I,J)<0 THEN GOSUB *HIT
370   NEXT I
380 NEXT J
390 LOCATE 60,5:PRINT "LEVEL":LOCATE 60,6:PRINT LV
400 LOCATE 60,9:PRINT "SCORE":LOCATE 60,10:PRINT SCORE
410 LOCATE 60,13:PRINT "MISS":LOCATE 60,14:PRINT MISS
420 '--------------------------------------------------------
430 IF MISS>=10 THEN COLOR 4:LOCATE 22,10:PRINT "GAME OVER":COLOR 7 : END
440 IF LV>10 THEN COLOR 4:LOCATE 21,10:PRINT "GAME CLEAR!!":COLOR 7 : END
450 GOTO 130 'Return to main-loop.
460 '------------------------------------------モグラ表示
470 *MOGURA
480 LOCATE I*17+3,J*7  :PRINT " ┌───┐ "
490 LOCATE I*17+3,J*7+1:PRINT " │○ ○│ "
500 LOCATE I*17+3,J*7+2:PRINT " │   │ "
510 LOCATE I*17+3,J*7+3:PRINT " │>○<│ "
520 LOCATE I*17+3,J*7+4:PRINT " │   │ "
530 LOCATE I*17+3,J*7+5:PRINT "─┴───┴─"
540 LOCATE I*17+8,J*7+6:PRINT (I+1)+(2-J)*3
550 RETURN
560 '
570 *HOLE
580 LOCATE I*17+3,J*7  :PRINT "       "
590 LOCATE I*17+3,J*7+1:PRINT "       "
600 LOCATE I*17+3,J*7+2:PRINT "       "
610 LOCATE I*17+3,J*7+3:PRINT "       "
620 LOCATE I*17+3,J*7+4:PRINT "       "
630 LOCATE I*17+3,J*7+5:PRINT "───────"
640 LOCATE I*17+8,J*7+6:PRINT (I+1)+(2-J)*3
650 RETURN
660 '
670 *HIT
680 LOCATE I*17+3,J*7  :PRINT "       "
690 LOCATE I*17+3,J*7+1:PRINT " ┌───┐ "
700 LOCATE I*17+3,J*7+2:PRINT " │× ×│ "
710 LOCATE I*17+3,J*7+3:PRINT " │   │ "
720 LOCATE I*17+3,J*7+4:PRINT " │>○<│ "
730 LOCATE I*17+3,J*7+5:PRINT "─┴───┴─"
740 LOCATE I*17+8,J*7+6:PRINT (I+1)+(2-J)*3
750 RETURN
760 '---------------------------------------------合否判定
770 *CHECK
780 IF HOLE(A,B)=0 THEN MISS=MISS+1
790 IF HOLE(A,B)>0 THEN HOLE(A,B)=-2:SCORE=SCORE+1:COUNT=COUNT+1
800 IF COUNT=10 THEN COUNT=0:LV=LV+1
810 RETURN

いくつか初めての命令も出てきているので、説明しておきますね。こうした突然の新命令が必要になることが多いので、実際はどんな命令があるのか調べながら作っていきます。プログラマーに問われる必須条件というのは、そういった情報収集能力らしいです。わからない情報がどうやったら手にはいるのかってことです。それを身につけるのは至難の業ですけど、慣れれば自然とできるようになります。ただし、ヘルプファイルは自力で読めるようになりましょう。それが英語であっても……(苦笑)。

▲▲▲▲▲▲ ここまでは 2000/11/04 に書いています ▲▲▲▲▲▲

まずは10行の2つの命令から。

文法

RANDOMIZE []
動作

の値によって、乱数列を初期化する。を省略した場合は、そのときの時間から自動的に値が作成される。

補足 乱数列についてはRND関数を参照してください。

もう1つ。

文法

WIDTH 横幅縦幅
動作

文字を表示する横幅縦幅を設定する。横幅には40か80が、縦幅には20か25が指定できます。

補足

互換BASICでは独特の動作をしているようで、実行画面のウィンドウのサイズを決定しています。

【互換BASICのヘルプより】
縦幅に正の数を指定すると25行モード(行間隔16ドット)になり、負の数を指定すると20行モード(行間隔20ドット) になります。ただし、オリジナルのN88BASICとの互換性のために、「WIDTH 80,20」例外として20行モードになります。

まず、RANDOMIZE命令です。
この命令は以前に説明したRND関数のところで、少し触れていました。RND関数は実行する度に同じ乱数列しか使ってくれないんですが、このRANDOMIZE命令を使うと、どの乱数列を使うのかもランダムに決めることができます。
今回のプログラムでは、を省略して自動的に乱数列を初期化するようにしています。
(別編 覚えておけば役に立つ 【1】省略せず、自分で時間から値を得る方法)

そしてWIDTH命令です。
画面のレイアウトを考えていましたけど、実行する人によってウィンドウサイズがバラバラだと上手くいかないですよね。なので、この命令を使ってウィンドウサイズをプログラムの中から変更しています。
注)これは互換BASIC特有の効果なので、他のソフトでは必要ないかもしれません。

さらに430行辺りにCOLOR命令がありますが、これは【2】ゲームでよく使う命令たちで説明しておきます。
ゲームが終了したときに白い文字で「GAME CLEAR!!」と表示されても気づきにくかったので、緑色にしてすぐにわかるようにしました。

▲▲▲▲▲▲ ここまでは 2000/11/05 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲


【10】ちょっとだけ高度なゲームを作ろう(2)


それではランキングを実現するために、ファイル処理とデータの並び替え(sort・ソート)について説明します。
試しに作ってみるまで気が付かなかったんですが、ランキングにはソートという技術が必要なんですよね。これが思ったより難しくなったので別の節を用意しました。

まずは一番最初のランキングが入っているファイルを作るコードを考えましょう。

'---------------------------------------------ランキング初期化
*NEWGAME
OPEN "RANKING.TXT" FOR OUTPUT AS #1
PRINT #1,"キング,100,3"
PRINT #1,"クイーン,100,5"
PRINT #1,"ジャック,100,7"
PRINT #1,"エース,100,9"
PRINT #1,"ハート,90,10"
PRINT #1,"ダイヤ,80,10"
PRINT #1,"スペード,70,10"
PRINT #1,"クラブ,60,10"
PRINT #1,"ジョーカー,50,10"
PRINT #1,"B.H.(--;,40,10"
CLOSE #1
CLS
LOCATE 20,11:PRINT "初期化しました。もう一度実行してください。"
END

データの形式は「名前」、「点数」、「失敗数」の順です。それをRANKING.TXTというファイルに10個書き込んでいます。
これで読み込むデータは作れましたので、次は実際に読み込んでみます。

OPEN "RANKING.TXT" FOR INPUT AS #1
  FOR I=0 TO 9
    INPUT #1,NM$(I),SC(I),MS(I)
  NEXT I
CLOSE #1

名前をNM$(10)、点数をSC(10)、失敗数をMS(10)として11個の配列変数を用意して、そこへ10個のデータを代入します。配列は10個でいいような気がするでしょうけど、後でちょっとしたテクニックを使うからこれでいいんです(にやり)。わざと余分に1つ空けています。

ここで、まずソートのことは後回しにして下のコードを見てください。

CLS
PRINT "SCORE RANKING TOP 10"
PRINT
PRINT "   RECORD FORMAT = RANK. NAME/SCORE/MISS"
PRINT
FOR I=0 TO 9
  PRINT I+1;". ";NM$(I);"/";SC(I);"/";MS(I)
NEXT I
'
OPEN "RANKING.TXT" FOR OUTPUT AS #1
  FOR I=0 TO 9
    PRINT #1,NM$(I);",";SC(I);",";MS(I)
  NEXT I
CLOSE #1

前半部分では読み込んだデータを表示しています。
後半部分では、OUTPUTモードでRANKING.TXTファイルを新規作成して、そこへデータを書き込んでいます。

さてさて、2つ上の読み込みのコードから、1つ上の書き込みのコードまでの間に、ランキングの中に自分のデータを挿入する処理を入れないといけませんね。また、ランクインしたときには名前を入力できないといけないし、ランクインしなかったときは以前のままの内容をファイルに出力する必要があります。

データの挿入方法を考える前に、ランキングの順位の条件を決めます。
条件1:点数が大きい方が上位。
条件2:点数が同じ場合は、失敗数が少ない方が上位。

では実際に考えていきましょう。
ランキングに挿入するというのは、自分より点数が悪い記録の順位を1つずつ落として、その空いた場所に自分の記録を挿入するということですよね。そして当然、10位までに入らなかった記録はなくなります。

まず、自分のスコアがどこに挿入できるのかを調べるために、ループを使って順に比較していきます。
そして、挿入できる順位が見つかったら、その順位以下の記録の失敗数と、自分の失敗数をループを使って順に比較していきます。
これで挿入できる場所を特定できます。コードの形としては、

1000 FOR I=0 TO 9
1010   IF SC(I)>SCORE THEN 1040 '比較して負けてるなら次の記録へ
1020   IF SC(I)<SCORE THEN
Z=I : GOSUB *RANKIN : GOTO 1040 '挿入位置を発見
1030   IF SC(I)=SCORE THEN GOSUB *SORT_MISS 'この先で失敗数について同じようなループ
1040 NEXT I
1050 'ランキングの表示へ

2000 '自分より悪い記録の順位を下げ、記録を挿入し、名前を入力する
2010 *RANKIN
2020 FOR N=9 TO Z STEP -1
2030   SWAP NM$(N),NM$(N+1) '2つの変数の中身を入れ替える命令です。後で説明します
2040   SWAP SC(N),SC(N+1)
2050   SWAP MS(N),MS(N+1)
2060 NEXT N
2070 SC(I)=SCORE
2080 MS(I)=MISS

2090 CLS
2100 PRINT "ランクインしました!"
2110
INPUT "名前を入力してください";NM$(I)
2120 RETURN

というのが考えられます。

……が、僕はここまで考えて止めました(笑)。これじゃあ構造がが複雑になってしまって、バグができそうな感じがしまくってます。真っ向勝負じゃ負けそうなので、やり方自体にひねりを利かせて、構造を簡単にしてみました。

FOR I=0 TO 9  '--------ランクインしたかどうか先に調べる
  IF SC(I)*100+10-MS(I) <= SCORE*100+10-MISS THEN SW=1
NEXT I
IF SW=1 THEN GOSUB *RANKIN

'---------------------------------------------ランクイン
*RANKIN
CLS
PRINT "ランクインしました!"
INPUT "名前を入力してください";NM$(10)
SC(10)=SCORE
MS(10)=MISS
FOR X=0 TO 9
  FOR Y=X+1 TO 10
    S1=SC(X)*100+10-MS(X)
    S2=SC(Y)*100+10-MS(Y)
    IF S1<=S2 THEN SWAP NM$(X),NM$(Y):SWAP SC(X),SC(Y):SWAP MS(X),MS(Y)
  NEXT Y
NEXT X
RETURN

読み込みと書き込みの間に入るコードと、そこからのサブルーチンを一緒に書いています。

前半のコードで、ランクインしたかどうかだけを調べて、ランクインしていたら挿入処理のサブルーチンに飛んでいます。
なぜこうしたか説明しましょうか。
例えば、このループの中に挿入処理を書いたり、ループの中からGOSUBを使って挿入処理へ飛んだとします。そして、挿入が終わって戻ってきます。そのときにループの内側にいると、もう既に挿入しているのに比較を続けてしまいますよね。それを回避する方法を考えるのが面倒だったからです。また複雑なコードになってしまいますしね。

ここでちょっとしたテクニック! 2行目の複雑な式を見てください。

SC(I)*100+10-MS(I)

この式を比較するだけで、ランクインしたかどうかを判別できています。さっき考えた複雑な構造のコードと比べれば、明らかに簡単ですよね。これは順位の条件1と条件2を、計算式で表しているんです。では、どうやって?
まず、点数50を100倍して、10から失敗数3を引いた値を加えてみます。すると、

50*100+10-3 = 5007 = "05007"

となります。また、点数100、失敗数0なら、

100*100+10-0 = 10000 = "10000"

ですし、点数0、失敗数10なら、

0*100+10-10 = 0 = "00000"

ですよね。さて、もうわかりましたか? ランキングの順位は、「点数が多いほど上位」という条件1を優先するので、それを高い位の数字にし、「点数が同じなら失敗数が少ない方が上位」という条件2は下位条件なので、これを低い位の数字にしているんです! ただし、条件2は「失敗数が少ない方が」なので、最大失敗数である10から失敗数を引いた値にしています。
こうすれば、この計算式で出てきた答えが大きい記録ほど上位にランキングされることがわかります。

ランキングに入っていることがわかったら、今度はどこへ挿入するのかです。
さっきの複雑なコードでは、「1つずつずらしていく」という作業をやったおかげでとんでもなく複雑になってしまいました。これももっと簡単に並び替えることができます。
さっき配列を1つ余分に宣言しておきましたよね。その余った部分を使います。
まず、あの配列の10番目に、「名前」、「点数」、「失敗数」を代入しておきます。

PRINT "ランクインしました!"
INPUT "名前を入力してください";NM$(10)
SC(10)=SCORE
MS(10)=MISS

そして、ここからソートのアルゴリズムとして一番簡単なものを使います。

その前にアルゴリズムという言葉を説明しておきましょうか。アルゴリズムというのは、「やり方」、「考え方」のことです。さっきまで考えていた、「挿入する場所を探す方法」というのもアルゴリズムを考えていたことになります。有名なアルゴリズムも存在して、これから使うソートのアルゴリズムは中でも代表的なものです。そういった定石を学ぶのも、プログラムの技を磨く1つの方法ですね。ちなみに僕はほとんど知りませんが(苦笑)。

DIM A(9)
FOR I=0 TO 4
  READ A(I)
NEXT I
DATA 5,10,6,3,1,20,8,5,0,12

まずは上のようにして配列変数Aに値を代入します。この配列変数Aの中身を大きい順に並び替えたいとき、次のようなアルゴリズムで実現できます。

1.まず、A(0)A(1)A(9)を比較していき、A(0)より大きいものがあればその都度データを入れ替える。これにより、A(0)には一番大きいデータが入る。
2.次に、A(1)A(2)A(9)を比較していき、A(1)より大きいものがあればその都度データを入れ替える。これにより、A(1)には二番目に大きいデータが入る。
3.今度は、A(2)A(3)A(9)を比較していき、A(2)より大きいものがあればその都度データを入れ替える。これにより、A(2)には三番目に大きいデータが入る。
4.以下同様に、A(8)A(9)を比較するまで繰り返す。
5.1から4までの処理は、次のように2重ループで行う。

FOR X=0 TO 8
  FOR Y=X+1 TO 9
    IF A(X)<=A(Y) THEN SWAP A(X),A(Y)
  NEXT Y
NEXT X

ここで新しい命令、SWAPを説明しておきます。

文法

SWAP 変数1変数2
動作

2つの変数の中身を入れ替える。

補足 なし。

ただ、この命令を使わずに2つの変数の中身を入れ替える方法も知っておきましょう。
NMの中身を入れ替えたいとします。すると、

W=N
N=M
M=W

という3つの文で入れ替えられます。一度、別に用意した変数に値をコピーして保管しておくのがコツですね。

さて、ソートのアルゴリズムは理解してもらえたでしょうか。紙にデータを書いてみて、どういう動きをするのか追ってみるとわかりやすいと思います。一度はやってみてくださいね。
これと同じ方法で、ランキングの記録を並び替えます。下にプログラムを抜粋しておきました。

FOR X=0 TO 9
  FOR Y=X+1 TO 10
    S1=SC(X)*100+10-MS(X)
    S2=SC(Y)*100+10-MS(Y)
    IF S1<=S2 THEN SWAP NM$(X),NM$(Y):SWAP SC(X),SC(Y):SWAP MS(X),MS(Y)
  NEXT Y
NEXT X

ここで2行目に注目してください。配列の10番目までをソート対象にしていますよね。ここまで言えば、もう気が付いた人もいるかな? どうして配列を11個も宣言したかというと、このアルゴリズムでソートするデータの中に、自分の記録をただ放り込んでおくだけでいいからですね! こうすれば、「記録を1つずつずらしていく」なんていう複雑なことはしなくてもすみます。前に10位だった記録は、配列の11番目に押しだされて自然と記録されません。

また、ランクインしたかどうかを先に調べているので、11位になること、つまりランク外にはじかれることはありませんから、名前の入力を毎回する設定でいいんです。その点を気にしなくても良い辺り、やはり先に調べた効果は大きかったですね。

この処理が終わると、NM$SCMSの中身は、ちゃんと順位の高い順番に並び替えられています。

3〜4行目は、ただ5行目が長くなりすぎて予定の画面サイズに収まらなくなってしまったので、短くするために変数を使っただけです。

ここまでで「モグラ叩きゲーム」は完成しました。それでは完成版をどうぞ。
ただし、前に載せたプログラムに、この新しいプログラムの一部を追加しないでください。いろいろと細かいところが変わっているので、ランキング処理の行を追加しただけでは動きませんのであしからず。

10 CLS:WIDTH 80,25:RANDOMIZE
20 DIM HOLE(2,2),NM$(10),SC(10),MS(10)
30 LV=1
40 '------------------------------------------タイトル画面
50 LOCATE 32, 8:PRINT "− モグラ叩き −"
60 LOCATE 27,12:PRINT "スペース/ゲームスタート"
70 LOCATE 27,14:PRINT "   Q/ランキング初期化"
80 I$=INKEY$
90 IF I$="Q" OR I$="q" THEN *NEWGAME
100 IF I$<>" " THEN 80
110 CLS
120 '------------------------------------------入力
130 I$=INKEY$ : A=0 : B=0
140 IF I$="1" THEN A=0:B=2:GOSUB *CHECK
150 IF I$="2" THEN A=1:B=2:GOSUB *CHECK
160 IF I$="3" THEN A=2:B=2:GOSUB *CHECK
170 IF I$="4" THEN A=0:B=1:GOSUB *CHECK
180 IF I$="5" THEN A=1:B=1:GOSUB *CHECK
190 IF I$="6" THEN A=2:B=1:GOSUB *CHECK
200 IF I$="7" THEN A=0:B=0:GOSUB *CHECK
210 IF I$="8" THEN A=1:B=0:GOSUB *CHECK
220 IF I$="9" THEN A=2:B=0:GOSUB *CHECK
230 '------------------------------------------モグラの動き
240 FOR J=0 TO 2
250   FOR I=0 TO 2
260     IF HOLE(I,J)<>0 THEN HOLE(I,J)=HOLE(I,J)+1
270     IF HOLE(I,J)=0 AND INT(RND(1)*35)=0 THEN HOLE(I,J)=1
280     IF HOLE(I,J)>14-LV THEN HOLE(I,J)=0:MISS=MISS+1:IF MISS>10 THEN MISS=10
290   NEXT I
300 NEXT J
310 '------------------------------------------画面表示
320 FOR J=0 TO 2
330   FOR I=0 TO 2
340     IF HOLE(I,J)=0 THEN GOSUB *HOLE
350     IF HOLE(I,J)>0 THEN GOSUB *MOGURA
360     IF HOLE(I,J)<0 THEN GOSUB *HIT
370   NEXT I
380 NEXT J
390 LOCATE 60,5:PRINT "LEVEL":LOCATE 60,6:PRINT LV
400 LOCATE 60,9:PRINT "SCORE":LOCATE 60,10:PRINT SCORE
410 LOCATE 60,13:PRINT "MISS":LOCATE 60,14:PRINT MISS
420 '--------------------------------------------------------
430 IF MISS>=10 THEN COLOR 4:LOCATE 22,10:PRINT "GAME OVER" : GOTO *RANKING
440 IF LV>10 THEN COLOR 4:LOCATE 21,10:PRINT "GAME CLEAR!!" : GOTO *RANKING
450 GOTO 130 'Return to main-loop.
460 '------------------------------------------モグラ表示
470 *MOGURA
480 LOCATE I*17+3,J*7  :PRINT " ┌───┐ "
490 LOCATE I*17+3,J*7+1:PRINT " │○ ○│ "
500 LOCATE I*17+3,J*7+2:PRINT " │   │ "
510 LOCATE I*17+3,J*7+3:PRINT " │>○<│ "
520 LOCATE I*17+3,J*7+4:PRINT " │   │ "
530 LOCATE I*17+3,J*7+5:PRINT "─┴───┴─"
540 LOCATE I*17+8,J*7+6:PRINT (I+1)+(2-J)*3
550 RETURN
560 '
570 *HOLE
580 LOCATE I*17+3,J*7  :PRINT "       "
590 LOCATE I*17+3,J*7+1:PRINT "       "
600 LOCATE I*17+3,J*7+2:PRINT "       "
610 LOCATE I*17+3,J*7+3:PRINT "       "
620 LOCATE I*17+3,J*7+4:PRINT "       "
630 LOCATE I*17+3,J*7+5:PRINT "───────"
640 LOCATE I*17+8,J*7+6:PRINT (I+1)+(2-J)*3
650 RETURN
660 '
670 *HIT
680 LOCATE I*17+3,J*7  :PRINT "       "
690 LOCATE I*17+3,J*7+1:PRINT " ┌───┐ "
700 LOCATE I*17+3,J*7+2:PRINT " │× ×│ "
710 LOCATE I*17+3,J*7+3:PRINT " │   │ "
720 LOCATE I*17+3,J*7+4:PRINT " │>○<│ "
730 LOCATE I*17+3,J*7+5:PRINT "─┴───┴─"
740 LOCATE I*17+8,J*7+6:PRINT (I+1)+(2-J)*3
750 RETURN
760 '---------------------------------------------合否判定
770 *CHECK
780 IF HOLE(A,B)=0 THEN MISS=MISS+1
790 IF HOLE(A,B)>0 THEN HOLE(A,B)=-2:SCORE=SCORE+1:COUNT=COUNT+1
800 IF COUNT=10 THEN COUNT=0:LV=LV+1
810 RETURN
820 '---------------------------------------------ランキング初期化
830 *NEWGAME
840 OPEN "RANKING.TXT" FOR OUTPUT AS #1
850 PRINT #1,"キング,100,3"
860 PRINT #1,"クイーン,100,5"
870 PRINT #1,"ジャック,100,7"
880 PRINT #1,"エース,100,9"
890 PRINT #1,"ハート,90,10"
900 PRINT #1,"ダイヤ,80,10"
910 PRINT #1,"スペード,70,10"
920 PRINT #1,"クラブ,60,10"
930 PRINT #1,"ジョーカー,50,10"
940 PRINT #1,"B.H.(--;,40,10"
950 CLOSE #1
960 CLS
970 LOCATE 20,11:PRINT "初期化しました。もう一度実行してください。"
980 END
990 '----------------------------------------------ランキング
1000 *RANKING
1010 LOCATE 13,16:PRINT "スペースキーを押してください":COLOR 7
1020 WHILE INKEY$<>" ":WEND
1030 '
1040 OPEN "RANKING.TXT" FOR INPUT AS #1
1050   FOR I=0 TO 9
1060     INPUT #1,NM$(I),SC(I),MS(I)
1070   NEXT I
1080 CLOSE #1
1090 '
1100 FOR I=0 TO 9  '--------ランクインしたかどうか先に調べる
1110   IF SC(I)*100+10-MS(I) <= SCORE*100+10-MISS THEN SW=1
1120 NEXT I
1130 IF SW=1 THEN GOSUB *RANKIN
1140 '
1150 CLS
1160 PRINT "SCORE RANKING TOP 10"
1170 PRINT
1180 PRINT "   RECORD FORMAT = RANK. NAME/SCORE/MISS"
1190 PRINT
1200 FOR I=0 TO 9
1210   PRINT I+1;". ";NM$(I);"/";SC(I);"/";MS(I)
1220 NEXT I
1230 '
1240 OPEN "RANKING.TXT" FOR OUTPUT AS #1
1250   FOR I=0 TO 9
1260     PRINT #1,NM$(I);",";SC(I);",";MS(I)
1270   NEXT I
1280 CLOSE #1
1290 END
1300 '---------------------------------------------ランクイン
1310 *RANKIN
1320 CLS
1330 PRINT "ランクインしました!"
1340 INPUT "名前を入力してください";NM$(10)
1350 SC(10)=SCORE
1360 MS(10)=MISS
1370 FOR X=0 TO 9
1380   FOR Y=X+1 TO 10
1390     S1=SC(X)*100+10-MS(X)
1400     S2=SC(Y)*100+10-MS(Y)
1410     IF S1<=S2 THEN SWAP NM$(X),NM$(Y):SWAP SC(X),SC(Y):SWAP MS(X),MS(Y)
1420   NEXT Y
1430 NEXT X
1440 RETURN

 

さてさて、第二章はこれでお終いです。
いやー、この章は長かったですね(苦笑)。もっと簡潔に書いても良かったんでしょうけど、それだと誰にでもわかる講座っていう目標から外れてしまうので、文字数が増えても、ちゃんと理解できるように書いたつもりです。その点、どうだったでしょうか?

ここまで読んでくださったあなた、ちゃんと理解してもらえたでしょうか? もしわからない所があったら、掲示板なりメールなりで質問してくださいね。時間と知識があればお答えしますので。今まで説明したことを全てマスターしていれば、それなりのゲームは作れるはずですから、何か作ってみるのもいいかもしれません。自信にも繋がりますしね。

次の第三章では、お待ちかね(?)のグラフィックを使っていきます。本格的なゲームのアルゴリズムについても載せていくので、お楽しみに。

注)変数について、全く触れていない重要なことがありました。この第二章の【1】第一章の補足からで、後日補足しておきます。

▲▲▲▲▲▲ ここまでは 2000/11/05 に書いています ▲▲▲▲▲▲

▲もくじに戻る▲

 


▲トップページに戻る▲