2007/9/22

乱数初期化話  VRMスクリプト禅問答
予定調和的ですけども。
だがしかし2、おっ!結構いいじゃんって思ったカメラアングルも、運転を終了して再度起動すると同じパターンでアングルが推移する。要するに乱数生成のパターンが常に同じってことであろう。時間等を使って生成パターンを変えられればいいのにな〜。

について。

現行のVRM4でビュワー起動毎に異なる乱数列を得たいとすれば、
<レイアウトスクリプト>

Var InitRnd1
Var InitRnd2
SetEventTimer this StartInitRnd InitRnd1 1
SetEventKey this StopInitRnd InitRnd2 {適当なキー}

BeginFunc StartInitRnd
  Var tmp
  rnd tmp
EndFunc

BeginFunc StopInitRnd
  KillEvent InitRnd1
  KillEvent InitRnd2
EndFunc
みたいなのを組み込んだ上で、編成の走行開始を手動化するのが暫定解なのかな、と思います。著しくエレガントではないですが。

以下、わかる人はもうわかっているし、わからない人は説明しても無駄だろう、と思いつつ解説します。


上掲のサンプルコードがやっているのは、

(1) ビュワー起動後、1ミリ秒毎にrnd命令を実行し続ける(し続けるだけで、得た値は使わない)。
(2) {適当なキー}が押された時点で(1)を止める。

ただ、それだけです。

VRMに限らず、PCで扱われる乱数の多くは“擬似乱数”です。擬似乱数の実装方法はいろいろありますが、最もシンプルなのは、n回目に得る値を、n-1回目に得た値を適当な関数に放り込んで得る、というものです。VRMとは関係なくなっちゃいますが、以前にボクが好んで使っていた方法を例にご説明しますと、
LD A,(RNDWK)
LD B,A
ADD A,A
ADD A,A
ADD A,B
INC A
LD (RNDWK),A
これはZ80という、現在のCoreDuoとかの遠い遠い先祖の親戚筋に当たる8ビットCPU用のプログラムなんですが、おそらく最もシンプルな擬似乱数の実装例です。ちなみに、このコードの一部(RND:で検索すると見つかります)で、全体としてはこんなゲームになります。

何をしているかと言うと、RNDWKという名前の箱に収めた数字を取ってきて、これを5倍して1加えて箱に戻す、ただそれだけです。なお、箱の大きさは8ビットに制限されているので、255になって1を加えると0に戻る、という制約つきの箱です。こんな単純なロジックでも(ゲーム用途であれば)十分に擬似乱数として機能します。
<レイアウトスクリプト>

Var Timer
SetEventTimer this RND Timer 1000

Var RNDWK

BeginFunc RND
  Var A
  mov A RNDWK
  mul A 5
  add A 1
  and A 255
  mov RNDWK A
  DrawVar A
EndFunc
上のコードは、前掲のZ80用擬似乱数生成ロジックを、そのままVRMスクリプトに直し、1秒毎に得られた乱数をログウィンドウに表示するようにしたものです。暇な人はお手元のVRM4で実行してみてください。一見して、こんな単純な生成ルールがあるとは思えない一連の数字が続きます。そして(当然のことながら)ビュワーを起動し直すと、まったく同じ一連の数字が再び表示されます。

VRMのrnd,irnd命令で得られる乱数も、複雑さの程度こそ異なれ、同じ原理で生成されていると考えて差し支えありません。なので、ビュワーを起動する都度、同じ乱数列が得られることになります。

tetta氏の言う“時間等を使って生成パターンを変えられればいいのに”は、上例のRNDWKのビュワー起動直後の最初の値に、現在時刻(を加工して得られる値)を使いたい、と言う話です。こうすれば、理屈の上ではまったく同じ乱数列が得られるのは24時間ごと、となり、経験上はビュワーを起動する毎に異なる乱数が得られることになります。

残念ながら、現在のVRM4にはVRMビュワーの外側の世界の時間を得る命令がありません。そこで(やっと話が元に戻りますが)PCとは違って真正にランダムな「人間がキーを押すタイミング」を利用しましょう、という話になります。擬似乱数は、ビュワー起動毎に常に同じ数列を返しますが、これを何番目から使い始めるかを変えることができれば、それ以降に得られる乱数列も当然変わってくる、というお話です。

具体的には、最初に示したサンプルコードの{適当なキー}が、自動運転の開始キーを兼ねていたりするのがいいと思います。ビュワー起動からキーを押すまでの間、メソッドStartInitRndが無意味にrnd命令を実行しまくって乱数列を進めてくれます。そして、ビュワー起動から人間がキーを押すまでの時間は毎回変化しますし、ましてや1ミリ秒単位で同じタイミングでキーを押すなんてことは滅多に起こらないので、結果的に、以降のレイアウト上のすべてのスクリプトロジックは、ビュワー起動毎に異なる乱数列を使って動作するように見えることになります。

*     *     *

なお、本稿中で紹介した「(8ビット範囲内で)5を掛けて1を加えるを繰り返す」擬似乱数の生成法はボクのオリジナルではありません。ボク自身は、徳間書店が出していた雑誌『MSXFAN』で読んで知ったものです。流石に、もー全部捨てちゃったので何号に載っていたかはわかりません、あしからず。
0

2007/9/23  3:12

投稿者:tetta
本当に実現しようと思うならば
鳴かぬならなかせてみせようV○Mの精神が必要そうですね。
高級言語に慣れ親しんでしまうと、基本原理への意識が遠のいてしまいます・・・歳とともに関数ありきの他力本願。
言葉足らずの文章へ補足もして頂きありがとうございます。

流石といえば其れまでですが、このブログ上にニーモニックが並ぶのは予想外でした。(笑)

コメントを書く

この記事にはコメントを投稿できません




teacup.ブログ “AutoPage”
AutoPage最新お知らせ