Fix the bots' corner bias; settle seat order and RULES v0.9 (6.2, 6.8)
Bots now pick their start square and break path ties in their own corner's frame; the absolute tie-break made corners 1 and 2 win 25-28%. With that fixed, seat order is within target at 4 players, and a +1 stripe first-player bonus (new switch) gains only ~1 point, so no compensation. RULES.md v0.9 lists the tested alternatives; the plan has the 6.8 consolidation run and the seat-order study. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+26
-4
@@ -3,9 +3,9 @@
|
||||
import random
|
||||
|
||||
import pytest
|
||||
from scripted import make_game, put
|
||||
from scripted import fixture_board, make_game, put
|
||||
|
||||
from dojo.board import Board, Pos
|
||||
from dojo.board import Board, Pos, rotate
|
||||
from dojo.bots import (
|
||||
AGGRESSIVE,
|
||||
BALANCED,
|
||||
@@ -15,7 +15,9 @@ from dojo.bots import (
|
||||
HeuristicAgent,
|
||||
Profile,
|
||||
RandomAgent,
|
||||
own_frame,
|
||||
series_probability,
|
||||
start_square,
|
||||
win_probability,
|
||||
)
|
||||
from dojo.engine import Agent, FightView, Game, RandomSource
|
||||
@@ -73,9 +75,29 @@ def test_heuristic_sees_no_opponent_to_fight_in_round_one() -> None:
|
||||
put(game, 1, TATAMI, track=3)
|
||||
|
||||
# In round 2 the aggressive bot picks the fight (test_profiles_differ_on_an_even_fight).
|
||||
choice = HeuristicAgent(AGGRESSIVE).choose_path(game.view(0), [(Pos(5, 3),), (TATAMI,)])
|
||||
# Two plain Tatami tie; the tie goes to the lower square in the bot's frame, (4, 2).
|
||||
choice = HeuristicAgent(AGGRESSIVE).choose_path(game.view(0), [(TATAMI,), (Pos(4, 2),)])
|
||||
|
||||
assert choice == (Pos(5, 3),)
|
||||
assert choice == (Pos(4, 2),)
|
||||
|
||||
|
||||
def test_bots_start_from_the_same_square_of_every_safe_room() -> None:
|
||||
board = fixture_board()
|
||||
|
||||
assert len({own_frame(start_square(board, corner), corner) for corner in range(4)}) == 1
|
||||
|
||||
|
||||
def test_tied_paths_are_chosen_alike_from_every_corner() -> None:
|
||||
game, _, _ = make_game(players=4)
|
||||
agent = HeuristicAgent(BALANCED)
|
||||
chosen = set()
|
||||
for seat, player in enumerate(game.players):
|
||||
player.position = rotate(Pos(4, 3), player.corner)
|
||||
tatami = [(rotate(square, player.corner),) for square in (Pos(5, 3), Pos(3, 3), Pos(4, 2))]
|
||||
choice = agent.choose_path(game.view(seat), tatami)
|
||||
chosen.add(own_frame(choice[0], player.corner))
|
||||
|
||||
assert chosen == {Pos(3, 3)}
|
||||
|
||||
|
||||
def test_brown_heuristic_takes_the_exam_when_it_can() -> None:
|
||||
|
||||
@@ -246,6 +246,17 @@ def test_no_kumite_in_round_one_an_occupied_tunnel_exit_cannot_be_used() -> None
|
||||
assert game.players[0].position == Pos(1, 6)
|
||||
|
||||
|
||||
def test_first_player_bonus_raises_only_the_first_seat() -> None:
|
||||
game, _, _ = make_game(players=3, config=RulesConfig(start_position=3, first_player_bonus=1))
|
||||
|
||||
assert [p.track for p in game.players] == [4, 3, 3]
|
||||
|
||||
|
||||
def test_first_player_bonus_cannot_be_negative() -> None:
|
||||
with pytest.raises(ValueError, match="first_player_bonus"):
|
||||
RulesConfig(first_player_bonus=-1)
|
||||
|
||||
|
||||
def test_first_kumite_round_must_be_positive() -> None:
|
||||
with pytest.raises(ValueError, match="first_kumite_round"):
|
||||
RulesConfig(first_kumite_round=0)
|
||||
|
||||
+11
-1
@@ -17,7 +17,7 @@ from dojo.events import (
|
||||
TurnStarted,
|
||||
)
|
||||
from dojo.metrics import KUMITE_SECONDS, TURN_SECONDS, GameMetrics, measure, summarize
|
||||
from dojo.rules import DEFAULT_RULES, Card
|
||||
from dojo.rules import DEFAULT_RULES, Card, RulesConfig
|
||||
|
||||
HOME_0 = Pos(0, 1) # quadrant 0
|
||||
|
||||
@@ -106,6 +106,16 @@ def test_kiai_decides_a_fight_only_when_the_result_flips_without_it(
|
||||
assert summary.exam_kiai_decided_rate == decided
|
||||
|
||||
|
||||
def test_first_player_bonus_is_counted_from_the_start() -> None:
|
||||
config = RulesConfig(first_player_bonus=1)
|
||||
|
||||
metrics = measure(
|
||||
[GameOver(None, 1)], config, (0, 1), ("balanced", "farmer"), scenario="t", seed=0
|
||||
)
|
||||
|
||||
assert metrics.final_track == (1, 0)
|
||||
|
||||
|
||||
def test_standings_are_sampled_at_turn_ends() -> None:
|
||||
metrics = run(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user