File size: 1,627 Bytes
280363b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from __future__ import annotations

from collections.abc import Callable, Iterable
from dataclasses import dataclass

import chess

from zero960.engine.search import EvalFn, select_move


@dataclass(slots=True)
class MatchResult:
    candidate_points: float
    total_games: int
    finished_games: int


def _new_board(chess960_index: int) -> chess.Board:
    board = chess.Board.from_chess960_pos(chess960_index)
    board.chess960 = True
    return board


def play_game(
    chess960_index: int,
    white_eval: EvalFn,
    black_eval: EvalFn,
    depth: int = 2,
    max_plies: int = 120,
) -> float:
    board = _new_board(chess960_index)

    for _ in range(max_plies):
        if board.is_game_over(claim_draw=True):
            break
        eval_fn = white_eval if board.turn == chess.WHITE else black_eval
        move = select_move(board, depth=depth, eval_fn=eval_fn)
        board.push(move)

    result = board.result(claim_draw=True)
    if result == "1-0":
        return 1.0
    if result == "0-1":
        return 0.0
    return 0.5


def play_match_series(
    candidate_eval: EvalFn,
    baseline_eval: EvalFn,
    start_positions: Iterable[int],
    depth: int = 2,
) -> MatchResult:
    points = 0.0
    total_games = 0

    for chess960_index in start_positions:
        total_games += 2
        points += play_game(chess960_index, candidate_eval, baseline_eval, depth=depth)
        points += 1.0 - play_game(chess960_index, baseline_eval, candidate_eval, depth=depth)

    return MatchResult(
        candidate_points=points,
        total_games=total_games,
        finished_games=total_games,
    )