ピック対象を1点~複数指定してすべて全てまで連続でガシャを引き続ける機能を作成しました。
出来たもの
デモサイトのリンクは以下になります。
デモサイト:ミリシタ ガシャシミュレータ(ピック指定)
マルチステップなフォームを作る
最初に作った画面はなんていうか見た目がよくなかったのでマルチステップな画面を作ってみました。
こちらを参考に作成しました。
参考:Bootstrap 4 Multi step form wizard Example
①ガシャ選択(これまでに開催されてきたガシャを選択)
②ピック対象選択(ピック指定のカードを1~n枚選択)
③ピック方法選択(単発 or 10連を選択)
④結果(ガシャ結果を確認する)
という流れとなってます。
PC版とSP版の表示差異が少なくなったので動作確認がちょっと楽になったかも?
ピック指定カードがそろうまでガシャを引き続ける処理について
whileの無限ループを使いました。
<?php namespace App\Controller\Api; use App\Utils\GashaUtils; use Cake\Event\Event; use Cake\Core\Exception\Exception; /** * ガシャ結果を返すコントローラクラス * * @property \App\Model\Table\GashasTable $Gashas * @property \App\Model\Table\CardsTable $Cards * */ class TargetPickGashaController extends ApiController { /** * 1~9連目の重み付けデータ * @var unknown */ private $cards_rate_data; /** * 10連目の重み付けデータ * @var unknown */ private $ssrsr_cards_rate_data; /** * ピックしたいカードのID配列 * @var unknown */ private $target_card_ids; /** * 共通処理内でガシャに必要なデータを取得 * {@inheritDoc} * @see \Cake\Controller\Controller::beforeFilter() */ public function beforeFilter(Event $event) { parent::beforeFilter($event); // リクエストタイムアウトの時間を5分に設定 set_time_limit(300); try { $request_data = $this->request->getData(); // ガシャID $gasha_id = @$request_data['gasha_id']; // ターゲットカードID $this->target_card_ids = @$request_data['target_card_ids']; if (is_null($gasha_id) || !is_numeric($gasha_id)) { throw new Exception('gasha_id is invalid.'); } else if (is_null($this->target_card_ids) || empty($this->target_card_ids)) { throw new Exception('target_card_ids is invalid.'); } // ガシャ情報取得 $gasha = $this->Gashas->get($gasha_id); // カード情報取得 $cards = $this->Cards->findGashaTargetCards($gasha); // 提供割合を取得 $provision_ratios = GashaUtils::getProvisionRatio($gasha, $cards); // 1~9連目の重み付けデータ作成 $this->cards_rate_data = GashaUtils::createWeightData(array_merge($provision_ratios['SSR'], $provision_ratios['SR'], $provision_ratios['R'])); // 10連目(SR以上確定)の重み付けデータ作成 if ($this->request->action === 'jyuren') { unset($cards['R']); $gasha->sr_rate = 100 - $gasha->ssr_rate; $provision_ratios = GashaUtils::getProvisionRatio($gasha, $cards); $this->ssrsr_cards_rate_data = GashaUtils::createWeightData(array_merge($provision_ratios['SSR'], $provision_ratios['SR'])); } } catch (\Exception $e) { $error_message = $e->getMessage(); $this->log($error_message); return $this->response->withStatus(500, $error_message)->withStringBody(null); } } /** * ピックしたいカードがそろうまで10連ガシャを引き続ける */ public function jyuren() { $card_ids = []; while (true) { // ガシャを9回引く $pick_ids = []; for ($i = 0; $i < 9; $i++) { $pick_ids[] = $this->_pick($this->cards_rate_data); } // SR以上確定ガシャ $pick_ids[] = $this->_pick($this->ssrsr_cards_rate_data); // 10枚のカードIDをマージ $card_ids = array_merge($card_ids, $pick_ids); // ピックしたいカードのID配列が空になったらガシャから解放する $this->target_card_ids = array_diff($this->target_card_ids, $pick_ids); if (empty($this->target_card_ids)) { break; } // ガシャ上限に到達したら終了 else if (count($card_ids) >= TARGET_PICK_ALLOW_MAX_NUM) { break; } } // レスポンスにセットするカード情報を取得 $results = $this->Cards->findByIds($card_ids); return $this->response->withStringBody(json_encode($results, JSON_UNESCAPED_UNICODE)); } /** * ピックしたいカードがそろうまで単発ガシャを引き続ける */ public function tanpatsu() { $card_ids = []; while (true) { // ガシャを1回引く $pick_id = $this->_pick($this->cards_rate_data); $card_ids[] = $pick_id; // ピックしたいカードのID配列が空になったらガシャから解放する $this->target_card_ids = array_diff($this->target_card_ids, [$pick_id]); if (empty($this->target_card_ids)) { break; } // ガシャ上限に到達したら終了 else if (count($card_ids) >= TARGET_PICK_ALLOW_MAX_NUM) { break; } } // レスポンスにセットするカード情報を取得 $results = $this->Cards->findByIds($card_ids); return $this->response->withStringBody(json_encode($results, JSON_UNESCAPED_UNICODE)); } /** * 重み付けの抽選処理 * * ガシャを1回引く * @param array $entries * @return integer $card_id */ private function _pick($entries) { $sum = array_sum($entries); $rand = rand(1, $sum); foreach($entries as $card_id => $rate){ if (($sum -= $rate) < $rand) { return $card_id; } } } }
1~9連目、10連目共にガシャ情報は前処理で取得しておいて無限ループ内で使いまわすようにしています。
whileは無限ループとなってますが外部に公開するものなので一応上限を定数で設定しています。現在は10000連を超えたら処理を抜けるようにしています。
フロント画面でピック指定したカードがそろうまでの判定処理はarray_diffを引き算のように使えばよさそう。
ガチャ上限を外して動作確認する
せっかくなんでローカルでの動作確認の際はガシャ上限をコメントアウトして動作確認してみました。
$card_ids = []; while (true) { // ガシャを9回引く $pick_ids = []; for ($i = 0; $i < 9; $i++) { $pick_ids[] = $this->_pick($this->cards_rate_data); } // SR以上確定ガシャ $pick_ids[] = $this->_pick($this->ssrsr_cards_rate_data); // 10枚のカードIDをマージ $card_ids = array_merge($card_ids, $pick_ids); // ピックしたいカードのID配列が空になったらガシャから解放する $this->target_card_ids = array_diff($this->target_card_ids, $pick_ids); if (empty($this->target_card_ids)) { break; } // ガシャ上限に到達したら終了 - else if (count($card_ids) >= TARGET_PICK_ALLOW_MAX_NUM) { - break; - } +// else if (count($card_ids) >= TARGET_PICK_ALLOW_MAX_NUM) { +// break; +// } }
- ガシャ対象は2020年1月31日より開催されてる「【期間限定】想いを込めて♡バレンタインガシャ」にしてみる
- ピック対象カードを上記ガシャで排出中のSSR全95種を選択
- ピック方法は単発と10連それぞれ
のような設定で引いてみました。
まず単発の結果は以下の通りでした。
ピック回数 | 19036回 |
SSR枚数 | 558 |
SSR率 | 2.93% |
SR枚数 | 2302 |
SR率 | 12.09% |
R枚数 | 16176 |
R率 | 84.98% |
次に10連の結果は以下のようになりました。
ピック回数 | 19890 |
SSR枚数 | 584 |
SSR率 | 2.94% |
SR枚数 | 4121 |
SR率 | 20.72% |
R枚数 | 15185 |
R率 | 76.34% |
比べてみるとSR率がだいぶ違うことがわかる。
10連の方は10連目のSR以上確定があるおかげでSR率が高くなっています。
ピック回数はたまたまですがどちらも2万弱となりました。何度かやるとたまに15000くらいとか出たりする!(^^)!!
SSR率は単発も10連も2万回近く引いてるので設定上の3%に結構近い感じに収束してます。
動画を取ってみた
Chromeの開発者ツールでSP版の画面表示してScreenToGifでGif動画撮ってみました。
ちなみにこの回は調子良くて単発でSSR95種揃えるのに掛かったピック回数が14433回でした。
ピックアップSSRがたくさん引けてることも確認できました。
課金額を計算してみる
上の方で上げた10連でSSR全95種をそろえるのにかかった19890回を参考に計算してみました。
ジュエルはいくつかの販売個数がありますが8400個(9800円)が一番1個辺りの単価が低いのでこちらで計算。
まず、何回10連ガシャを引いたかは単純に10で割る
19890 ÷ 10 = 1,989回
次にどれだけのジュエルが必要だったかは1回の10連ガシャが2500個なので掛ける
1989 × 2500 = 4,972,500ジュエル
何回8400個の石を購入すればいいかは単純に8400で割る
4972500 ÷ 8400 = 591.96 ≒ 592
最後に9800円をかける
592 × 9800 = 5,801,600円
多分これで合ってると思うけど数字が苦手なので違ったら教えてください。
580万あればそろうかもしれない(^q^
ちなみに一番結果がよかった14433回の場合は
14433 × 250 ÷ 8400 = 429.55 ≒ 430
430 × 9800 = 4,214,000円
となりました( ・`ー・´)