プチコンmkII

プチコンmkII 初心者講座

サンプルプログラム7 (その3)

プログラマーからひとこと

サンプルプログラム7を見ていくのも、とうとう3回目です。
全4回でまだ1回分ありますが、今回がクライマックスです。
なぜならページの長さがいちばん長いからです。
ボス戦前のダンジョンとかこんな感じですよね。

今回は1章ごとにけっこうドンドン進みます。
そのスピードにおいていかれることもあるかもしれませんが、そんな時ははじめに戻って、きっちりわかったあたりで次に進むくらいがいいかもしれません。
なんだか勉強っぽくてイヤかもしれませんが、なにしろラストダンジョンです。
ワンパク君たちが迷うところではいっしょに迷いつつ、いっしょに戦っていきましょう。
ところで、「ワンパク君」とかあたりまえのように呼んでますが、あれ、本名なの?


敵を出してみよう

インテリ「ここまででプレイヤー側の動きはだいたいできあがったね。いよいよ敵の動きのプログラムに入ってみようか!
神崎「今までよりも大変そうなヨカン……。
インテリ「ははは。たしかにタクサンの敵がジグザグに動き回るから、これまでよりもフクザツではあるね。
最初はカンタンに、スタートしたときのならび方から始めようか。
ワンパク「カンタンって言っても、こういうワケじゃねェよな?
0010#. LOCATE 0,3
0011#. PRINT ”Е Е Е Е Е Е Е Е Е Е”
0012#. LOCATE 0,5
0013#. PRINT ”Е Е Е Е Е Е Е Е Е Е”
サスガにココまでカンタンじゃねェとは、オレにもワカるぜ。
神崎「たしかに、そうかも。サンプルをRUNしてみると、敵Еは1匹ずつ順に動いてるんだよね。ショットでやられたらその敵だけが消えるわけだし……。
インテリ「つまり1匹ずつ、あわせて20匹ぶんの変数がいるわけだね。……こう言えば、今までにおぼえたコトが役に立つんじゃないかな?
ワンパク「そうか! 20個の変数をヨウイするんだな!
インテリ「い、いや……
神崎「……じゃなくって、配列変数を使うんだよね。
インテリ「そうそう、ソコだよ!
0008#. DIM EX(20):DIM EY(20):DIM ED(20)
ゲームで使うのはこんなところかな。敵は英語で「Enemyエネミー」だから、変数の頭文字には「E」をつけることにするよ。
神崎「EXEYはやっぱり、ヨコ(X)とタテ(Y)かな?
インテリ「そうそう。ヨコがXでタテがYという変数の名付け方は、はっきりルールがあるワケじゃないけど、プログラマーの人はたいていこう書くね。おぼえておいていいんじゃないかな。
変数EDの「D」は「Dynamicsダイナミクス」のDなんだけど、……まあ、「動き方」くらいに考えておけばいいかな。
ワンパク「やけにハッキリしない言い方じゃねェか。ナニか言いたくないイミでもあんのか!?
インテリ「「力学」とか「変動」とか……
ワンパク「おベンキョウの話だったか、キタイはずれだゼ!
神崎「ま、まあ分かりにくいのは本当かな。とにかく変数EDは敵がどう動くかのフラグにするってことだね?
インテリ「そういうコト。とりあえず最初はにしておいていいかな。
ハカセ「ネンのタメに、ここで配列変数をどう使うのかまとめておこうかのう。
たとえば1匹めの敵は配列の番号を(0)に統一するのじゃ。こやつのいる場所を調べたければ、横方向なら配列変数EX(0)、縦方向ならEY(0)じゃな。同じように2匹めの敵なら、EX(1)EY(1)で場所が決まっているワケじゃ。
それとタダの書き方のモンダイじゃが、
0008#. DIM EX(20):DIM EY(20):DIM ED(20)
というふうにコロン()で分けなくても、
0008#. DIM EX(20),EY(20),ED(20)
と、ひとつのDIM命令でスッキリ書くこともできるぞい。
ワンパク「じゃあ、その3つの配列変数を使いながら敵をナラべてみるゼ!
0011#. FOR I=0 TO 9
0012#.  EX(I)=I*2
0013#.  EY(I)=3
0013#.  ED(I)=0
0014#.  LOCATE X(I),Y(I)
0015#.  PRINT ”Е”;
0016#. NEXT
0017#. FOR I=10 TO 19
0018#.  EX(I)=I*2-20
0019#.  EY(I)=5
0020#.  ED(I)=0
0021#.  LOCATE X(I),Y(I)
0022#.  PRINT ”Е”;
0023#. NEXT
……け、けっこうメンドくせェな、コレ!
ハカセ「イヤイヤ、なかなかジョウデキじゃぞい。トクに18行目なんかはクロウのアトがみえるのう。
神崎「敵が2列にならんでいるから、FOR文を2回に分けて作らないといけなかったんだね。
たしかに「FOR I=0 TO 19」ひとつで終わらせる方法ってないもんなあ。IF文で変数が10を超えたら……いやいや、それもプログラムが長くなっちゃう……
ワンパク「このタメだけに似たような行を2つ書くのがなんたってメンドクセェぜ!
インテリ「実は、すごく短くする方法があるんだけど……
ワンパク「な、ナニ!? キキズテならねえな!

