class HamiltonSolver(BaseSolver): def __init__(self, snake, shortcuts=True): if snake.map.num_rows % 2 != 0 or snake.map.num_cols % 2 != 0: raise ValueError("num_rows and num_cols must be even.") super().__init__(snake) self.__shortcuts = shortcuts self.__path_solver = PathSolver(snake) self.__table = [[_TableCell() for _ in range(snake.map.num_cols)] for _ in range(snake.map.num_rows)] self.__build_cycle() @property def table(self): return self.__table def next_direc(self): head = self.snake.head() nxt_direc = self.__table[head.x][head.y].direc # Take shorcuts when the snake is not too long if self.__shortcuts and self.snake.len() < 0.5 * self.map.capacity: path = self.__path_solver.shortest_path_to_food() if path: tail, nxt, food = self.snake.tail(), head.adj( path[0]), self.map.food tail_idx = self.__table[tail.x][tail.y].idx head_idx = self.__table[head.x][head.y].idx nxt_idx = self.__table[nxt.x][nxt.y].idx food_idx = self.__table[food.x][food.y].idx # Exclude one exception if not (len(path) == 1 and abs(food_idx - tail_idx) == 1): head_idx_rel = self.__relative_dist( tail_idx, head_idx, self.map.capacity) nxt_idx_rel = self.__relative_dist(tail_idx, nxt_idx, self.map.capacity) food_idx_rel = self.__relative_dist( tail_idx, food_idx, self.map.capacity) if nxt_idx_rel > head_idx_rel and nxt_idx_rel <= food_idx_rel: nxt_direc = path[0] return nxt_direc def __build_cycle(self): """Build a hamiltonian cycle on the map.""" path = self.__path_solver.longest_path_to_tail() cur, cnt = self.snake.head(), 0 for direc in path: self.__table[cur.x][cur.y].idx = cnt self.__table[cur.x][cur.y].direc = direc cur = cur.adj(direc) cnt += 1 tail = self.snake.tail() self.__table[tail.x][tail.y].idx = cnt self.__table[tail.x][tail.y].direc = self.snake.direc def __relative_dist(self, ori, x, size): if ori > x: x += size return x - ori
def __init__(self, snake): super().__init__(snake) self._path_solver = PathSolver(snake) self.open_ = [Node(self.snake.head(), 0, 0, 0, self.snake.head())] self.closed_ = [] self.closed_dict = {} self.flag_new = True self.path = []
def __init__(self, snake, shortcuts=True): if snake.map.num_rows % 2 != 0 or snake.map.num_cols % 2 != 0: raise ValueError("num_rows and num_cols must be even.") super().__init__(snake) self.__shortcuts = shortcuts self.__path_solver = PathSolver(snake) self.__table = [[_TableCell() for _ in range(snake.map.num_cols)] for _ in range(snake.map.num_rows)] self.__build_cycle()
def __init__(self, snake, shortcuts=True): if snake.map.num_rows % 2 != 0 or snake.map.num_cols % 2 != 0: raise ValueError("num_rows and num_cols must be even.") super().__init__(snake) self._shortcuts = shortcuts self._path_solver = PathSolver(snake) self._table = [[_TableCell() for _ in range(snake.map.num_cols)] for _ in range(snake.map.num_rows)] self._build_cycle()
class GreedySolver(BaseSolver): def __init__(self, snake): super().__init__(snake) self._path_solver = PathSolver(snake) def next_direc(self): # Create a virtual snake s_copy, m_copy = self.snake.copy() # Step 1 self._path_solver.snake = self.snake path_to_food = self._path_solver.shortest_path_to_food() if path_to_food: # Step 2 s_copy.move_path(path_to_food) if m_copy.is_full(): return path_to_food[0] # Step 3 self._path_solver.snake = s_copy path_to_tail = self._path_solver.longest_path_to_tail() if len(path_to_tail) > 1: return path_to_food[0] # Step 4 self._path_solver.snake = self.snake path_to_tail = self._path_solver.longest_path_to_tail() if len(path_to_tail) > 1: return path_to_tail[0] # Step 5 head = self.snake.head() direc, max_dist = self.snake.direc, -1 for adj in head.all_adj(): if self.map.is_safe(adj): dist = Pos.manhattan_dist(adj, self.map.food) if dist > max_dist: max_dist = dist direc = head.direc_to(adj) return direc
class HamiltonSolver(BaseSolver): def __init__(self, snake, shortcuts=True): if snake.map.num_rows % 2 != 0 or snake.map.num_cols % 2 != 0: raise ValueError("num_rows and num_cols must be even.") super().__init__(snake) self._shortcuts = shortcuts self._path_solver = PathSolver(snake) self._table = [[_TableCell() for _ in range(snake.map.num_cols)] for _ in range(snake.map.num_rows)] self._build_cycle() @property def table(self): return self._table def next_direc(self): head = self.snake.head() nxt_direc = self._table[head.x][head.y].direc # Take shorcuts when the snake is not too long if self._shortcuts and self.snake.len() < 0.5 * self.map.capacity: path = self._path_solver.shortest_path_to_food() if path: tail, nxt, food = self.snake.tail(), head.adj(path[0]), self.map.food tail_idx = self._table[tail.x][tail.y].idx head_idx = self._table[head.x][head.y].idx nxt_idx = self._table[nxt.x][nxt.y].idx food_idx = self._table[food.x][food.y].idx # Exclude one exception if not (len(path) == 1 and abs(food_idx - tail_idx) == 1): head_idx_rel = self._relative_dist(tail_idx, head_idx, self.map.capacity) nxt_idx_rel = self._relative_dist(tail_idx, nxt_idx, self.map.capacity) food_idx_rel = self._relative_dist(tail_idx, food_idx, self.map.capacity) if nxt_idx_rel > head_idx_rel and nxt_idx_rel <= food_idx_rel: nxt_direc = path[0] return nxt_direc def _build_cycle(self): """Build a hamiltonian cycle on the map.""" path = self._path_solver.longest_path_to_tail() cur, cnt = self.snake.head(), 0 for direc in path: self._table[cur.x][cur.y].idx = cnt self._table[cur.x][cur.y].direc = direc cur = cur.adj(direc) cnt += 1 # Process snake bodies cur = self.snake.tail() for _ in range(self.snake.len() - 1): self._table[cur.x][cur.y].idx = cnt self._table[cur.x][cur.y].direc = self.snake.direc cur = cur.adj(self.snake.direc) cnt += 1 def _relative_dist(self, ori, x, size): if ori > x: x += size return x - ori
def __init__(self, snake): super().__init__(snake) self._path_solver = PathSolver(snake)
class AStarSolver(BaseSolver): def __init__(self, snake): super().__init__(snake) self._path_solver = PathSolver(snake) self.open_ = [Node(self.snake.head(), 0, 0, 0, self.snake.head())] self.closed_ = [] self.closed_dict = {} self.flag_new = True self.path = [] def g(self, parent): return parent.g + 1 def h(self, pos): # Manhattan distance between pos and food return abs(pos.x - self.map.food.x) + abs(pos.y - self.map.food.y) # Euclidean distance between pos and food # return math.sqrt(abs(pos.x - self.map.food.x) ** 2 + abs(pos.y - self.map.food.y) ** 2) def append_(self, node, list_): for i in range(len(list_)): if node.f < list_[i].f: list_.insert(i, node) return 1 elif node.f == list_[i].f and node.g + self.euclidean_dist( node.pos, self.map.food ) <= list_[i].g + self.euclidean_dist(list_[i].pos, self.map.food): list_.insert(i, node) return 1 list_.append(node) return 1 def euclidean_dist(self, pos1, pos2): return math.sqrt(abs(pos1.x - pos2.x)**2 + abs(pos1.y - pos2.y)**2) def print_list(self, list_): print("[", end="") for node in list_: print("(%d, %d)" % (node.pos.x, node.pos.y), end=" ") print("\b]") def print_list_f(self, list_): print("[", end="") for node in list_: print("%d(%d, %d)" % (node.f, node.pos.x, node.pos.y), end=" ") print("\b]") def get_path(self, pos1, pos2): path = [] while pos1.x != pos2.x or pos1.y != pos2.y: path.insert(0, pos2) pos2 = self.closed_dict[pos2].parent return path def next_direc(self): head = self.snake.head() if self.flag_new: self.open_ = [Node(self.snake.head(), 0, 0, 0, self.snake.head())] self.closed_ = [] self.closed_dict = {} self.path = [] else: if len(self.path) == 0: print(self.snake.direc) return self.snake.direc elif len(self.path) > 1: next_pos = self.path[0] self.path.pop(0) print(head.direc_to(next_pos)) return head.direc_to(next_pos) else: next_pos = self.path[0] self.path.pop(0) print(head.direc_to(next_pos)) print() self.flag_new = True return head.direc_to(next_pos) self.flag_new = False while self.open_: least_node = self.open_[0] self.open_.remove(least_node) print("head (%d, %d), least_node %d(%d, %d)" % (head.x, head.y, least_node.f, least_node.pos.x, least_node.pos.y)) self.print_list_f(self.open_) self.print_list(self.closed_) for adj in least_node.pos.all_adj(): print("(" + str(adj.x) + ", " + str(adj.y) + ")", end=" ") if not self.map.is_safe(adj): print("not safe") continue g = self.g(least_node) h = self.h(adj) f = g + h print("%d = %d + %d" % (f, g, h), end=" ") adj_node = Node(adj, g, h, f, least_node.pos) if self.map.food.x == adj.x and self.map.food.y == adj.y: self.append_(least_node, self.closed_) self.closed_dict[least_node.pos] = least_node least_node = adj_node print("FOOD") print("head (%d, %d), least_node %d(%d, %d)" % (head.x, head.y, least_node.f, least_node.pos.x, least_node.pos.y)) self.print_list_f(self.open_) self.print_list(self.closed_) self.append_(least_node, self.closed_) self.closed_dict[least_node.pos] = least_node self.path = self.get_path(head, adj) print(self.path) if len(self.path) > 1: next_pos = self.path[0] self.path.pop(0) print(head.direc_to(next_pos)) return head.direc_to(next_pos) else: next_pos = self.path[0] self.path.pop(0) print(head.direc_to(next_pos)) print() self.flag_new = True return head.direc_to(next_pos) flag = False for i in range(len(self.open_)): node = self.open_[i] if node.pos.x == adj.x and node.pos.y == adj.y: if node.f < f: flag = True else: self.open_.pop(i) break for i in range(len(self.closed_)): node = self.closed_[i] if node.pos.x == adj.x and node.pos.y == adj.y: if node.f < f: flag = True else: self.closed_dict[node.pos] = adj_node # self.closed_.pop(i) break if flag: print("flag") continue self.append_(adj_node, self.open_) print("appended") self.append_(least_node, self.closed_) self.closed_dict[least_node.pos] = least_node print("No path") self.flag_new = True self._path_solver.snake = self.snake path_to_tail = self._path_solver.longest_path_to_tail() if len(path_to_tail) > 0: print(path_to_tail[0]) return path_to_tail[0] else: for adj in head.all_adj(): if self.map.is_safe(adj): print(head.direc_to(adj)) return head.direc_to(adj) print(self.snake.direc) return self.snake.direc