SOLVERS_C4 = [
("random", lambda b, ctr, rng: (0, random_move(b, rng))),
("minimax_d4", lambda b, ctr, rng: minimax(b, 4, _current_player(b), ctr)),
("alphabeta_d4", lambda b, ctr, rng: alphabeta(b, 4, _current_player(b), ctr)),
("mcts_2000", lambda b, ctr, rng: mcts(b, 2000, ctr, rng)),
]
TIMEOUT_MOVE = 5.0 # secondes par coup, meme budget que le terrain Sudoku
def opening_position(seed, plies=6):
"""Position d ouverture reproductible : plies coups aleatoires seedes."""
rng = random.Random(seed)
b = ConnectFour()
for _ in range(plies):
b.play(rng.choice(b.legal_moves()), _current_player(b))
return b
def copy_board(b):
w = ConnectFour()
w.grid = [c[:] for c in b.grid]
w.heights = list(b.heights)
w.moves = b.moves
return w
def play_game(solver_x, solver_o, seed):
"""Une partie complete ; retourne (vainqueur_mark, stats_X, stats_O)."""
stats = {0: {"nodes": 0, "time": 0.0, "moves": 0}, 1: {"nodes": 0, "time": 0.0, "moves": 0}}
b = opening_position(seed)
rng = random.Random(seed + 1)
turn = 0
max_dt = 0.0
while True:
idx = turn % 2
player = "X" if idx == 0 else "O"
fn = (solver_x if idx == 0 else solver_o)[1]
ctr = NodeCounter()
t0 = time.perf_counter()
_, col = fn(b, ctr, rng)
dt = time.perf_counter() - t0
max_dt = max(max_dt, dt)
stats[idx]["nodes"] += ctr.nodes
stats[idx]["time"] += dt
stats[idx]["moves"] += 1
row = b.play(col, player)
if b.is_win_at(col, row, player):
return player, stats[0], stats[1], max_dt
if b.is_full():
return None, stats[0], stats[1], max_dt
turn += 1
roundrobin_rows = []
game_seed = 100
worst_move = 0.0
for i in range(len(SOLVERS_C4)):
for j in range(i + 1, len(SOLVERS_C4)):
for color_x in (True, False):
sa, sb = SOLVERS_C4[i], SOLVERS_C4[j]
sx = sa if color_x else sb
so = sb if color_x else sa
winner, stx, sto, mx = play_game(sx, so, seed=game_seed)
game_seed += 1
worst_move = max(worst_move, mx)
# une ligne PAR JOUEUR : chacun sa couleur, son resultat, son cout
if color_x:
persp = ((sa[0], sb[0], "X", stx), (sb[0], sa[0], "O", sto))
else:
persp = ((sa[0], sb[0], "O", sto), (sb[0], sa[0], "X", stx))
for name, opp, mark, st in persp:
roundrobin_rows.append({
"solver": name, "opponent": opp, "couleur": mark,
"outcome": 1.0 if winner == mark else (0.5 if winner is None else 0.0),
"nodes": st["nodes"], "time_s": st["time"], "moves": st["moves"],
})
df_c4 = pd.DataFrame(roundrobin_rows)
print(f"{len(roundrobin_rows) // 2} parties (round-robin double, {len(roundrobin_rows)} lignes = une par joueur, ouvertures 6 plies, graines 100..{game_seed - 1})")
print(f"Temps de coup le plus eleve observe : {worst_move:.2f}s (budget {TIMEOUT_MOVE}s)")
df_c4