def move(self, direction: Direction) -> (Point, bool, bool): # move the snake in a new direction snake_head = self.snake.move(direction, self.ate_food) self._n_steps += 1 if self.snake.touches_tail() or self.__is_touching_wall(): self.game_over() return Point.from_numpy( snake_head), self.ate_food, self.is_game_over() # check if the snake touches food if self.__is_touching_food(): self.ate_food = True self._n_food_eaten += 1 self._n_steps_without_food = 0 print("Ate food") self.__random_food() else: self.ate_food = False self._n_steps_without_food += 1 if self._n_steps_without_food > 1000: self.game_over() return Point.from_numpy( snake_head), self.ate_food, self.is_game_over() self.__draw_snake() self.__draw_food() return Point.from_numpy(snake_head), self.ate_food, self.is_game_over()
def lidar_west_pulse(self): """ The lidar beam that travels west within the game. :return: a point of where the beam either hit a wall or a part of the environment. """ pulse_x, pulse_y = self.snakeEnv.snake.head.pos() west_pulse = Point(pulse_x, pulse_y) west_wall = Point((-1) * (self.snakeEnv.screen_width / 2), pulse_y) hit_obstacle = False hit_apple = False while True: if west_pulse.x <= west_wall.x: hit_obstacle = True break if self.snakeEnv.snake.point_is_in_tail(west_pulse)[0]: hit_obstacle = True break if self.snakeEnv.current_food.point_is_in_food(west_pulse)[0]: hit_apple = True break west_pulse.offset(-1, 0) return (west_pulse, hit_obstacle, hit_apple) """
def lidar_north_pulse(self): """ The lidar beam that travels north within the game. :return: a point of where the beam either hit a wall or a part of the environment. """ pulse_x, pulse_y = self.snakeEnv.snake.head.pos() north_pulse = Point(pulse_x, pulse_y) north_wall = Point(pulse_x, self.snakeEnv.screen_height / 2) hit_obstacle = False hit_apple = False while True: if north_pulse.y >= north_wall.y: hit_obstacle = True break if self.snakeEnv.snake.point_is_in_tail(north_pulse)[0]: hit_obstacle = True break if self.snakeEnv.current_food.point_is_in_food(north_pulse)[0]: hit_apple = True break north_pulse.offset(0, 1) return (north_pulse, hit_obstacle, hit_apple)
def __left_lidar(self): """ Gets the Snake's 5 point lidar distances when it is facing left. Parameters: None Returns: lidar - A 1D numpy array with 5 entries, one for each lidar distance. """ lidar_pulses = [ self.lidar_south_pulse(), self.lidar_south_west_pulse(), self.lidar_west_pulse(), self.lidar_north_west_pulse(), self.lidar_north_pulse() ] lidar = np.zeros((len(lidar_pulses) * 3), dtype=np.float32) snake_x, snake_y = self.snakeEnv.snake.head.pos( )[0], self.snakeEnv.snake.head.pos()[1] snake_pos = Point(snake_x, snake_y) for i in range(len(lidar_pulses)): lidar_pulse = lidar_pulses[i] index = i * 3 lidar[index] = snake_pos.distance(lidar_pulse[0]) lidar[index + 1] = lidar_pulse[1] lidar[index + 2] = lidar_pulse[2] self.lidar_end_points = [ lidar_pulses[0][0], lidar_pulses[1][0], lidar_pulses[2][0], lidar_pulses[3][0], lidar_pulses[4][0] ] return lidar
def __stop_lidar(self): """ Gets the snakes 8 point lidar in it's starting position. :return: """ lidar_pulses = [ self.lidar_north_pulse(), self.lidar_north_east_pulse(), self.lidar_east_pulse(), self.lidar_south_east_pulse(), self.lidar_south_pulse(), self.lidar_south_west_pulse(), self.lidar_west_pulse(), self.lidar_north_west_pulse() ] lidar = np.zeros((len(lidar_pulses) * 3), dtype=np.float32) snake_x, snake_y = self.snakeEnv.snake.head.pos( )[0], self.snakeEnv.snake.head.pos()[1] snake_pos = Point(snake_x, snake_y) for i in range(len(lidar_pulses)): lidar_pulse = lidar_pulses[i] index = i * 3 lidar[index] = snake_pos.distance(lidar_pulse[0]) lidar[index + 1] = lidar_pulse[1] lidar[index + 2] = lidar_pulse[2] self.lidar_end_points = [ lidar_pulse[0] for lidar_pulse in lidar_pulses ] return lidar
def __get_food_distance_reward(self, previous_location: Point, current_location: Point): """ :param previous_location: A type point that specifies previous location of the environment :param current_location: A type point that specifies the current location of the environment :return: 1 if the environment moved towards the food, 0 if the environment moved away from the food """ """ food_x = self.current_food.head.xcor() food_y = self.current_food.head.ycor() food_point = Point(food_x, food_y) if (previous_location.distance(food_point) > current_location.distance(food_point)): return 1 return """ def normalise(x): return 1 - (x / self.max_distance) food_x = self.current_food.head.xcor() food_y = self.current_food.head.ycor() food_point = Point(food_x, food_y) multiplier = len( self.snake.segments) if len(self.snake.segments) > 2 else 1 return normalise(current_location.distance(food_point)) * multiplier
def distance_to_apple_percentage_tail_length_multiplier(env: SnakeEnv): def normalise(x): return 1 - (x / env.max_distance) food_x = env.current_food.head.xcor() food_y = env.current_food.head.ycor() food_point = Point(food_x, food_y) current_location = Point(env.snake.head.xcor(), env.snake.head.ycor()) multiplier = len(env.snake.segments) if len(env.snake.segments) > 2 else 1 return normalise(current_location.distance(food_point)) * multiplier
def __wall_distances(self, snake_head: Point, direction: Direction) -> np.array: north_wall = Point(snake_head.x, 0) west_wall = Point(0, snake_head.y) south_wall = Point(snake_head.x, self.game.dimensions()[1]) east_wall = Point(self.game.dimensions()[0], snake_head.y) snake_north_wall_dist = snake_head.distance(north_wall) snake_west_wall_dist = snake_head.distance(west_wall) snake_south_wall_dist = snake_head.distance(south_wall) snake_east_wall_dist = snake_head.distance(east_wall) if direction == Direction.UP: return np.array([ snake_west_wall_dist, snake_north_wall_dist, snake_east_wall_dist ]) if direction == Direction.RIGHT: return np.array([ snake_north_wall_dist, snake_east_wall_dist, snake_south_wall_dist ]) if direction == Direction.DOWN: return np.array([ snake_east_wall_dist, snake_south_wall_dist, snake_west_wall_dist ]) if direction == Direction.LEFT: return np.array([ snake_south_wall_dist, snake_west_wall_dist, snake_north_wall_dist ])
def lidar_south_east_pulse(self): """ The lidar beam that travels south east within the game. :return: a point of where the beam either hit a wall or a part of the environment. """ pulse_x, pulse_y = self.snakeEnv.snake.head.pos() south_east_pulse = Point(pulse_x, pulse_y) south_wall = Point(pulse_x, (-1) * (self.snakeEnv.screen_height / 2)) east_wall = Point(self.snakeEnv.screen_width / 2, pulse_y) hit_obstacle = False hit_apple = False while True: if south_east_pulse.y <= south_wall.y or south_east_pulse.x >= east_wall.x: hit_obstacle = True break if self.snakeEnv.snake.point_is_in_tail(south_east_pulse)[0]: hit_obstacle = True break if self.snakeEnv.current_food.point_is_in_food( south_east_pulse)[0]: hit_apple = True break south_east_pulse.offset(1, -1) return (south_east_pulse, hit_obstacle, hit_apple) """
def __random_food(self) -> Point: available_slots = self.__available_food_positions() grid_slot = random.choice(tuple(available_slots)) n_columns = self.screen_width / self.snake_size n_rows = self.screen_height / self.snake_size slot_x = grid_slot % n_columns slot_y = math.floor(grid_slot / n_rows) food_x = slot_x * self.snake_size food_y = slot_y * self.snake_size # self.food = pygame.Rect(food_x, food_y, self.snake_size, self.snake_size) return Point(food_x, food_y)
def move(self, direction: Direction) -> (Point, bool, bool): snake_head = self.grid.move_snake(direction, self.ate_food) self._n_steps += 1 if self.grid.snake().touches_tail( ) or self.grid.snake_is_touching_wall(): self.game_over() return Point.from_numpy( snake_head), self.ate_food, self.is_game_over() # check if the snake touches food if self.grid.snake_is_touching_food(): self.ate_food = True self._n_food_eaten += 1 self._n_steps_without_food = 0 self.grid.random_food() else: self.ate_food = False self._n_steps_without_food += 1 if self._n_steps_without_food > 1000: self.game_over() return Point.from_numpy(snake_head), self.ate_food, self.is_game_over()
def start_position(self) -> Point: start_x = self.screen_width / 2 start_y = self.screen_height / 2 return Point(start_x, start_y)
def food_position(self) -> Point: return Point.from_numpy(self.grid.food().position())
def snake_head(self) -> Point: return Point.from_numpy(self.grid.snake().head())
def __is_touching_food(self) -> bool: return Point.from_numpy(self.snake.head()) == self.food_position()
def get_current_location(self): return Point(self.head.xcor(), self.head.ycor())