【マイレース開発記録(13)】finish_bonus調整地獄                               

マイレースのレースロジックが一通り動き始めたころ、
私はある違和感に気づきました。

レースの順位が、ほとんどスピードだけで決まってしまうのです。

マイレースでは、各レーンの速さを次のようなロジックで計算しています。

def speed(natal_lon, transit_lon):
return 1.0 + (1 - angle_diff(natal_lon, transit_lon) / 180) * 0.4

出生図(natal)当日の天体(transit)の角度差から、その日の「外側の流れ」をスピードとして算出する仕組みです。

ただ、この計算だけだと、問題がありました。

順位がほぼ固定されてしまうのです。

スピードが速いレーンは常に上位、遅いレーンは常に下位になりやすい。

もちろん占星術的にはそれも意味がありますが、
「レースとして見て楽しいか」という観点では少し物足りないと感じました。

マイレースでは、うさぎの動きに

  • 走る速さ(外側の流れ)
  • ジャンプ(内側のリズム)

という2つの要素があります。

ジャンプは次のようにフェーズから計算しています。

def calc_phases(birthday: date, target: date):
days = (target - birthday).days
return {
"moon": calc_phase(days, 29.5),
"mars": calc_phase(days, 687),
}

  • 月のリズム
  • 火星のリズム

この2つを使って、ジャンプの高さを変えています。

しかし当初は、このジャンプは完全に見た目だけの演出でした。

レース結果には影響していません。

そこで考えたのが、

ジャンプのタイミングを、ゴールタイムに少しだけ影響させる

という仕組みでした。

そこで追加したのが、次の関数です。

def speed(natal_lon, transit_lon):
return 1.0 + (1 - angle_diff(natal_lon, transit_lon) / 180) * 0.4

出生図(natal)と当日の天体(transit)の角度差から、その日の「外側の流れ」をスピードとして算出する仕組みです。

ただ、この計算だけだと、問題がありました。

順位がほぼ固定されてしまうのです。

スピードが速いレーンは常に上位、遅いレーンは常に下位になりやすい。

もちろん占星術的にはそれも意味がありますが、「レースとして見て楽しいか」という観点では少し物足りないと感じました。

マイレースでは、うさぎの動きに

  • 走る速さ(外側の流れ)
  • ジャンプ(内側のリズム)

という2つの要素があります。

ジャンプは次のようにフェーズから計算しています。

def calc_phases(birthday: date, target: date):
days = (target - birthday).days
return {
"moon": calc_phase(days, 29.5),
"mars": calc_phase(days, 687),
}

  • 月のリズム
  • 火星のリズム

この2つを使って、ジャンプの高さを変えています。

しかし当初は、このジャンプは完全に見た目だけの演出でした。

レース結果には影響していません。

そこで考えたのが、

ジャンプのタイミングを、ゴールタイムに少しだけ影響させる

という仕組みでした。

そこで追加したのが、次の関数です。

def finish_bonus(phase: float) -> float:
rhythm = (phase - 0.5) * 2 # -1.0〜+1.0
return - rhythm * 0.15 # 最大±0.15秒

このロジックでは、

  • フェーズが高い(リズムが良い)
  • フェーズが低い(リズムが落ち着いている)

といった状態によって、

ゴールタイムに最大 ±0.15秒 の補正をかけます。

そして実際のゴールタイムは、

base_time = 1.0 / spd
finish_times[key] = base_time + finish_bonus(phase)

という形で計算しています。

つまり、

  • スピードがベースの順位
  • リズムが微調整

という構造です。

ここで問題が起きました。

補正値が大きすぎると、

  • スピードが遅いレーンが突然1位になる
  • レース結果が毎回バラバラになる

逆に小さすぎると、

  • ほとんど順位が変わらない
  • ジャンプの意味がなくなる

この調整が、予想以上に難しかったのです。

0.3
0.2
0.1
0.05

何度も数値を変えながら、レースを繰り返し眺めることになりました。

このあたりの調整は、AIとの対話がかなり役立ちました。

私はよく、

  • 今のロジックはどういう振る舞いになるのか
  • 数式としてどういう意味になるのか
  • バランスはどこにあるのか

といったことを、言葉で説明しながら整理します。

AIにコードを書いてもらうというより、自分の考えを説明するための相手として使うことが多いです。

特に私の場合、頭の中だけで考えていると整理が止まってしまうことがあります。

言葉にして説明することで、

「あ、ここがズレているのか」

と気づくことがよくあります。

最終的に、

return - rhythm * 0.15

という値に落ち着きました。

このくらいだと、

  • 基本の順位はスピードが決める
  • でもジャンプ次第で入れ替わることもある

という、
「レースとしてちょうど気持ちいい揺らぎ」になります。

もうひとつ意識したのは、

完全なランダムにはしない

ということです。

マイレースでは、

  • 生年月日
  • 眺めたい日
  • カテゴリ

をもとに、結果が決まります。

つまり同じ条件なら、同じレース結果になるという設計です。

これは占いとしての一貫性を保つためでもあり、アプリとしての再現性を守るためでもあります。

finish_bonus は、コードとしてはとても小さな関数です。

でもこの補正を入れたことで、

  • レースが少しドラマチックになり
  • ジャンプが意味を持ち
  • 見ていて楽しい動きになりました

ロジックとしてはほんの数行ですが、体験を変える数行になったと思っています。

次回は、

【マイレース開発記録(14)】デバッグで救われた瞬間

として、開発中に「これは助かった」と思ったデバッグの仕組みについて書いていきます。

\ 最新情報をチェック /

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です