開発日記④ ロンで必要な点数を計算する

前回までの進捗

前回は、自分の席や目標順位、本場、供託リーチ棒を入力できるようにした。
これで、

  • 現在の持ち点
  • 自分の席
  • 目標順位
  • 本場
  • 供託

という、条件計算に必要そうな情報がひと通りそろった。
今回は、いよいよこのアプリの中心になる処理に着手する。
まずはロンの場合に、誰から何点アガれば目標順位を達成できるのかを計算してみる。

単純な点差だけでは決められない

最初は、現在の点差を見れば必要な点数を出せると思っていた。
たとえば、自分が23,000点で相手が26,000点なら、差は3,000点。
それなら3,000点以上アガれば逆転できそうに見える。
ただし、相手からロンした場合は、

  • 自分の点数が増える
  • 放銃した相手の点数が減る

という二つの変化が同時に起こる。
2,000点をロンした場合でも、自分と相手の差は合計4,000点縮まる。
さらに、放銃者が誰なのかによって、ほかの人との順位関係も変わる。
本場や供託があれば、実際に増減する点数も変わる。
単純に「上の人との点差」を計算するだけでは足りなさそうだった。

実際に点数を動かして確かめる

そこで今回は、必要な点数を数式だけで求めるのではなく、候補となる点数を一つずつ試す方法にした。
処理の流れは、だいたいこんな感じ。

  1. 放銃者を一人選ぶ
  2. 候補となるロン点を低い順に試す
  3. ロン後の点数を計算する
  4. ロン後の順位を計算する
  5. 目標順位を達成できたか確認する
  6. 最初に達成できた点数を必要点数とする

少し力技にも見えるけれど、候補となる点数の数はそれほど多くない。
複雑な条件を無理に一つの式へまとめるより、実際の点数移動を再現して確認する方が分かりやすいと判断した。

和了点の候補を用意する

麻雀のロン点は、100点ずつ自由に増えるわけではない。
そこで、実際に使われる代表的な点数を配列として用意した。

const childRonPoints = [
  1000,
  1300,
  1600,
  2000,
  2300,
  2600,
  2900,
  3200,
  3900,
  5200,
  7700,
  8000,
  12000,
];

実際の配列にはもう少し細かく候補を入れている。
自分が親の場合は点数が変わるため、親用と子用で別の候補を持たせることにした。
現在の試作では南4局を想定し、北家を親として固定している。
そのため、自分の席が北家かどうかで、使用する点数候補を切り替える。

放銃者ごとに条件を探す

ロンの場合は、誰からアガるかが重要になる。
たとえば、トップを目指しているときにトップから直撃できれば、少ない点数で逆転できる可能性がある。
一方、4着の相手からロンしても、トップとの差は自分の加点分しか縮まらない。
そこで、自分以外の3人を放銃者として、それぞれ必要な点数を探す。
イメージとしては、次のような処理になる。

for (const loser of seats) {
  if (loser === selfSeat) {
    continue; // 自分の場合はスキップ
  }

  for (const points of ronPointCandidates) {
    // ロン時の点数を試算
    const nextScores = applyRon(
      scores,
      selfSeat,
      loser,
      points,
      honba,
      kyotaku,
    );

    // 順位を算出
    const ranking = calculateRanking(nextScores);

    if (isGoalAchieved(ranking, selfSeat, goal)) {
      // この放銃者からは、この点数で条件を達成
      break;
    }
  }
}

コードとしては少し長くなったけれど、やっていること自体は単純。
低い点数から順番に試し、目標を達成できた時点で探索を止める。
これで、放銃者ごとの最低条件を出せる。

本場と供託も反映する

ロンの場合、本場があると放銃者の支払いが増える。

実際の支払い = ロン点 + 本場 × 300点

供託リーチ棒がある場合は、アガった人に1本あたり1,000点が加算される。
本場と供託が多ければ、同じ基本点でも目標順位に届く可能性がある。
前回追加した入力項目が、ここでようやく計算に使われるようになった。
入力欄を作った時点では画面に数字が増えただけだったけれど、計算結果に影響するようになると、少しアプリらしく感じる。

ちょうどの点数が存在しない場合

たとえば計算上は3,500点あれば逆転できるとしても、ロン点として3,500点という候補はない。
その場合は、3,500点以上で最初に見つかる実際の和了点を表示する必要がある。
候補を低い順に試す方式なら、ここも自然に処理できる。
3,500という数字をそのまま表示するのではなく、実際に条件を満たせる3,900点などを結果として返す。
今回この方式にした一番大きな理由は、こういった麻雀特有の点数の飛び方を扱いやすかったから。

今回できたこと

今回の進捗は以下のとおり。

  • 親と子でロン点候補を分けた
  • ロン後の点数を計算できるようにした
  • 放銃者ごとに必要な点数を探索した
  • ロン後の順位から目標達成を判定した
  • 本場と供託を必要点数の計算に反映した
  • 実際に存在する和了点から最低条件を表示した

点数入力や順位表示だけだった画面に、「誰から何点」という情報を出せるようになった。
ようやく、作りたかったオーラス条件確認アプリらしい機能が一つできたと思う。
ただ、ロン条件だけではまだ不十分。
麻雀ではツモによる逆転もあるし、親と子で支払い方も変わる。
次はツモの場合について、ほかの3人からの支払いを反映しながら必要点数を探していく。

前回までと合わせて画面はこんな感じ。

放銃者ごとに目標順位達成に必要なロン点を表示する画面
放銃者ごとに目標順位達成に必要なロン点を表示した画面

まだまだゲームどころかアプリにすらなってないけど、
次にツモの場合を作って、その次くらいに対局結果(誰が何点でアガったか)入力と
リザルト表示になる想定。
そこまでいけば、点数シミュレーションアプリくらいの体裁にはなるだろうか。

牛一。

コメント

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