Weighted Pick (重み付き抽選)
ゲームでよく使われる「コモンは当たりやすく、レアは当たりにくい」といった確率抽選を実装するためのロジックです。
各アイテムに weight プロパティを持たせ、その値の大きさに比例して選ばれる確率が決まります。
ボタンを押すと抽選が行われます。設定確率は Common(70), Rare(25), SR(5) です。 試行回数を重ねると、設定された確率に収束していく様子が確認できます。
コードスニペット
Section titled “コードスニペット”1. メソッド (Method)
Section titled “1. メソッド (Method)”シーンクラスに追加するヘルパーメソッドです。ジェネリクスを使用しているため、weight プロパティを持つ任意のオブジェクト配列に対応できます。
/** * 重み付きランダム抽選を行う * @param items 重み(weight)を持つアイテムの配列 * @returns 抽選されたアイテム */public weightedPick<T extends { weight: number }>(items: T[]): T | null { // 重みの合計を計算 const totalWeight = items.reduce((sum, item) => sum + item.weight, 0);
// 0から合計重みまでのランダムな値を取得 let random = Math.random() * totalWeight;
// 重みを順番に減算して、0以下になった時点のアイテムを選択 for (const item of items) { if (random < item.weight) { return item; } random -= item.weight; }
return null;}2. 使い方 (Usage)
Section titled “2. 使い方 (Usage)”アイテム配列を定義してメソッドに渡すだけで、抽選結果取得できます。
// 抽選対象の定義 (weightが確率の重みになります)const dropItems = [ { name: 'Potion', weight: 100 }, { name: 'Elixir', weight: 20 }, { name: 'Rare Sword', weight: 5 }];
// 抽選の実行const result = this.weightedPick(dropItems);
if (result) { console.log(`You got: ${result.name}`);}完全なソースコード
Section titled “完全なソースコード”クラス全体を見る
import Phaser from 'phaser';
export class WeightedPickScene extends Phaser.Scene { private resultText?: Phaser.GameObjects.Text; private statsText?: Phaser.GameObjects.Text; private counts: Record<string, number> = {}; // 各アイテムの定義 private items = [ { name: 'Common', weight: 70, color: '#aaaaaa' }, { name: 'Rare', weight: 25, color: '#0088ff' }, { name: 'SR', weight: 5, color: '#ffaa00' } ];
constructor() { super({ key: 'WeightedPickScene' }); this.items.forEach(item => this.counts[item.name] = 0); }
create() { this.add.text(10, 10, 'Weighted Pick Demo', { fontFamily: '"Noto Sans JP", sans-serif', fontSize: '24px', color: '#00ff00' });
// ... UI Setup ...
// 抽選実行の例 (ボタンクリック時など) this.handleDraw(); }
private handleDraw() { const picked = this.weightedPick(this.items); if (picked) { this.counts[picked.name]++; console.log(`Pooled: ${picked.name}`); // ... UI Update ... } }
/** * 重み付きランダム抽選を行う * @param items 重み(weight)を持つアイテムの配列 * @returns 抽選されたアイテム */ public weightedPick<T extends { weight: number }>(items: T[]): T | null { const totalWeight = items.reduce((sum, item) => sum + item.weight, 0); let random = Math.random() * totalWeight;
for (const item of items) { if (random < item.weight) { return item; } random -= item.weight; } return null; }}実装のポイント
Section titled “実装のポイント”-
汎用的な設計:
weight: numberを持つオブジェクトであれば何でも受け取れるようにジェネリクス (<T extends { weight: number }>) を使用しています。これにより、アイテムデータ、敵の行動パターン、ドロップテーブルなど様々な用途に再利用できます。 -
重みの計算: 最初に
totalWeightを計算し、0からtotalWeightまでのランダムな数値を生成します。これにより、各要素のweightの比率がそのまま確率になります(合計が100である必要はありません)。 -
アルゴリズム: ランダム値から各要素の
weightを順番に引いていき、0以下(または現在のweight未満)になった時点でその要素を選択します。これを「ルーレット選択」や「線形探索」と呼びます。