読者です 読者をやめる 読者になる 読者になる

職業プログラマの休日出勤

職業プログラマによる日曜自宅プログラミングや思考実験の成果たち。リアル休日出勤が発生すると更新が滞りがちになる。記事の内容は個人の意見であり、所属している(いた)組織の意見ではない。

ドラゴンクエスト1に学ぶ「乱数」

数日前、iOS / Android 向けにDQ1ドラゴンクエスト1)が100万人限定で無料配信されるということがあり、ネット上でもリアルでも、周囲はDQ1をプレイしている人が大勢居られました。僕は今そんなもんで遊び始めたら生活が破綻するので落としに行ってすらいません。遊びたいのは山々なんですけどね。

その大勢の方々の中には当然ながらその日のうちに竜王を倒してしまう人も居られたようです。そういう話をネット上で聞いたときに、そういえばDQ1攻略の最速記録ってどれくらいなんだろう?と思ってググったら、こんなのがヒットしました。

日々是遊戯:初代「ドラクエ」のおそるべきタイムアタック動画。竜王逃げてー! - ねとらぼ
※SFC(スーパーファミコン)版。45分05秒でのクリア動画の埋め込み(ニコニコ動画)あり。

これは2011年時点の最速だそうで、現在でも最速記録なのかどうかは調べていませんが、異常に速いのは確かです。で、この中で使われているテクニックとして紹介されているのが「状況再現」です。これについての解説が、以下のサイトにあります。(上記のリンクからもリンクされています)

SFC版DQ1 状況再現について

動画は「すげー」という驚きの言葉と共に見させて頂きましたが、これらのサイトの説明文を読んでいて、乱数についての話に少し違和感を覚えました。ここで少し乱数について語っておきたいと思います。

乱数とは

文字通りの意味は「乱れた数」ということで、サイコロを振ることによく例えられます。
サイコロであれば、(通常は)1から6までの目が、それぞれ同じ確率で出ることになります。コンピュータの乱数も似たようなもので、処理系にもよりますが、0以上1未満の実数や、0以上n未満の整数を生成していることがほとんどです。ところが、実はコンピュータにとって本当の意味での乱数を作るのは難しいのです。多くの場合(筆者の知る限りでは「全て」ですが、例外が無いことを否定できない)、コンピュータでは「疑似乱数」というのを使っています。

疑似乱数とは

あたかも乱数であるかのように見えるけど実際は乱数じゃない数のことです。多くの場合は「再現性」があります。
一般的に、乱数を使う際は、最初に「乱数の種」を使って「乱数列の初期化」をします。同じ乱数の種で初期化され、同じ手法で生成された乱数列は、まったく同じ並びになります。

例えば、C言語のプログラム

#include <stdio.h>
#include <stdlib.h>
 
int main(void){
	int i;
	srand(42);
	for(i=0; i<10; i++) {
		printf("%d\n",rand());
	}
}

は、筆者の環境では何度実行しても以下のような出力をします。

705894
1126542223
1579310009
565444343
807934826
421520601
2095673201
1100194760
1139130650
552121545

擬似乱数についての、より正確な説明は Wikipediaの記事 を参照して下さい。

擬似乱数を生成する仕組みについては、C言語の世界になりますが、 C言語による乱数生成 とかがわかり易いんじゃないかなと思います。

SFC版ドラゴンクエスト1で起きていること(推測)

さて、冒頭で紹介したSFC版DQ1の最速クリア動画の話に戻ります。
こういったゲームの世界では乱数というのはよく使われるもので、普通の人間にとっては再現性の無い理想的な乱数であるかのように観測されます。しかしながら、この動画の主のような特殊な技能と根性を持つ人だったり正確な操作をするTAS(Tool-Assisted Speedrun)が繰り出す「状況再現」という技は、ゲームで使われている乱数には再現性のある擬似乱数が使われていることを示しています。

どのように疑似乱数列を使っているのか?

ゲームの世界に限らず、時刻を乱数の種として用いて疑似乱数を使う方法はよくあることです。C言語であれば、srand()関数に時刻(の数値)を渡すことで実装します。この方法は

  • 乱数を必要とする度に乱数列を初期化する必要がある
  • 使用している時計の精度(秒単位とかミリ秒単位とか)に見合ったタイミングで乱数を取得することで「状況再現」が可能

という特徴を持ちます。使っている時計の精度が秒単位とか0.1秒単位しか無いときは違う手法を採用することもあるでしょうけど、大抵はこの手法でしょうし、SFC版DQ1も恐らく同様でしょう。更に、SFCでは時計とは言っても内蔵の時計はありませんので、ゲーム開始からの経過時間を数えているだけの時計でしょう。だから状況再現が可能なのです。


こういったコンピュータの常識から考えれば、動画の主さんが SFC版DQ1 状況再現について で述べられていることというのは、かなりメチャクチャなことが多いです。しかしながら、これらの考えや手法は「乱数を調整」(って何やねん!)することではなくて、「乱数の種である時計」に合わせて人間が操作するための手段としてはかなり有用かつ現実的なものでしょう。この努力と操作精度の高さには、本当に頭が下がります。

さいごに

iOS / Android 版でも状況再現を試みる人、きっと出てくるんでしょうね。でもスーパーファミコンとかの時代とは違って、今はハードウェアに内蔵の時計(電源を切ってもリセットされない!)がありますので、それを乱数の種に使われたら、我々が再現性を検知することはほとんど無いでしょう。。