Files
bridzik/rl/evaluate.py
T
timandClaude Fable 5 e1733f4943 RL: baseline boti a evaluacny harness
RandomPlayer, HeuristicPlayer (MC tipper nad rozdaniami neznamych kariet
+ tipom riadena hracia heuristika) a McPlayer (MC ohodnotenie kazdeho
kandidatskeho tahu nad rozdaniami konzistentnymi s dedukovanymi voidmi).
Evaluacia s rotaciou sedadiel: py -m rl.evaluate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:55 +02:00

90 lines
3.2 KiB
Python

"""Evaluacny harness: odohra N kol medzi 4 hracmi a spocita metriky.
Metriky per hrac (viz rl/DESIGN.md, sekcia 5): priemerne body na kolo
a presnost tipu (% kol s presne trafenym tipom). Sedadla sa medzi kolami
rotuju, aby ziadny hrac nebol systematicky zvyhodneny poradim tipovania.
Spustenie ako skript porovna baseline botov:
py -m rl.evaluate --rounds 500 --seed 7
"""
import argparse
from random import Random
from bridzik import ROUNDS_PER_SERIES
from rl.env import PHASE_GUESS, RoundEnv
def play_round(players: list, env: RoundEnv, round_number: int = None,
first_player: int = None) -> list:
"""Odohra jedno kolo; `players[seat]` rozhoduje za sedadlo `seat`.
Vrati body 4 sedadiel (`Round.get_points_summary()`).
"""
decision = env.reset(round_number, first_player)
while True:
seat = decision.player
if decision.phase == PHASE_GUESS:
action = players[seat].guess(env.round, seat)
else:
action = players[seat].play(env.round, seat)
decision, rewards, done = env.step(action)
if done:
return rewards
def evaluate(players: list, n_rounds: int, rng: Random = None,
round_numbers: list = None) -> list:
"""Odohra `n_rounds` kol s rotaciou sedadiel; vrati stats per hrac.
Vystup: zoznam dictov v poradi `players` --
{'avg_points': float, 'hit_rate': float, 'rounds': int}.
"""
rng = rng if rng is not None else Random()
env = RoundEnv(rng)
points = [0] * 4
hits = [0] * 4
for i in range(n_rounds):
round_number = rng.choice(round_numbers) if round_numbers \
else rng.randrange(ROUNDS_PER_SERIES)
# rotacia: sedadlo s obsadzuje players[(s + i) % 4]
seating = [players[(s + i) % 4] for s in range(4)]
rewards = play_round(seating, env, round_number)
for seat in range(4):
player_index = (seat + i) % 4
points[player_index] += rewards[seat]
hits[player_index] += rewards[seat] > 0
return [{'avg_points': points[p] / n_rounds,
'hit_rate': hits[p] / n_rounds,
'rounds': n_rounds} for p in range(len(players))]
def main():
from rl.players import HeuristicPlayer, RandomPlayer
parser = argparse.ArgumentParser(description='Evaluacia baseline botov')
parser.add_argument('--rounds', type=int, default=500)
parser.add_argument('--seed', type=int, default=7)
parser.add_argument('--mc-samples', type=int, default=100)
args = parser.parse_args()
rng = Random(args.seed)
lineups = [
('4x random', [RandomPlayer(rng) for _ in range(4)]),
('1x heuristika + 3x random',
[HeuristicPlayer(rng, n_samples=args.mc_samples)]
+ [RandomPlayer(rng) for _ in range(3)]),
('4x heuristika',
[HeuristicPlayer(rng, n_samples=args.mc_samples) for _ in range(4)]),
]
for label, players in lineups:
stats = evaluate(players, args.rounds, rng)
print(f'\n{label} ({args.rounds} kol):')
for i, s in enumerate(stats):
print(f' hrac {i}: {s["avg_points"]:6.2f} bodov/kolo, '
f'tip trafeny {100 * s["hit_rate"]:5.1f} %')
if __name__ == '__main__':
main()