ガチャが世に出てきてかなり経つけど、ソースコードって出回ってないような気がするので、書いてみた。
配列からランダムに1件取得するメソッド(Utility.php)はガチャのロジックを比較してみたを参照。
単純にリストからランダムで引かせるだけじゃなくて「Xグレードの確率 X倍アップ!」とか言いやすいように、最初にグレードを決めてから対象のガチャを引くようにしています。
あとModelは記載していませんので、自前の環境に合わせて補完してください。
ソース
ガチャ本体
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;
}
}
配列からランダムに取得するメソッド
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
-- ガチャ本体
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);
実行用コード
$result = Gacha::doGacha(1, 10000); // ガチャID:1を10000回引く
ksort($result);
foreach ($result as $cardId => $number) {
echo 'CardID:' . $cardId . '/' . $number .PHP_EOL;
}