スポンサーリンク

ミリシタのガシャ(ガチャ)シミュレータを作った

アイドルマスター ミリオンライブ!シアターデイズ(以降ミリシタ)の10連ガシャのシミュレータを作成しました。

このトップ絵好き。

ミリシタのガシャについて

  • レアリティごとのピックレートはSSR3%、SR12%、R85%
    • 3か月に1回くらい開催されるフェスではSSR6%、SR12%、R82%
  • 10連目はSR以上確定
    • つまりSSR3%、SR97%、R0%(フェス中SSR6%、SR94%、R0%)
  • ガシャ開催に合わせて追加されたキャラはピックアップキャラとなり期間中ピック率が上がる
    • 多分SSR0.99%、SR2.4%、R8.5%だと思われる
    • ↑はそれぞれキャラが1人のときの確率
    • SSRが2枚追加されたときは2人で分けて1人辺りは0.495%になるっぽい
  • フェス限定、限定、恒常、復刻限定がある
    • 恒常:いつでも引けるやつ
    • 復刻限定:1年くらい前のガシャの期間限定キャラが期間限定で復活するやつ

ガシャの仕様は大体どのソシャゲも一緒かと思います。
2020年1月現在開催されているガシャの情報は以下の通り



  • SSRピックアップ 0.99%
  • SSR 0.021%
  • SRピックアップ 1.2%
  • SR 0.085%
  • R 0.85%

でした。

シミュレータで出来ることとか

  • 過去のガシャを指定して10連ガチャを引ける
    • ステップアップガシャとタイプ別ガシャは不定期でWikiにも乗ってなかったので除外
    • タイプ別はそのうちなんらかの形で作るかもしれない
  • 過去のガシャを指定してガシャのピック確率や提供割合を表示できる
    • 過去のピックアップ確率やピックアップキャラが定かではない
      • 100%これで正解か微妙、過去のガシャについて書いてあるWikiとかブログを見た感じ概ね合っているような気はする
    • 一応最新のガシャ情報(寒空と微熱ガシャ)の提供割合と比較すると一致してる模様
  • 本家と同様に10連目はSR以上確定
  • 引いたSSR,SR,Rの確率と枚数を集計して表示

など

管理画面について

