108 lines
4.4 KiB
Python
108 lines
4.4 KiB
Python
from game_logic.goboard import Move
|
|
|
|
|
|
def is_ladder_capture(game_state, candidate, recursion_depth=50):
|
|
return is_ladder(True, game_state, candidate, None, recursion_depth)
|
|
|
|
|
|
def is_ladder_escape(game_state, candidate, recursion_depth=50):
|
|
return is_ladder(False, game_state, candidate, None, recursion_depth)
|
|
|
|
|
|
def is_ladder(try_capture, game_state, candidate,
|
|
ladder_stones=None, recursion_depth=50):
|
|
"""Ladders are played out in reversed roles, one player tries to capture,
|
|
the other to escape. We determine the ladder status by recursively calling
|
|
is_ladder in opposite roles, providing suitable capture or escape candidates.
|
|
|
|
Arguments:
|
|
try_capture: boolean flag to indicate if you want to capture or escape the ladder
|
|
game_state: current game state, instance of GameState
|
|
candidate: a move that potentially leads to escaping the ladder or capturing it, instance of Move
|
|
ladder_stones: the stones to escape or capture, list of Point. Will be inferred if not provided.
|
|
recursion_depth: when to stop recursively calling this function, integer valued.
|
|
|
|
Returns True if game state is a ladder and try_capture is true (the ladder captures)
|
|
or if game state is not a ladder and try_capture is false (you can successfully escape)
|
|
and False otherwise.
|
|
"""
|
|
|
|
if not game_state.is_valid_move(Move(candidate)) or not recursion_depth:
|
|
return False
|
|
|
|
next_player = game_state.next_player
|
|
capture_player = next_player if try_capture else next_player.other
|
|
escape_player = capture_player.other
|
|
|
|
if ladder_stones is None:
|
|
ladder_stones = guess_ladder_stones(game_state, candidate, escape_player)
|
|
|
|
for ladder_stone in ladder_stones:
|
|
current_state = game_state.apply_move(candidate)
|
|
|
|
if try_capture:
|
|
candidates = determine_escape_candidates(
|
|
game_state, ladder_stone, capture_player)
|
|
attempted_escapes = [ # now try to escape
|
|
is_ladder(False, current_state, escape_candidate,
|
|
ladder_stone, recursion_depth - 1)
|
|
for escape_candidate in candidates]
|
|
|
|
if not any(attempted_escapes):
|
|
return True # if at least one escape fails, we capture
|
|
else:
|
|
if count_liberties(current_state, ladder_stone) >= 3:
|
|
return True # successful escape
|
|
if count_liberties(current_state, ladder_stone) == 1:
|
|
continue # failed escape, others might still do
|
|
candidates = liberties(current_state, ladder_stone)
|
|
attempted_captures = [ # now try to capture
|
|
is_ladder(True, current_state, capture_candidate,
|
|
ladder_stone, recursion_depth - 1)
|
|
for capture_candidate in candidates]
|
|
if any(attempted_captures):
|
|
continue # failed escape, try others
|
|
return True # candidate can't be caught in a ladder, escape.
|
|
return False # no captures / no escapes
|
|
|
|
|
|
def is_candidate(game_state, move, player):
|
|
return game_state.next_player == player and \
|
|
count_liberties(game_state, move) == 2
|
|
|
|
|
|
def guess_ladder_stones(game_state, move, escape_player):
|
|
adjacent_strings = [game_state.board.get_go_string(nb) for nb in move.neighbors() if game_state.board.get_go_string(nb)]
|
|
if adjacent_strings:
|
|
string = adjacent_strings[0]
|
|
neighbors = []
|
|
for string in adjacent_strings:
|
|
stones = string.stones
|
|
for stone in stones:
|
|
neighbors.append(stone)
|
|
return [Move(nb) for nb in neighbors if is_candidate(game_state, Move(nb), escape_player)]
|
|
else:
|
|
return []
|
|
|
|
|
|
def determine_escape_candidates(game_state, move, capture_player):
|
|
escape_candidates = move.neighbors()
|
|
for other_ladder_stone in game_state.board.get_go_string(move).stones:
|
|
for neighbor in other_ladder_stone.neighbors():
|
|
right_color = game_state.color(neighbor) == capture_player
|
|
one_liberty = count_liberties(game_state, neighbor) == 1
|
|
if right_color and one_liberty:
|
|
escape_candidates.append(liberties(game_state, neighbor))
|
|
return escape_candidates
|
|
|
|
|
|
def count_liberties(game_state, move):
|
|
if game_state.board.get_go_string(move):
|
|
return game_state.board.get_go_string(move).num_liberties
|
|
else:
|
|
return 0
|
|
|
|
|
|
def liberties(game_state, move):
|
|
return list(game_state.board.get_go_string(move).liberties)
|