ワンポイントテクニック No.022 G800シリーズ対応

GOTOでテーブル処理


 テンキーによる8方向移動のように法則性があるIF文の場合はそれを数式で表すことでIF文による判定を大幅に減らすことが可能なのですが、実際はそういうテクニックが使える機会はそれほど多くはありません。
 ゲームでキー判定を行う場合、同時キー入力を使って2人対戦を行う場合は使うキーが3つとしてもキー入力判定だけを見ても最大で1人当たり7通り、2人対戦時には14通り必要になってきます。メインルーチン内にキー判定のみで14個もIF文が羅列していては処理が遅くなるというのは想像しただけでも理解できると思います。

 そこで便利なのがON〜GOTOです。ただON〜GOTOではアスキーコード32([SPACE])〜245([PF5])までのキーを使用する場合、214個もジャンプ先を羅列する必要があり効率が非常に悪いものとなってしまいます。
 ON A GOTOで実際にジャンプ先を増やして指定したところその処理時間はジャンプ先10個で3.8m秒30個で7.5m秒もかかりました。ジャンプ先が1個増える毎に処理時間が約0.02m秒増える計算になるので200個も指定したらとんでもなく時間がかかってしまいます。(ジャンプ先がブランクでも1個当たり約0.01m秒増える)

 そういう時に役に立つのがGOTO文のテーブル処理です。シャープのポケコン用BASICは一般的なBASICとは異なり、GOTOのジャンプ先に変数が使えます。GOTO 変数という形で使用することでIF文で判定することなく特定の行にジャンプさせキー入力後の処理をすることができます。そのためには事前に必要な分だけ配列変数を確保してそれにジャンプ先の行番号を書き込んでおく必要があります。

10 DIM Z(252)
20 FOR I=0 TO 252:Z(252)=120:NEXT
30 FOR I=1 TO 15:READ A,B:Z(A)=B:NEXT
中略
100 'メインルーチン
110 GOTO Z(ASC INKEY$)
中略
200 'キーニュウリョクショリ
中略
1000 DATA 13,210,32,250,49,300,50,310,51,320,52,330,54,340,55,350,56,360,57,370,241,400,242,410,243,420,244,430,245,440

 テンキーで8方向移動&スペース&リターンキー&PFキー5個の計15個のキーを使う場合でも110行のGOTO文1つですべて賄うことが可能になります(上記リストはテーブル処理の準備方法の例なので処理先の実行文を用意しておらず、実際にキー入力を行うとエラーとなるので注意)。そのためにはキーコード、その処理を行う行ナンバーをペアにしたデータを用意してそれを30行で書き込んでおきます。
 しかし、これだとそれ以外のキーを押したときに0行にジャンプしてしまうためGOTOのある行の次の行にジャンプするようにあらかじめデータを入れておきます。上記サンプルでは110行にGOTO文があるため指定しているキー以外を入力した場合は120行にジャンプするようにするために20行で120という値を書き込んでいます。

 これをもしもON〜GOTO〜1つのみでで記せば110行は下記のようになります。

110 ON (ASC INKEY$)-12 GOTO 210,,,,,,,,,,,,,,,,,,,250,,,,,,,,,,,,,,,,,300,310,320,330,,340,350,360,370
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,400,410,420,430,440

 残念ながらこれはポケコンでは1行に収まる文字数を超えてしまうために実現はできません。したがって、ON〜GOTO〜は2回に分けて処理する必要があります。(というか、これだけブランクだらけだと打ち間違いは必至かも)
 ちなみにGOTO Z(A)という処理はジャンプ先がいくらに増えようとも5.7m秒で済みます。つまり、ジャンプ先が20個を越えればON A GOTO〜よりも速くなるのです。(2回に分けることでジャンプ先を減らして1つあたりの処理時間を減らしても2回に分けた時点で遅くなってしまう)

 配列変数を200以上も確保するというのはメモリ効率が悪いのですが、これによって得られる高速化を考えると難しいところです。使うキーがテンキーとアルファベットのみというのであればZ(アスキーコード90)が最大であるため配列変数は90確保すればよいのですが、その場合PFキーを押した場合エラーになるため押したキーが90以下かどうか判定するIF文が別途必要になります。(IF文1つ分遅くなるためON〜GOTO〜を使った場合との差はかなり縮まってしまう)

 配列変数90個でもメモリの無駄とか、使うキーを制限されたくないとか言うのであれば配列変数ではなくPOKEを使いメモリ上にジャンプ先の行番号を書き込んでおくといいでしょう。そうすればGOTO PEEK (S+K)(Kには押したキーコード、Sにはキーコードが0の場合のアドレスが入る)とするだけで上記と同じように使用できます。すべてのキーを使うようなゲームでも最大255バイト確保するだけで済みますからね。それに加えて配列変数を使うよりも速いというメリットもあります。
 ただし、この場合注意すべきことは1バイトでジャンプ先を記している関係上使える値は0〜255ということです。つまり、キーが押されて処理するルーチンは255行までに抑えておく必要があるということです。だから、キー入力処理ルーチンはプログラムリストの先頭に近い部分においておくと良いでしょう。GOTO文は小さい行番号の方がジャンプが高速であるために先頭に近い部分におくことで高速化も期待できます。

コラム GOTOの高速化

 一般的にいってBASICなどのインタープリタ言語では先頭に近い行にジャンプする方が処理が速い。これは先頭行から実行しているので当然なのだけどPC-E500シリーズではGOTOのジャンプ先は一度実行されたらメモリ上の実アドレスに書き変えられるためにGOTO命令が非常に高速なものとなっており、先頭に近い方が高速という一般論は通じなくなっている。
 しかし、これは固定の行番号や*ラベルを使っている場合の話であってGOTOに変数や”ラベル”を使っている場合は一般的なインタープリタ型BASICと同じように先頭に近い方が速くなっている。
 したがって、GOTO命令を高速化する場合はどのような形で使用するかが重要になり、普段はGOTOに変数や”ラベル”を使う人は少ないと思われるためにPC-E500シリーズにおいてはGOTO命令は特に速度を意識して使う必要はないと思われる。


 このGOTOのテーブル処理の実例はシュプールのリストを見てもらえば分かりますが、煩雑なキー入力判定が500行のK=PEEK 242:GOSUB PEEK (772352+K)だけで実現できています。このテクニックを使用せずIF文でキー入力判定を行うならばキー判定だけでIF文が18個必要になりますのでこの効果がいかに大きいか分かると思います。
 なお、シュプールではGOTOのジャンプ先をPEEKを使って読み出している関係上、255行より後ろの行にジャンプできないため最も高速な先頭行にジャンプして、その行からGOTOのある行の次の行にジャンプするという措置をとっています。(2バイト使えば直接すべての行にジャンプできるようになるけど速度面でのメリットがかなり薄れてしまう)

 2人用の同時キー入力は私が普段使用している簡易設定でも冒頭に書いたように最大14個のIF文が必要になり、このテクニックが非常に役に立つと思います。ただし、速度は速くなる半面プログラムリストが冗長になりがちなので8方向移動のようにパターン化されたIF文の簡略化が可能であればそちらを使用した方がいい場合もあるでしょう。
 このテーブル処理を行った場合はRENUMができないため一旦GOTOの前にREM(’)をつけて、RENUM後はそれを削るといった措置も必要になるために開発しにくくなるという問題点もあります。しかし、この辺のデメリットを考慮しても高速化のメリットは大きいので使うキーが多くなおかつ速度を重視するアクション系のゲームなどでは積極的に使っていってもいいかもしれません。


RETURN

inserted by FC2 system