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>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user