ガチャのサンプルコード

ガチャが世に出てきてかなり経つけど、ソースコードって出回ってないような気がするので、書いてみた。
配列からランダムに1件取得するメソッド(Utility.php)は[ガチャのロジックを比較してみた](http://qiita.com/chiyoyo/items/72a6362f1f2ed4f5511b)を参照。

単純にリストからランダムで引かせるだけじゃなくて「Xグレードの確率 X倍アップ!」とか言いやすいように、最初にグレードを決めてから対象のガチャを引くようにしています。
あとModelは記載していませんので、自前の環境に合わせて補完してください。

# ソース
## ガチャ本体
```php:Gacha.php
require_once 'Utility.php';
class Gacha
{
	/**
	 * ガチャをひく
	 * @param int ガチャID
	 * @param int 引く枚数
	 * @return array (カードID => 枚数)
	 */
	public static function doGacha($gachaId, $number = 1)
	{
		// どのグレードを何回引けばいいか取得
		$gradeList = self::getGachaGrade($gachaId, $number);

		// グレードに対応するガチャを引く
		$result = [];
		foreach ($gradeList as $gradeId => $number) {
			$cardList = self::getCard($gradeId, $number);
			foreach ($cardList as $cardId => $cardNumber) {
				if (isset($result[$cardId])) {
					$result[$cardId] += $cardNumber;
				} else {
					$result[$cardId] = $cardNumber;
				}
			}
		}
		return $result;
	}

	/**
	 * 当選グレードを決める
	 * @param int $gachaId ガチャID
	 * @param int $number 引く回数
	 * @return array [グレードID => 回数]
	 */
	private static function getGachaGrade($gachaId, $number = 1)
	{
		// 指定ガチャIDのグレード一覧を取得
		// 配列で返却される['id' => グレードID, 'rate' => 確率]
		$gradeList = GachaGradeModel::findByGachaId($gachaId);

		// 引くグレードIDと回数を取得
		$result = [];
		for ($i = 0 ; $i < $number ; $i++) {
			// ランダムでグレードを取得
			$grade = Utility::getRandomData($gradeList);
			if (isset($result[$grade['id']])) {
				$result[$grade['id']]++;
			} else {
				$result[$grade['id']] = 1;
			}
		}
		return $result;
	}

	/**
	 * ガチャの中身を引く
	 * @param int $gradeId ガチャグレードID
	 * @param int $number 引く回数
	 * @return array [カードID => 枚数]
	 */
	private static function getCard($gradeId, $number = 1)
	{
		// ガチャの中身一覧を取得
		// 配列で返却される['card_id' => カードID, 'rate' => 確率]
		$contentsList = GachaContentsModel::findByGradeId($gradeId);

		// 引くカードと枚数を取得
		$result = [];
		for ($i = 0 ; $i < $number ; $i++) {
			// 中身からランダムに1枚取得
			$contents = Utility::getRandomData($contentsList);

			if (isset($result[$contents['card_id']])) {
				$result[$contents['card_id']]++;
			} else {
				$result[$contents['card_id']] = 1;
			}
		}
		return $result;
	}
}
```

## 配列からランダムに取得するメソッド
```php:Utility.php
class Utility
{
	/**
	 * 配列の中から確率を使いランダムで1件取得する
	 * @param array データ配列
	 * @param string 確率を表す連想配列名(デフォルトは"rate")
	 */
	public static function getRandomData(array $array, $keyName = 'rate')
	{
		// 確率の合計値を求める
		$max = 0;
		foreach ($array as $record) {
			$max += $record[$keyName];
		}

		// 合計値までのランダム値を求める
		$rate = mt_rand(0, $max-1);

		// ランダムの値がどれにあたるか調べる
		foreach ($array as $record) {
			$max -= $record[$keyName];
			if ($max <= $rate) {
				return $record;
			}
		}
		throw new Exception('ここには来ないはず');
	}
}
```

# DB
```sql
-- ガチャ本体
CREATE TABLE gacha (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255));
-- ガチャに入っているグレード
CREATE TABLE gacha_grade (
  id INT AUTO_INCREMENT PRIMARY KEY,
  gacha_id INT,
  name VARCHAR(255),
  rate INT);
-- グレードに対する中身
CREATE TABLE gacha_contents (
  id INT AUTO_INCREMENT PRIMARY KEY,
  gacha_grade_id INT,
  card_id INT,
  rate INT);
```

# 実行用コード
```php
$result = Gacha::doGacha(1, 10000);　// ガチャID:1を10000回引く
ksort($result);
foreach ($result as $cardId => $number) {
	echo 'CardID:' . $cardId . '/' . $number .PHP_EOL;
}
```