短かくしてみよう

インテリ「さっきのワンパク君のプログラムでも必要なコトはできてるから、わざわざ短くしなくてもいいんだけど……
ワンパク「イヤイヤ、さっきのプログラムを打つのはかなりメンドウだったんだぜ。短くなるならカンゲイじゃねェか?
ハカセ「ならば、こう書くとかなり短くなるのう。
0011#. FOR I=0 TO 19
0012#.  EX(I)=FLOOR(I%10)*2
0013#.  EY(I)=FLOOR(I/10)*2+3
0014#.  ED(I)=0
0015#.  LOCATE EX(I),EY(I)
0016#.  PRINT ”Е”
0017#. NEXT
ワンパク「ハ……ハァ!?
神崎「たしかに短いけど……よくわからない……
ワンパク「そうか、まだ習ってねェFLOORがカギだな! これにスゲェ意味が……
インテリ「FLOORは「切り捨てて整数にする」命令だよ。たとえばA=FLOOR(1.75)と書けば変数には1.75から「.75」を切り捨ててが入るね。
ワンパク「ふ、フツウだぜ!
ハカセ「FLOORフロア」すなわち「たいらにする」というイミじゃから、整数にするというイミに変わったのじゃな。
12行目の計算をカンタンに言いなおすと、「変数を10で割ったあまりから小数点以下を切りすてて2倍する」となるのう。
ワンパク「ぜんぜんカンタンじゃねー!
神崎「うーん、なるほど……たしかにEX(0)=0EX(1)=2EX(2)=4と2ずつ増えていって、EX(10)になったとたんEX(10)=0に戻っているね。やりたかったコト通りではあるんだけど……なんだかフシギだなあ。
ハカセ「13行目も、「を10で割って小数点以下を切りすてたら、2倍して3を足す」だけじゃがこれで敵の1~10匹めまではタテ位置行目に、11~20匹めまではタテ位置行目にそろうというワケじゃ。
ワンパク「ウ……ウウ……。ケイサンしてみたらタシカにその通りだったが……セイカツの中でまったく出てこねェ式にもホドがあるぜ!
インテリ「あんまりパッと出てくる計算じゃないかもね。
でも「FLOOR(I/N)ならが『の倍数』になるたび1増える」。
FLOOR(I%N)ならが『の倍数』になるたびゼロまでおり返しながら1増えていく」。
原理まで考えなくても、こう丸アンキしておくとプログラムで使えるバメンはけっこう多いよ。
ハカセ「たとえば時計を考えてみい。1時間は60分で、0分~59分をくり返すものじゃが、この「分」はFLOOR(I%60)で書きあらわせるのう。「時間」はFLOOR(I/60)と書けばいいワケじゃ。
かなりカタいたとえになってしまったが、サンプルプログラムのように、敵をセイレツさせるようなコトにも応用できるんじゃぞい。
神崎「プログラマーの生活のチエってところかな。
インテリ「そういうコト。そうそう、ここでは「FLOOR(I%N)」なんて書いたけど、本当は「割り算のあまり」に少数が出てくるハズはないから、「I%N」と書くだけでいいんだよ。
ハカセ「ワシは……ワシは、見た目をそろえておきたかったのじゃああああ

