| from settings import * |
| from queue import Queue |
| from snake import create_duplicate_snake, deepcopy |
| from random import randrange |
|
|
| def get_neighbours(position): |
| ''' |
| Get neighbouring positions from the current position |
| |
| Args: |
| - position: Tuple representing the current position (x, y) |
| Returns: |
| - List of neighbouring positions |
| ''' |
|
|
| neighbours = [[position[0] + BLOCK_SIZE, position[1]], |
| [position[0] - BLOCK_SIZE, position[1]], |
| [position[0], position[1] + BLOCK_SIZE], |
| [position[0], position[1] - BLOCK_SIZE]] |
| |
| neighbours_within_grid = [] |
| for pos in neighbours: |
| if pos in GRID: |
| neighbours_within_grid.append(pos) |
|
|
| return neighbours_within_grid |
|
|
| def get_available_neighbours(snake, position, apple_pos): |
| valid_neighbours = [] |
| neighbours = get_neighbours(position) |
| for neighbour in neighbours: |
| if is_position_available(snake, neighbour) and apple_pos != neighbour: |
| valid_neighbours.append(neighbour) |
| return valid_neighbours |
|
|
| def is_position_available(snake, position): |
| if (0 <= position[0] <= ROWS or 0 <= position[1] <= ROWS) and position not in snake.body: |
| return True |
| return False |
|
|
| def distance(position1, position2): |
| x1, x2 = position1[0], position2[0] |
| y1, y2 = position1[1], position2[1] |
| return abs(x2 - x1) + abs(y2 - y1) |
| |
|
|
| def longest_path_to_tail(snake, apple_pos): |
| neighbours = get_available_neighbours(snake, (snake.head.x, snake.head.y), apple_pos) |
| path = [] |
| if neighbours: |
| d = -9999 |
| for neighbour in neighbours: |
| tail_pos = (snake.tail.x, snake.tail.y) |
| if distance(neighbour, tail_pos) > d: |
| dup_snake = create_duplicate_snake(snake) |
| dup_snake.go_to(neighbour) |
| dup_snake.move() |
| if dup_snake.head.x == apple_pos[0] and dup_snake.head.y == apple_pos[1]: |
| dup_snake.grow() |
| if path_to_tail(dup_snake): |
| path.append(neighbour) |
| d = distance(neighbour, tail_pos) |
| if path: |
| return [path[-1]] |
| |
| def find_safe_move(snake, apple_pos): |
| neighbours = get_available_neighbours(snake, (snake.head.x, snake.head.y), apple_pos) |
| path = [] |
| if neighbours: |
| path.append(neighbours[randrange(len(neighbours))]) |
| dup_snake = create_duplicate_snake(snake) |
| for move in path: |
| dup_snake.go_to(move) |
| dup_snake.move() |
| if path_to_tail(dup_snake): |
| return path |
| else: |
| return path_to_tail(snake) |
| |
| def BFS(start_pos, target_pos, snake_body): |
| ''' |
| Perform Breadth-First Search (BFS) to find the shortest path from the snake's head to the apple |
| |
| Args: |
| - start_pos: Tuple representing the starting position (snake's head) |
| - target_pos: Tuple representing the target position (apple) |
| - snake_body: List representing the coordinates of the snake's body |
| Returns: |
| - List representing the sequence of moves to the apple as a list of tuples (coordinates) |
| ''' |
| |
| queue = Queue() |
| queue.put((start_pos, [])) |
|
|
| visited = set() |
| visited.add(start_pos) |
|
|
| while not queue.empty(): |
| current_pos, path = queue.get() |
|
|
| |
| if current_pos == target_pos: |
| return path |
| |
| |
| neighbours = get_neighbours(current_pos) |
| for neighbour in neighbours: |
| if is_position_safe(snake_body, tuple(neighbour)) and tuple(neighbour) not in visited: |
| |
| direction = [neighbour[0] - current_pos[0], neighbour[1] - current_pos[1]] |
| visited.add(tuple(neighbour)) |
| new_path = path + [tuple(direction)] |
| queue.put((tuple(neighbour), new_path)) |
| |
| return [] |
|
|
| def is_position_safe(snake_body, position): |
| if position[0] < 0 or position[0] >= WIDTH or position[1] < 0 or position[1] >= HEIGHT: |
| return False |
| for square in snake_body: |
| if position == (square.x, square.y): |
| return False |
| return True |
|
|
| def set_path(snake, apple): |
| apple_pos = (apple.apple.x, apple.apple.y) |
| snake_head_pos = (snake.head.x, snake.head.y) |
| if snake.score == SNAKE_MAX_LEN - 1 and apple_pos in get_neighbours(snake_head_pos): |
| winning_path = [apple_pos] |
| return winning_path |
| |
| duplicate_snake = create_duplicate_snake(snake) |
| dup_head_pos = (duplicate_snake.head.x, duplicate_snake.head.y) |
|
|
| initial_path = BFS(dup_head_pos, apple_pos, duplicate_snake.body) |
| secondary_path = [] |
| |
| if initial_path: |
| for position in initial_path: |
| duplicate_snake.go_to(position) |
| duplicate_snake.move() |
| duplicate_snake.grow() |
| secondary_path = path_to_tail(duplicate_snake) |
| if secondary_path: |
| |
| return initial_path |
| if longest_path_to_tail(snake, apple_pos) and snake.score % 2 == 0: |
| |
| return longest_path_to_tail(snake, apple_pos) |
| if find_safe_move(snake, apple_pos): |
| |
| return find_safe_move(snake, apple_pos) |
| if path_to_tail(snake): |
| |
| return path_to_tail(snake) |
| print("snake is in danger") |
|
|
| def path_to_tail(snake): |
| body_len = len(snake.body) |
| tail_pos = deepcopy(snake.tail) |
| |
| if len(snake.body) > 1: |
| snake.tail = snake.body.pop(-1) |
| |
| head_pos = (snake.head.x, snake.head.y) |
| snake_tail_pos = (tail_pos.x, tail_pos.y) |
| path = BFS(head_pos, (snake_tail_pos), snake.body) |
|
|
| if len(snake.body) < body_len: |
| snake.body.append(snake.tail) |
| snake.tail = snake.body[-1] |
|
|
| return path |