127 lines
3.3 KiB
Python
127 lines
3.3 KiB
Python
from sys import stderr
|
|
import time
|
|
import math
|
|
from typing import Any, Callable
|
|
import Goban
|
|
|
|
|
|
def _next_color(color):
|
|
return Goban.Board._BLACK if color == Goban.Board._WHITE else Goban.Board._WHITE
|
|
|
|
# Returns heuristic, move
|
|
def _alphabeta(
|
|
board: Goban.Board,
|
|
heuristic: Callable[[Goban.Board, Any], float],
|
|
color,
|
|
move,
|
|
alpha=-math.inf,
|
|
beta=math.inf,
|
|
depth: int = 3,
|
|
shouldStop = lambda: False
|
|
) -> tuple[float, Any]:
|
|
|
|
wantMax = (board.next_player == color)
|
|
if depth == 0 or board.is_game_over():
|
|
return heuristic(board, color), move
|
|
|
|
if wantMax:
|
|
acc = -math.inf, None
|
|
for move in board.generate_legal_moves():
|
|
if Goban.Board.flat_to_name(move) == "PASS":
|
|
continue
|
|
|
|
board.push(move)
|
|
value = (
|
|
_alphabeta(
|
|
board,
|
|
alpha=alpha,
|
|
beta=beta,
|
|
move=move,
|
|
heuristic=heuristic,
|
|
color=_next_color(color),
|
|
depth=depth - 1,
|
|
)[0],
|
|
move,
|
|
)
|
|
acc = max(
|
|
acc,
|
|
value,
|
|
key=lambda t: t[0],
|
|
)
|
|
board.pop()
|
|
|
|
if shouldStop() or acc[0] >= beta:
|
|
break # beta cutoff
|
|
alpha = max(alpha, acc[0])
|
|
|
|
else:
|
|
acc = math.inf, None
|
|
for move in board.generate_legal_moves():
|
|
|
|
board.push(move)
|
|
value = (
|
|
_alphabeta(
|
|
board,
|
|
alpha=alpha,
|
|
beta=beta,
|
|
move=move,
|
|
heuristic=heuristic,
|
|
color=_next_color(color),
|
|
depth=depth - 1,
|
|
)[0],
|
|
move,
|
|
)
|
|
acc = min(
|
|
acc,
|
|
value,
|
|
key=lambda t: t[0],
|
|
)
|
|
board.pop()
|
|
|
|
if shouldStop() or acc[0] <= alpha:
|
|
break # alpha cutoff
|
|
beta = min(beta, acc[0])
|
|
|
|
return acc
|
|
|
|
|
|
def alphabeta(
|
|
board: Goban.Board,
|
|
heuristic: Callable[[Goban.Board, Any], float],
|
|
color,
|
|
depth: int = 3,
|
|
):
|
|
_, move = _alphabeta(board, move=-1, heuristic=heuristic, color=color, depth=depth)
|
|
return move
|
|
|
|
|
|
def IDDFS(board: Goban.Board, heuristic, color, duration: float, maxdepth=42):
|
|
st = time.time()
|
|
shouldStop = (lambda: time.time() - st > duration)
|
|
depth = 0
|
|
move = -1
|
|
score = -1
|
|
|
|
while not shouldStop() and depth <= maxdepth:
|
|
if depth % 2 == 0:
|
|
score, move = _alphabeta(
|
|
board, heuristic, color, move=move, alpha=-math.inf, beta=math.inf, depth=depth, shouldStop=shouldStop
|
|
)
|
|
|
|
if score == math.inf:
|
|
return move, score
|
|
|
|
else:
|
|
score, move = _alphabeta(
|
|
board, heuristic, color, move=move, alpha=-math.inf, beta=math.inf, depth=depth, shouldStop=shouldStop
|
|
)
|
|
|
|
if score == -math.inf:
|
|
return move, score
|
|
|
|
print("depth:", depth, time.time() - st, score, file=stderr)
|
|
depth += 1
|
|
|
|
print(time.time() - st, duration, depth, file=stderr)
|
|
return move, score
|