敵を動かそう

ワンパク「敵をならべた後は、イヨイヨ動かすワケだが……
神崎「今までとちがって1方向や2方向じゃないのが、ナヤむところだね。
インテリ「迷ったら、まずセイリしてみよう!
0右に動くX=31になるまでX+1
1下におりる1回だけY+1
2左に動くX=0になるまでX-1
3下におりる1回だけY+1
キホン的には、このくり返しだよね。
ワンパク「そう言われれば、そう……なのか?
神崎「ショットで撃たれたり、下までおりて上にもどるのが抜けてるけど……
インテリ「そこはもっとアトから考えよう! まずこの表にある動きをクリアーしないとそこまでたどりつかないしね。
ワンパク「そのワリキリ、キライじゃねえな!
じゃあとにかくサブルーチン「ALIENエイリアン」をメインルーチンから呼び出すことにするぜ!
0023#.  GOSUB @ALIEN
で、アトは……1匹ずつ動かすコトだけ考えるゼ!
0080#. ’--- シンリャクシャ
0081#. @ALIEN
0082#. FOR I=0 TO 19
0083#.  LOCATE EX(I),EY(I):PRINT” ”
0084#.  GOSUB @EMOVE
0085#. NEXT
0086#. RETURN
動かすトキのキホンだから、” ”で消すだけは消しといたゼ。
こっからサキはナガくなりそうなんで@EMOVEGOSUBさせといたが、モンダイはどの方向に動かすか……だな。
インテリ「なかなかワルくないよ。飛んだ先でまた別のラベルに飛ぶけど、こうすると分かりやすいんじゃないかな。
0088#. @EMOVE
0089#. ON ED(I) GOTO @RI,@DW,@LF,@DW
ED(I)=0右に動くX=31になるまでX+1
ED(I)=1下におりる1回だけY+1
ED(I)=2左に動くX=0になるまでX-1
ED(I)=3下におりる1回だけY+1
この表のとおりだね。フラグに使ってる変数ED(I)の数しだいで、ラベル@RI@DW@LF@DWのどれかに飛ぶってやり方さ。
神崎「ああそうか、ラベルはそれぞれ「RIght」「DoWn」「LeFt」になってるんだ。
ワンパク「とりあえず、右に動かすコトだけ考えるゼ!
0091#. @RI
0092#. EX(I)=EX(I)+1
0098#. LOCATE EX(I),EY(I)
0099#. PRINT ”Е”
0100#. RETURN
コレで……まあ、右にだけはウゴくだろうな。
神崎「ずいぶんドンドン行くなあ。
でも、のこりの方向も同じように動かせそうだね。
0102#. @DW
0103#. EY(I)=EY(I)+1
0104#. LOCATE EX(I),EY(I)
0105#. PRINT ”Е”
0106#. RETURN
インテリ「やってるコトがカブる行が多いから、ゼンブをまとめるとこうなるかな。
0091#. @RI
0092#. EX(I)=EX(I)+1
0093#. GOTO @EPUT
0094#.
0095#. @DW
0096#. EY(I)=EY(I)+1
0097#. GOTO @EPUT
0098#.
0099#. @LF
0100#. EX(I)=EX(I)-1
0101#. GOTO @EPUT
0102#.
0103#. @EPUT
0104#. LOCATE EX(I),EY(I)
0105#. PRINT ”Е”
0106#. RETURN
ラベル名の「PUTプット」は日本語では「置く」というイミになるかな。敵キャラを画面に置くルーチンってことだね。
ワンパク「ココまではよし。……というか、まだ右に行くコトしかデキてねェな。フラグがED(I)=0のまんまだからな。
神崎「いまRUNしても、右ハジまで来たら表示がおかしくなるよ。
ワンパク「マカセとけ! たとえば@RIなら、右ハジに来たときだけフラグを変えてやりゃいいんだな?
0091#. @RI
0092#. EX(I)=EX(I)+1
0093#. IF EX(I)<31 THEN @EPUT
0094#. ED(I)=1
0095#. GOTO @EPUT
てコトは、@LFでも同じようにできるハズだゼ!
0101#. @LF
0102#. EX(I)=EX(I)-1
0103#. IF EX(I)>0 THEN @EPUT
0104#. ED(I)=3
0105#. GOTO @EPUT
インテリ「ふむふむ。
ED(I)=0右に動くX=31になるまでX+1@RI
ED(I)=1下におりる1回だけY+1@DW
ED(I)=2左に動くX=0になるまでX-1@LF
ED(I)=3下におりる1回だけY+1@DW
フラグED(I)になると、次はラベル@DWに行くことになるね。
神崎「@DWでは下におりるのは1回だけだから、すぐフラグを変えることになるね。
0097#. @DW
0098#. EY(I)=EY(I)+1
0099#. IF ED(I)==1 THEN ED(I)=2
0100#. IF ED(I)==3 THEN ED(I)=0
0101#. GOTO @EPUT
こうやってIF文を使うことになるのかな?
ワンパク「ン? ……ああそうか、さっきまで右に動いていたとき(ED(I)==1)と、さっきまで左に動いていたとき(ED(I)==3)で、次に立てるフラグがカワッてくんのか。
インテリ「せっかくだから、今まででおぼえたコトを使って、もっとシンプルにしてみようか。
0097#. @DW
0098#. EY(I)=EY(I)+1
0099#. ED(I)=(ED(I)+1) AND 3
0100#. GOTO @EPUT
ワンパク「……ナニ?
神崎「あ、ビット演算かあ……。
ワンパク「ケッ! オリコウさんドモはいつもこうだ! ウーム、のトキは2進数で10で、は2進数で11だから……ブツブツ……
神崎「えーと、ようするに99行ではED(I)のナカミが1だけふえるけど、になったときだけはにもどされるってコトだよね?
インテリ「ワリとカンタンなんだけどなあ。
ワンパク「デキるヤツの考えのオシツケはマッピラだぜ!
インテリ「ヒドい言われようだよ。まあ、ビット演算の話はもっと後からくわしくおぼえることにしようか。
そうそう、画面の下まで行ったらまた上から出てくる動きも足しておこうね。
0099#. IF EY(I)>20 THEN EY(I)=3
ワンパク「ナルホドな。コレでとりあえず、敵の動きはひととおりデキたか……。
ずいぶんトントンビョウシじゃねェか。RUNしてみるか!

