Drop variant B, adopt Black-belt Exam defender, add one-sidedness metrics
- Remove variant B (Brown cap, +1 for higher beating lower) from rules, engine, bots and simulator; a higher belt beating a lower one gains 0. - Final Exam: the defender fights as a Black belt (RULES.md v0.7). Ties to the defender and best-of-3 stay as simulator switches (`--exam`). - Report stripe income per source as gains and losses. - Add ten one-sidedness measures sampled at turn ends (lead gap, winner margin and lead share, halfway leader, last lead change, contention, rank swaps, leader Kumite losses, Exam challengers, winner pace). - Plan: decisions D28-D33 and the detailed Phase 6. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+36
-20
@@ -5,34 +5,43 @@ import dataclasses
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
from dojo.events import Source
|
||||
from dojo.metrics import GameMetrics, Spread, Summary
|
||||
|
||||
|
||||
def format_summary(summary: Summary) -> str:
|
||||
income = summary.income_per_turn
|
||||
seats = ", ".join(f"{seat} {rate:.0%}" for seat, rate in enumerate(summary.win_rate_by_seat))
|
||||
bots = ", ".join(f"{bot} {rate:.0%}" for bot, rate in summary.win_rate_by_bot.items())
|
||||
income = ", ".join(
|
||||
f"{source} +{gain:.2f}/{loss:.2f}"
|
||||
for source, (gain, loss) in summary.income_per_turn.items()
|
||||
)
|
||||
lines = [
|
||||
f"== {summary.scenario} ({summary.games} games, {summary.finished:.0%} finished)",
|
||||
f"Rounds {_spread(summary.rounds)}",
|
||||
f"Minutes {_spread(summary.minutes)}",
|
||||
f"Kumite {summary.kumite_per_round:.2f}/round; upsets {summary.upset_rate:.0%}, "
|
||||
f"higher beats lower {summary.expected_rate:.0%}",
|
||||
f"Longest chain {_spread(summary.max_chain)} (Kumite in one turn, per game)",
|
||||
f"Exam {summary.exam_attempts_per_game:.2f} attempts/game, "
|
||||
f"{summary.exam_success:.0%} passed",
|
||||
f"Stuck on 14 {summary.turns_on_14_per_game:.2f} turns/game, "
|
||||
f"longest streak {summary.longest_14_streak} turns",
|
||||
f"Income/turn square {income[Source.SQUARE]:+.2f} "
|
||||
f"(home {summary.square_income_home_per_turn:+.2f}, "
|
||||
f"away {summary.square_income_away_per_turn:+.2f}), "
|
||||
f"card {income[Source.CARD]:+.2f}, kumite {income[Source.KUMITE]:+.2f}, "
|
||||
f"exam {income[Source.EXAM]:+.2f}",
|
||||
f"Lead changes {_spread(summary.lead_changes)}",
|
||||
f"Final spread {_spread(summary.final_spread)}",
|
||||
f"Wins by seat {seats}",
|
||||
f"Wins by bot {bots}",
|
||||
f"Rounds {_spread(summary.rounds)}",
|
||||
f"Minutes {_spread(summary.minutes)}",
|
||||
f"Kumite {summary.kumite_per_round:.2f}/round; upsets {summary.upset_rate:.0%}, "
|
||||
f"higher beats lower (no stripes change) {summary.expected_rate:.0%}",
|
||||
f"Longest chain {_spread(summary.max_chain)} (Kumite in one turn, per game)",
|
||||
f"Exam {summary.exam_attempts_per_game:.2f} attempts/game, "
|
||||
f"{summary.exam_success:.0%} passed, {summary.exam_challengers:.2f} challengers/game",
|
||||
f"Income/turn {income}",
|
||||
f"Square income home {summary.square_income_home_per_turn:+.2f}/turn, "
|
||||
f"away {summary.square_income_away_per_turn:+.2f}/turn (net)",
|
||||
f"Winner pace {_spread(summary.winner_turns_to_brown)} (own turns to reach Brown)",
|
||||
f"Lead changes {_spread(summary.lead_changes)}",
|
||||
f"Last change {_shares(summary.last_lead_change)} (point of the game)",
|
||||
f"Gap 1st-2nd {_spread(summary.lead_gap)} (per-game mean, stripes)",
|
||||
f"Winner margin {_spread(summary.winner_margin)} (over best rival at the end)",
|
||||
f"Winner led {summary.winner_lead_share:.0%} of turns; halfway leader won "
|
||||
f"{summary.halfway_leader_wins:.0%} of {summary.halfway_games} games",
|
||||
f"Contention {summary.contenders_at_end:.2f} rivals within one belt at the end; "
|
||||
f"{summary.out_of_contention:.0%} of player-turns out (>2 belts behind)",
|
||||
f"Worst player {_shares(summary.worst_out_of_contention)} (share of turns out)",
|
||||
f"Rank swaps {_spread(summary.rank_swaps)}",
|
||||
f"Leader beaten {summary.leader_kumite_losses:.2f} Kumite lost by the leader per game",
|
||||
f"Final spread {_spread(summary.final_spread)}",
|
||||
f"Wins by seat {seats}",
|
||||
f"Wins by bot {bots}",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -51,3 +60,10 @@ def _spread(spread: Spread) -> str:
|
||||
f"mean {spread.mean:.1f} p10 {spread.p10:.1f} median {spread.median:.1f} "
|
||||
f"p90 {spread.p90:.1f}"
|
||||
)
|
||||
|
||||
|
||||
def _shares(spread: Spread) -> str:
|
||||
return (
|
||||
f"mean {spread.mean:.0%} p10 {spread.p10:.0%} median {spread.median:.0%} "
|
||||
f"p90 {spread.p90:.0%}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user