111 lines
2.7 KiB
Python
111 lines
2.7 KiB
Python
from sys import stderr
|
|
import time
|
|
import math
|
|
from typing import Any, Callable
|
|
import Goban
|
|
|
|
|
|
def _alphabeta(
|
|
board: Goban.Board,
|
|
heuristic: Callable[[Goban.Board, Any], float],
|
|
color,
|
|
alpha=-math.inf,
|
|
beta=math.inf,
|
|
depth: int = 3,
|
|
shouldStop=lambda: False,
|
|
) -> tuple[float, Any]:
|
|
if board.is_game_over() or depth == 0:
|
|
return heuristic(board, color), None
|
|
|
|
wantMax = (board.next_player() == color)
|
|
best_move = -1
|
|
|
|
if wantMax:
|
|
acc = -math.inf
|
|
for move in board.generate_legal_moves():
|
|
if Goban.Board.flat_to_name(move) == "PASS":
|
|
continue
|
|
|
|
board.push(move)
|
|
value = _alphabeta(
|
|
board,
|
|
heuristic=heuristic,
|
|
color=color,
|
|
alpha=alpha,
|
|
beta=beta,
|
|
depth=depth - 1,
|
|
shouldStop=shouldStop,
|
|
)[0]
|
|
board.pop()
|
|
|
|
if value > acc:
|
|
acc = value
|
|
best_move = move
|
|
|
|
alpha = max(alpha, acc)
|
|
if shouldStop() or acc >= beta:
|
|
break # beta cutoff
|
|
|
|
else:
|
|
acc = math.inf
|
|
for move in board.generate_legal_moves():
|
|
if Goban.Board.flat_to_name(move) == "PASS":
|
|
continue
|
|
|
|
board.push(move)
|
|
value = _alphabeta(
|
|
board,
|
|
heuristic=heuristic,
|
|
color=color,
|
|
alpha=alpha,
|
|
beta=beta,
|
|
depth=depth - 1,
|
|
shouldStop=shouldStop,
|
|
)[0]
|
|
board.pop()
|
|
|
|
if value < acc:
|
|
acc = value
|
|
best_move = move
|
|
|
|
beta = min(beta, acc)
|
|
if shouldStop() or acc <= alpha:
|
|
break # alpha cutoff
|
|
|
|
return acc, best_move
|
|
|
|
|
|
def alphabeta(
|
|
board: Goban.Board,
|
|
heuristic: Callable[[Goban.Board, Any], float],
|
|
color,
|
|
depth: int = 3,
|
|
):
|
|
_, move = _alphabeta(board, heuristic=heuristic, color=color, depth=depth)
|
|
return move
|
|
|
|
|
|
def IDDFS(
|
|
board: Goban.Board,
|
|
heuristic: Callable[[Goban.Board, Any], float],
|
|
color,
|
|
max_depth: int = 10,
|
|
duration: float = 5.0, # Duration in seconds
|
|
):
|
|
best_move = -1
|
|
start_time = time.time()
|
|
shouldStop = lambda: (time.time() - start_time) >= duration
|
|
|
|
for depth in range(1, max_depth + 1):
|
|
value, move = _alphabeta(
|
|
board, heuristic=heuristic, color=color, depth=depth, shouldStop=shouldStop
|
|
)
|
|
|
|
if shouldStop():
|
|
break
|
|
|
|
print(f"{depth}, {value}", file=stderr)
|
|
best_move = move
|
|
|
|
return best_move
|