シミュレータで使用するデータの管理画面はCakeプラグイン化した自動生成ツールで作成しました。
そのためほぼノータッチです( ・`ー・´)⁺
どちらかというとCSVインポートで使用するデータをExcelで作ってた時間の方が長いかも。

フロント画面について

こちらはさすがに自動生成とはいかないので頑張って自作。
最初にRESTfulなAPIの形で提供割合や10連の処理を作り始めたのでそのままフロントはAPIをAjaxで呼び出すだけの画面となりました。

簡単なようでAjaxリクエストはデバッグキットで表示できないのでSQLの細かいところを直すときにちょっとめんどくさかった。

最終的にカード情報を取得する処理は以下のようなプログラムとなりました。

/**
 * カード情報を返す
 * @param Gasha $gasha
 */
public function findGashaTargetCards(Gasha $gasha) {

  $start_date = $gasha->start_date->i18nFormat('yyyy-MM-dd');

  $query = $this->find();
  $rarity_cases = $query->newExpr()->addCase(
      [
          $query->newExpr()->add(['rarity' => '02']),
          $query->newExpr()->add(['rarity' => '03']),
          $query->newExpr()->add(['rarity' => '04'])
      ],
      ['R', 'SR', 'SSR'],
      ['string', 'string', 'string']
  );
  $type_cases = $query->newExpr()->addCase(
      [
          $query->newExpr()->add(['type' => '01']),
          $query->newExpr()->add(['type' => '02']),
          $query->newExpr()->add(['type' => '03'])
      ],
      ['Princess', 'Fairy', 'Angel'],
      ['string', 'string', 'string']
  );
  $query->select(['id', 'character_id', 'name', 'rarity' => $rarity_cases, 'type' => $type_cases])
  ->enableHydration(false);

  // 恒常カード情報を取得
  $cards = $query->where([
      'gasha_include' => 1,
      'limited' => 0,
      'add_date <=' => $start_date
  ])->toArray();

  // 限定カードを取得
  if ($gasha->isLimited()) {

    $limited_cards = $query->where([
        'gasha_include' => 1,
        'limited' => 1,
        'add_date' => $start_date
    ], [], true)->toArray();
    $cards = array_merge($limited_cards, $cards);

  }
  // フェス限定カードを取得
  else if ($gasha->isFesLimited()) {

    $fes_limited_cards = $query->where([
        'gasha_include' => 1,
        'limited' => 2,
        'add_date <=' => $start_date, // 過去のフェス限を含める
    ], [], true)->toArray();
    $cards = array_merge($fes_limited_cards, $cards);

  }
  // 復刻?の条件追加
  else if ($gasha->isReprintLimited()) {

    $card_reprints = TableRegistry::getTableLocator()->get('CardReprints');
    $sub_query = $card_reprints->find()->select(['card_id'])->where(['gasha_id' => $gasha->id]);

    $reprint_limited_cards = $query->where([
        'Cards.id IN' => $sub_query,
        'Cards.gasha_include' => 1,
        'Cards.limited' => 1,
    ], [], true)->toArray();
    $cards = array_merge($reprint_limited_cards, $cards);
  }

  // TODO 以下の処理は上のカード情報を取得するORMにまとめられそう
  // ピックアップ情報を取得して付加
  $card_ids = Hash::extract($cards, '{n}.id');
  $gasha_pickups = TableRegistry::getTableLocator()->get('GashaPickups');
  $pickup_targets = $gasha_pickups->find()->select(['card_id'])->where([
      'card_id IN' => $card_ids,
      'gasha_id' => $gasha->id,
  ])
  ->enableHydration(false)->toArray();
  $pickup_targets = Hash::extract($pickup_targets, '{n}.card_id');
  foreach ($cards as $card_index => $card) {
    $cards[$card_index]['pickup'] = (in_array($card['id'], $pickup_targets));
  }

  // レアリティごとに持ち替え
  $cards = Hash::combine($cards, '{n}.id', '{n}', '{n}.rarity');

  return $cards;
}

SQLはなんとかCakePHP3のORMを使用した形で作れました。
いつもだと少し複雑なSQLを組み立てる際はConnectionManagerのコネクション取得して生のSQLを生成してぶん投げるみたいなことをしています(^q^
最初、恒常のカードと限定のカードを一度のSQLで取得するべくOR条件を作成していたのですが可読性が悪い気がしたので、現在の別々に取得する形となりました。

動作確認する

まず「10連ガチャを引く」を何度かクリックし、現在開催中のガシャのSSRピックアップが出ることを確認してみました。
概ね300連以内に出ました。


提供割合はこんな感じ。

上の方で載せているミリシタの提供割合画面と一致しました。


次に10連×300回引いてみました。

SSRのピック率が3.07%と期待していた結果となりました((^^))
SRは10連目でSR以上確定の影響を受けて設定値の12%より8%高い20.53%となりました。

何度かリセットしてやり直してみましたが概ね似たような結果となりましたd(^^三^^)b

参考サイト

カード情報や過去のガシャ情報についてはWikiを参考にさせていただきました。
参考:アイマス ミリシタ攻略まとめwiki【アイドルマスター ミリオンライブ! シアターデイズ】 – Gamerch

また、重み付けの抽選処理についてはこちらの処理を参考にさせていただきました。
自力では思いつけなかった。。;;
参考:重み付きのランダム抽選関数を作る(PHP) | tyablog.net

自分用メモ

↓この後やること

  • 特定のカードをチェックしておいて、そのカードを引けるまでに何回ガチャを引いたかとかわかるようにする
  • フロント画面をもう少しどうにかする
    • 出来れば実際のミリシタの画面に似せたりする
    • SSRとかSR引いたら早坂さんのスライドを出すとか
  • 提供割合のソート表示をPrincess→Fairy→Angel順にする
  • 公開する
    • 新しくmilligashaみたいなサブドメイン切って公開する予定
    • Gitのプライベートリポジトリをパブリックにする

その他

ゲームによってガチャだったりガシャだったりするのが気になって調べてみたらまとめられている方がいらっしゃいました。
参考:ガチャとガシャの違い:にわかがお伝えします。 – ブロマガ
参考:「ガチャポン」と「ガシャポン」の違いとは? | これってどう違うの?

ガチャとガシャの指し示す意味は同じ模様。
ミリシタはバンダイナムコのゲームだから「ガシャ」ということの模様。

タイトルとURLをコピーしました