from enum import Enum from collections import deque def get_neighbors(p, q): directions = [(0, -1), (1, -1), (1, 0), (0, 1), (-1, 1), (-1, 0)] return [(p + dp, q + dq) for dp, dq in directions] class Piece: def get_valid_jumps() -> list: raise NotImplementedError class Bee(Piece): def __init__(self, board, p, q, team): self.board = board self.p = p self.q = q self.team = team # 'team' should be 'upper' or 'lower' based on the piece case. def get_valid_jumps(self) -> list: # The bee can move to an adjacent empty space. valid_moves = [] for neighbor in get_neighbors(self.p, self.q): # Check if the neighbor is within the bounds of the board and is empty if neighbor in self.board and self.board[neighbor] == "": valid_moves.append(neighbor) return valid_moves class Beetle(Piece): def __init__(self, p, q, team): self.p = p self.q = q self.team = team def get_valid_jumps(self, board): # The beetle can move to an adjacent space whether it is empty or not. valid_moves = get_neighbors(self.p, self.q) return valid_moves class Spider(Piece): def __init__(self, p, q, team): self.p = p self.q = q self.team = team def get_valid_jumps(self, board): # Use BFS to find all tiles exactly three moves away start = (self.p, self.q) visited = set() # Keep track of visited tiles queue = deque([(start, 0)]) # Queue of (position, distance) valid_moves = [] while queue: current_position, current_distance = queue.popleft() if current_distance == 3: # We have found a tile exactly three moves away valid_moves.append(current_position) continue # We don't want to move further from this tile if current_position in visited: continue # Already visited this tile visited.add(current_position) # Get all neighboring positions for neighbor in get_neighbors(*current_position): if ( neighbor in board and board[neighbor] == "" and neighbor not in visited ): queue.append((neighbor, current_distance + 1)) return valid_moves class Grasshopper(Piece): def __init__(self, p, q, team): self.p = p self.q = q self.team = team def get_valid_jumps(self, board): # Generator function to yield valid moves def generate_moves(): # Check each direction for possible jumps for dp, dq in get_neighbors(0, 0): # Use (0, 0) to get direction vectors pos = (self.p + dp, self.q + dq) # Continue in the direction until we find a piece to jump over while pos in board and board[pos] != "": pos = (pos[0] + dp, pos[1] + dq) # If we jumped over at least one piece and landed on an empty space, yield the move if ( pos in board and board[pos] == "" and (pos[0] != self.p or pos[1] != self.q) ): yield pos return list(generate_moves()) class Ant(Piece): def __init__(self, p, q, team): self.p = p self.q = q self.team = team def get_valid_jumps(self, board): visited = set() # Keep track of visited nodes to prevent cycles # Recursive function to explore all valid moves def explore(p, q): for dp, dq in get_neighbors(0, 0): # Get direction vectors new_p, new_q = p + dp, q + dq next_pos = (new_p, new_q) # Continue in this direction while there are pieces to slide around while ( next_pos in board and board[next_pos] != "" and next_pos not in visited ): visited.add(next_pos) next_pos = (next_pos[0] + dp, next_pos[1] + dq) # If we found an empty space, add it as a valid move and explore from there if next_pos in board and board[next_pos] == "": visited.add(next_pos) yield next_pos # Explore further moves from this new position yield from explore(next_pos[0], next_pos[1]) # Start exploring from the Ant's current position return list(explore(self.p, self.q))