Open Eliguli opened 3 months ago
class Offset: def __init__(self, x, y): self.x = x self.y = y def get_x_offset(self): return self.x def get_y_offset(self): return self.y class Node: def __init__(self, column, row, status=0): self.column = column self.row = row self.status = status self.railway_id = None def __str__(self): return f"column: {self.column} row: {self.row} status: {self.status} rail {self.railway_id}" def is_same_position(self, node): return self.row == node.row and self.column == node.column class GridDrawer: COLORS = { "red": 41, "yellow": 43, "green": 42, "blue": 44 } def __init__(self, game_grid): self.game_grid = game_grid self.highlighted = [] self.highlight_color = [] def highlight(self, column, row, color): self.highlight_node(self.game_grid.find_node(column, row), color) def highlight_node(self, node, color): self.highlighted.append(node) self.highlight_color.append(str(self.COLORS[color])) def highlight_nodes(self, nodes, color): for node in nodes: self.highlight_node(node, color) def clear_all_highlight(self): self.highlighted.clear() self.highlight_color.clear() def clear_highlight(self, node): if node in self.highlighted: index = self.highlighted.index(node) self.highlight_color.pop(index) self.highlighted.remove(node) def print_grid(self): for i in range(self.game_grid.total_column + 1): for j in range(self.game_grid.total_row + 1): print("\033[0m", end="") if self.game_grid.find_node(j, i) in self.highlighted: index = self.highlighted.index(self.game_grid.find_node(j, i)) print(f"\033[{self.highlight_color[index]}m", end="") node_status = self.game_grid.find_node(j, i).status if node_status == 0: print("\033[37m. ", end="") elif node_status == 1: print("\033[31m* ", end="") elif node_status == 2: print("\033[34m* ", end="") print("\033[0m") print("") class Grid: DIRECTIONS8 = [ Offset(-1, -1), Offset(-1, 0), Offset(-1, 1), Offset(0, -1), Offset(0, 1), Offset(1, -1), Offset(1, 0), Offset(1, 1) ] def __init__(self, column_size, row_size): self.column_size = column_size self.row_size = row_size self.total_column = column_size - 1 self.total_row = row_size - 1 self.nodes = [[Node(i, j) for j in range(row_size)] for i in range(column_size)] self.railways = [] for i in range(column_size): self.nodes[i][0].status = 1 self.nodes[i][1].status = 1 self.nodes[i][4].status = 2 self.nodes[i][5].status = 2 def is_legal_position(self, column, row): return 0 <= column <= self.total_column and 0 <= row <= self.total_row def find_node(self, column, row): return self.nodes[column][row] def mirror_node(self, column, row, mode): if mode == 1: return self.find_node(self.total_column - column, row) elif mode == 2: return self.find_node(column, self.total_row - row) elif mode == 3: return self.find_node(self.total_row - row, self.total_column - column) elif mode == 4: return self.find_node(row, column) elif mode == 5: return self.find_node(self.total_row - row, column) else: return self.nodes[column][row] def check_game_status(self): for i in range(self.total_column + 1): for j in range(self.total_row + 1): if self.find_node(i, j).status != 0: return False return True def mirror_node_by_instance(self, node, mode): return self.mirror_node(node.column, node.row, mode) def get_available_path(self, this_node): normal_move = [] capture_move = [] protected_move = [] result = [] visited = {} for this_rail in self.railways: for node in this_rail: if node.is_same_position(this_node): for i in range(len(this_rail)): if this_rail[i].column == this_node.column and this_rail[i].row == this_node.row: j = i j = (j + 1) % len(this_rail) while j not in visited and this_rail[j].status == 0: normal_move.append(this_rail[j]) visited[j] = 114514 j = (j + 1) % len(this_rail) if j in visited: continue elif this_rail[j].status == this_node.status and this_rail[j] != this_node: protected_move.append(this_rail[j]) elif this_rail[j].status != this_node.status and this_rail[j].status != 0: capture_move.append(this_rail[j]) j = i j = (j - 1 + len(this_rail)) % len(this_rail) while j not in visited and this_rail[j].status == 0: normal_move.append(this_rail[j]) visited[j] = 1919810 j = (j - 1 + len(this_rail)) % len(this_rail) if j in visited: continue elif this_rail[j].status == this_node.status and this_rail[j] != this_node: protected_move.append(this_rail[j]) elif this_rail[j].status != this_node.status and this_rail[j].status != 0: capture_move.append(this_rail[j]) break for this_offset in self.DIRECTIONS8: if self.is_legal_position(this_node.column + this_offset.x, this_node.row + this_offset.y): new_node = self.find_node(this_node.column + this_offset.x, this_node.row + this_offset.y) if new_node not in visited: if new_node.status == 0: normal_move.append(new_node) elif new_node.status == this_node.status: protected_move.append(new_node) else: capture_move.append(new_node) result.append(normal_move) result.append(capture_move) result.append(protected_move) return result def generate_surakarta_path(self): for i in range(1, self.total_column // 2 + 1): railway = [] for j in range(self.total_column + 1): node = self.find_node(j, i) node.railway_id = i - 1 railway.append(node) for j in range(3 * self.total_column + 3): mirrored_node = self.mirror_node_by_instance(railway[j], 5) mirrored_node.railway_id = i - 1 railway.append(mirrored_node) self.railways.append(railway) def move_chess(self, from_node, to_node): if from_node.status == 0 or to_node.status == from_node.status: return False else: to_node.status = from_node.status from_node.status = 0 return True class GamePlayer: def __init__(self, game_grid, name, side): self.game_grid = game_grid self.name = name self.side = side def next_move(self): return True class ExamplePlayer(GamePlayer): def __init__(self, game_grid, name, side): super().__init__(game_grid, name, side) self.grid_drawer = GridDrawer(game_grid) self.rand = random.Random() class StatNode(Node): def __init__(self, column, row, node=None): if node: super().__init__(node.column, node.row, node.status) self.railway_id = node.railway_id else: super().__init__(column, row) self.is_protected = False self.is_captured = False def __str__(self): return f"column: {self.column} row: {self.row} status: {self.status} rail {self.railway_id} protected {'yes' if self.is_protected else 'no'} captured {'yes' if self.is_captured else 'no'}" def next_move(self): node_status = [[self.StatNode(i, j, self.game_grid.find_node(i, j)) for j in range(self.game_grid.total_row + 1)] for i in range(self.game_grid.total_column + 1)] moves = [[] for _ in range(5)] for i in range(self.game_grid.total_column + 1): for j in range(self.game_grid.total_row + 1): node = self.game_grid.find_node(i, j) if node.status != self.side and node.status != 0: res = self.game_grid.get_available_path(node) for this_node in res[2] + res[0] + res[1]: node_status[this_node.column][this_node.row].is_protected = True for i in range(self.game_grid.total_column + 1): for j in range(self.game_grid.total_row + 1): node = node_status[i][j] if not node.is_protected and node.status != self.side and node.status != 0: moves[0].append(node) elif node.is_protected and node.status != self.side and node.status != 0: moves[1].append(node) elif not node.is_protected and node.status == 0: moves[2].append(node) elif node.is_protected and node.status == 0: moves[3].append(node) for move in moves: for this_node in move: if this_node.status != self.side: new_node = Node(this_node.column, this_node.row) new_node.status = -1 new_node.railway_id = this_node.railway_id possible_start = self.game_grid.get_available_path(new_node) random.shuffle(possible_start[1]) possible_nodes = [start_node for start_node in possible_start[1] if start_node.status == self.side] if possible_nodes: start_node = possible_nodes[int(len(possible_nodes) * self.rand.random())] return self.game_grid.move_chess(self.game_grid.find_node(start_node.column, start_node.row), self.game_grid.find_node(this_node.column, this_node.row)) return False class ExamplePlayer2(GamePlayer): def __init__(self, game_grid, name, side): super().__init__(game_grid, name, side) self.grid_drawer = GridDrawer(game_grid) self.rand = random.Random() def next_move(self): my_nodes = [self.game_grid.find_node(i, j) for i in range(self.game_grid.total_column + 1) for j in range(self.game_grid.total_row + 1) if self.game_grid.find_node(i, j).status == self.side] random.shuffle(my_nodes) for node in my_nodes: all_possible_end = self.game_grid.get_available_path(node) possible_ends = all_possible_end[1] random.shuffle(possible_ends) if possible_ends: return self.game_grid.move_chess(node, possible_ends[0]) for node in my_nodes: all_possible_end = self.game_grid.get_available_path(node) possible_ends = all_possible_end[0] random.shuffle(possible_ends) if possible_ends: return self.game_grid.move_chess(node, possible_ends[0]) return False if __name__ == "__main__": import random p1_win_count = 0 for _ in range(5000): game_grid = Grid(6, 6) game_grid.generate_surakarta_path() player1 = ExamplePlayer(game_grid, "p1", 1) player2 = ExamplePlayer2(game_grid, "p2", 2) for _ in range(100): if not player1.next_move(): break if game_grid.check_game_status(): p1_win_count += 1 break if not player2.next_move(): p1_win_count += 1 break if game_grid.check_game_status(): break print(p1_win_count / 5000) if __name__ == "__main__": import random # Initialize the game grid and players game_grid = Grid(6, 6) game_grid.generate_surakarta_path() player1 = ExamplePlayer(game_grid, "p1", 1) player2 = ExamplePlayer2(game_grid, "p2", 2) grid_drawer = GridDrawer(game_grid) # Simulate the game for _ in range(100): grid_drawer.print_grid() print() if not player1.next_move(): print("Player 2 wins!") break if game_grid.check_game_status(): print("Player 1 wins!") break grid_drawer.print_grid() print() if not player2.next_move(): print("Player 1 wins!") break if game_grid.check_game_status(): print("Player 2 wins!") break # Print final grid state print() grid_drawer.print_grid()