神崎「……なんか、ちがう……
ワンパク「ウオォー! ナンだこの動き、ナンかチガうぞコレ、っつかキモチワリー! なんかキモチワリー!
ハカセ「ふぉっふぉっふぉ。ようやく気づいたようじゃのう。めざしていた正しい動きはこっちじゃよ。

ワンパク「そうだコレだ。敵は下におりるトキは、1匹ずつじゃなくマトメて下におりるんだ。チクショウ、どこでマチガエたんだ!?
インテリ「ここまでにミスがあったワケじゃないよ。まだそういう動きをプログラムしてないっていうだけさ。

足りないところをつけ足してみよう

インテリ「ちょっとアタマをヒネるところだよ。いいかい?
今までは「1匹ずつ、右ハジ・左ハジに来たら下に行く」ことにしていたね。
神崎「うん、そのハズ。……そうだよね?
ワンパク「ムシロ、そのドコがワルいのかがワカらねーぜ!
インテリ「ところが、ボクらがやろうとしてたのは「どれか1匹が右ハジ・左ハジに来たら、次のターンでは敵全部がその場から下に行く」動きなんだよ。
神崎「……!
ワンパク「つまりアレか、今までやってたのは行列を作って歩く「ムカデ歩き」の動きで、本当にやりたいのは行進の「右向け、右!」でゼンインがいつでも同じ方向をむく動きってコトか?
ハカセ「わりとヨウチじゃが、悪くないたとえじゃぞい。ココまでわかれば、アトはできるんではないかの?
ワンパク「待て待て待て、やるべきコトはだいたいワカったが、けっこうコンランするぜ!
インテリ「急にハッソウを変えるところだからね。順にかたづけていこうか。
神崎「まず、方向を変えるきっかけは「どれか1匹が右ハジ・左ハジに来たら」だよね。ここから考えてみようよ。
ワンパク「それは、ツマリ……@RI@LFの中の、IF文にひっかからなかったトキじゃねェか!
0091#. @RI
0092#. EX(I)=EX(I)+1
0093#. IF EX(I)<31 THEN @EPUT
0094#. ED(I)=1
0095#. GOTO @EPUT
この94行目のあたりだな!
インテリ「そうだね。ここでED(I)のフラグを変えてるけど、これだと(I)に入った1匹ずつ、右ハジにつくまでフラグが変わらないね。
ワンパク「ソレじゃマズいんだよな……そのカワリに、ゼンインのフラグを変える方法……?
神崎「もう1つ、大きなフラグを作ったらいいんじゃないかな!
0094#. EDW=TRUE
94行目を新しい変数EDWのフラグを立てるように書きかえたよ。
このフラグが立ってる時は、どこにいる敵でもフラグED(I)を下におりるように変えたらどうかな?
ワンパク「フラグED(I)を変えるフラグがEDWか……ドンドンややこしくなってくるぜェ……。
インテリ「ここがサイゴのカベだよ、がんばろう!
神崎「画面のハジに来たことはEDWで分かるようになったから、「次のターンで敵ぜんぶがその場から下に行く」動きをプログラムすればいいんだよね。
さっきのビット演算ED(I)=(ED(I)+1) AND 3を使えばそれでいいかな。
ワンパク「ムムム……。次のターンってことは、だいぶ前にモドるがこのあたりか。
0080#. ’--- シンリャクシャ
0081#. @ALIEN
0082#. FOR I=0 TO 19
0083#.  LOCATE EX(I),EY(I):PRINT” ”
0084#.  GOSUB @EMOVE
0085#. NEXT
0086#. RETURN
ココのFOR~NEXTで敵を動かしてたハズだぜ!
神崎「いちど敵のターンが終わってからED(I)のフラグを変えるわけだから……アレ? フラグを変えていい場所がないよ!?
ワンパク「ナニ言ってんだ……オ、オォ!?
なんてこった、タシカにそうだぜ! 83行目のアトでフラグを変えてもシッパイだし、85行目のアトじゃFOR~NEXTループが終わってるからED(I)って書いてもナンにもならねェぞ!
インテリ「ソコがモンダイだね。フラグEDWが立つのは敵のターンのトチュウだから、まず敵のターンが終わるまで待たなきゃいけない。でも敵のターンが終わったFOR~NEXTの外だとED(I)を変えるコトができない。
ワンパク「ハマリじゃねェか! どうしようもねえ!
神崎「うーん、FOR~NEXTが終わったあとに、もう1回こういうFOR~NEXTを置くのはどう?
0087#. FOR I=0 TO 19
0088#.  IF EDW THEN ED(I)=(ED(I)+1) AND 3
0089#. NEXT
0090#. EDW=FALSE
ED(I)を変えるためだけのFOR~NEXTなんだけど……
ワンパク「やるじゃねェか。コレでバッチリに見えるぜ?
ハカセ「なかなかワルくないぞい。しかし、似たようなFOR~NEXTを2回ツカうのはカイゼンのヨチありじゃ。
神崎「
ハカセ「このテイドの長さなら気にならんじゃろうが、FOR~NEXT文は増えれば増えるほど動作に時間がかかってしまうモノなのじゃ。ミライあるショクンには、これでマンゾクせずにFOR~NEXTの少ないプログラムをメザしてもらいたいのう。
神崎「ムズカしいんだなあ。
ワンパク「そうなるとマスマスどうすりゃイイのかワカらねえじゃねーか!
インテリ「ここは……もう1つフラグを使うことにしようか。
ED(I)敵1匹ずつ(I)の動きを決めるフラグ
EDW次のターンで敵ぜんぶが下におりるフラグ
MD今のターンで敵1匹ずつ(I)下におりるフラグ
この表のとおり、モンダイの動きをコントロールするための変数MDを使ってみるよ。
ワンパク「フラグMDが1匹ずつ動かすのはイイのか……? ああ、1匹ずつと言っても、FOR~NEXTのターンまるごとMDフラグで動かしてるから、ケッキョク敵ゼンブまとめて動いてるのか……
マジでヤヤコシイな、ついてこれてるのか自分でもワカらねーぜ!
インテリ「プログラムとしては、こういうことになるね。よく見直してみよう。
0080#. ’--- シンリャクシャ
0081#. @ALIEN
0082#. MD=FALSE
0083#. IF EDW THEN MD=EDW:EDW=FALSE
0084#. ’---
0085#. FOR I=0 TO 19
0086#.  LOCATE EX(I),EY(I):PRINT” ”
0087#.  IF MD==FALSE THEN @EMV
0088#.  ’--- 
0089#.  ED(I)=(ED(I)+1) AND 3
0090#.  ’---
0091#.  @EMV
0092#.  GOSUB @EMOVE
0093#. NEXT
0094#. RETURN
EDWフラグのことはFOR~NEXTの外でかたづけて、ジッサイにED(I)を変えるのはMDフラグにマカせてあるのさ。
神崎「83行目でEDWフラグのナカミをMDフラグに引きつがせて、EDWフラグじたいは初期化してるんだね。
インテリ「ココまでのプログラムを全部書くと、こんな感じかな。
プログラムリストを表示(※表示ボタンがあるのはWeb版に限られます。別ページのリストをごらんください)
ワンパク「ナンドでも言うが、ヤヤコシイぜ!
ハカセ「ひとつずつモンダイをカイケツしていくうちに、いつしかこうなってしまったワケじゃが、だれにでも見やすいプログラムかといえばギモンかもしれん。
全てがアタマに入ったところで、あらためて見やすいプログラムに書きなおすのもダイゴミじゃぞ。今すぐやるべきコトではないかもしれんが……
インテリ「この講座がゼンブ終わったあとで、みんなやってみてね!
ワンパク「マル投げのフンイキがプンプンにおうぜ!

今回のまとめ

配列宣言をいちどにすませる
DIM A(10),B(10),C(10)
DIMの後にで区切って配列宣言を並べることができます。
FLOOR命令
(変数)=FLOOR(
数から小数点以下を切り捨てて整数にします。
FOR I=0 TO 100
 A(I)=FLOOR(I/5)
NEXT

こう書くと、変数A(I)に入る数は、変数が「5の倍数」になるたび1増えます。
A(0)から順に、0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,……と入っていくわけです。
FOR I=0 TO 100
 A(I)=I%5
NEXT

こう書けば、変数A(I)に入る数は1ずつ増えていき、それが「5の倍数」になるたび0に戻ります。
A(0)から順に、0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,……と入っていくわけです。