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
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