0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

サーバーを持たずに「今日は全員同じ問題」を実現する

0
Posted at

導入

最近、AIが何でも自動でやってくれる話をよく見かけますが、今回の話は真逆です。
乱数を「毎回ちゃんとバラバラに」出すために、自分でハッシュ関数を書いた話です。
地味ですが、地味なだけに知らないとハマる類のものでした。

目的

個人開発しているPWAloki_zenbuには、2048や反応速度ゲームなどのミニゲームが
あります。友人と点数を比べられるように、その日にプレイした人は全員、同じ盤面・
同じ出現順に挑戦する
デイリーチャレンジという仕組みを作りました。

このアプリはサーバーを持たない、ブラウザだけで完結する設計です。「今日のお題」を
どこかで計算して配ることができないので、サーバーなしで全員に同じ結果を持たせる
必要がありました。

実装

コンピュータの乱数生成には、同じ「種(シード)」から始めれば常に同じ数列が出る、
という性質があります(疑似乱数生成器、PRNG)。「今日の日付」をシードにすれば、
サーバーなしで全員が同じ盤面にたどり着けるはずだと考えました。

// 端末のローカル時刻から YYYY-MM-DD 相当の整数を作る
function dailySeed(date: Date = new Date()): number {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  return year * 10000 + month * 100 + day;
}

日付の取得はtoISOString()(UTC基準)ではなく、ローカル時刻を使うメソッドに
しています。UTC基準だと、日本時間の夜の時間帯はまだ前日扱いになり、デイリー
チャレンジの切り替わりが体感時刻とズレるためです。

このシードを、シード付き乱数生成器mulberry32にそのまま渡していました。動かして
みて、同じ日なら同じ盤面が出ることも確認できたので、一度はこれで完成としました。

その後、日付を1日ずつずらして何日か分の盤面を見比べたところ、隣り合う日の
最初の数手がやけに似ていることに気づきました。調べると、この手の軽量PRNGには
「内部状態が近いシードを渡すと、最初の数回の出力も近くなりやすい」という性質が
あるとわかりました。2026081520260816のようにシードの数値がほぼ同じだと、
まさにこれを踏み抜くことになります。

対策として、シードをPRNGに渡す前に、ハッシュ関数で一度撹拌することにしました。

function hashSeed(seed: number): number {
  let h = seed >>> 0;
  h = Math.imul(h ^ (h >>> 16), 2654435761);
  h = Math.imul(h ^ (h >>> 13), 2246822519);
  h ^= h >>> 16;
  return h >>> 0;
}

これはmurmur3というハッシュアルゴリズムで使われている「アバランチ」という
処理です。入力を1ビット変えるだけで出力が大きく変わるという性質があり、
2026081520260816のように近い入力でも、ハッシュ後の値はまったくの
別物になります。最終的に「日付 → 整数シード → ハッシュで撹拌 →
mulberry32で乱数列を生成」という3段構成に落ち着きました。

なお、対面で遊ぶチンチロというミニゲームだけは、あえてこのシードを使って
いません。こちらは毎回本当にランダムであってほしいので、Math.random()
そのまま使っています。

反省・教訓

一度動作確認しただけでは、このクセには気づけませんでした。複数日の盤面を
並べて見比べる、という一手間をかけて初めてわかったことです。「動いたから
完成」で止めず、意図した性質(この場合は日ごとに十分ばらけているか)を
実際に確認する視点は、今後も持っておきたいと思っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?