90 lines
2.8 KiB
Python
Executable File
90 lines
2.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
from puzzledb import PuzzleDb
|
|
from puzzlerunner import run_puzzle
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
from random import Random
|
|
|
|
import glicko2
|
|
|
|
|
|
def select_puzzles(player, puzzle_db, num_puzzles, played_puzzles, random):
|
|
rd = player.rd
|
|
candidates = []
|
|
|
|
while len(candidates) < num_puzzles:
|
|
candidates = puzzle_db \
|
|
.filter(lambda p: p.rating >= player.rating - 2 * rd) \
|
|
.filter(lambda p: p.rating <= player.rating + 2 * rd) \
|
|
.filter(lambda p: p not in played_puzzles) \
|
|
.collect()
|
|
|
|
rd += 50
|
|
|
|
return sorted(random.sample(candidates, num_puzzles),
|
|
key=lambda p: p.rating)
|
|
|
|
|
|
def play_round(player, puzzle_db, num_puzzles, engine,
|
|
timeout, played_puzzles, random):
|
|
puzzles = select_puzzles(player, puzzle_db, num_puzzles,
|
|
played_puzzles, random)
|
|
played_puzzles.update(puzzles)
|
|
outcomes = []
|
|
|
|
for puzzle in puzzles:
|
|
print(f'Running puzzle {puzzle.puzzle_id} '
|
|
f'with rating {puzzle.rating}... ',
|
|
end='', flush=True)
|
|
|
|
result = run_puzzle(puzzle, engine, timeout)
|
|
success = result.is_success()
|
|
outcomes.append(success)
|
|
|
|
if success:
|
|
print('OK')
|
|
else:
|
|
print('FAIL')
|
|
|
|
ratings = [p.rating for p in puzzles]
|
|
rds = [p.rd for p in puzzles]
|
|
player.update_player(ratings, rds, outcomes)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--engine', type=Path, required=True)
|
|
parser.add_argument('--timeout', type=float, default=60)
|
|
parser.add_argument('--min-plays', type=int, default=500)
|
|
parser.add_argument('--min-popularity', type=int, default=50)
|
|
parser.add_argument('--rounds', type=int, default=20)
|
|
parser.add_argument('--puzzles-per-round', type=int, default=5)
|
|
parser.add_argument('--random-seed', type=int, default=0)
|
|
parser.add_argument('--puzzle-db', type=Path, required=True)
|
|
args = parser.parse_args()
|
|
|
|
with args.puzzle_db.open(newline='') as f:
|
|
puzzle_db = PuzzleDb.from_csv(f) \
|
|
.filter(lambda p: p.num_plays >= args.min_plays) \
|
|
.filter(lambda p: p.popularity >= args.min_popularity) \
|
|
.sorted(key=lambda p: p.rating)
|
|
|
|
player = glicko2.Player()
|
|
played_puzzles = set()
|
|
random = Random(args.random_seed)
|
|
|
|
for current_round in range(1, args.rounds + 1):
|
|
print(f'=== Round {current_round}/{args.rounds}, '
|
|
f'current rating: {round(player.rating)} '
|
|
f'(rd: {round(player.rd)}) ===')
|
|
play_round(player, puzzle_db, args.puzzles_per_round,
|
|
args.engine, args.timeout, played_puzzles, random)
|
|
|
|
print(f'Final rating: {round(player.rating)} (rd: {round(player.rd)})')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|