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)