Skip to content
Snippets Groups Projects
Commit 127e04be authored by DannyAbdi's avatar DannyAbdi
Browse files

Tidying code

parent 78ab1dea
Branches
No related tags found
1 merge request!2Master
import heapq
class AStar:
"""
Initializes the AStar solver with the given maze.
:param maze: The maze to solve.
"""
def __init__(self, maze):
self.maze = maze
self.start = (1, 1)
self.corners = set()
self.directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for i in range(maze.num_rows()):
for j in range(maze.num_columns()):
if maze[i][j] == 3 or maze[i][j] == 4:
self.corners.add((i, j))
"""
Heuristic function for A* search.
:param current: Current position.
:param corners: Set of corner positions.
:param visited_corners: Set of visited corner positions.
:return: Heuristic value.
"""
def heuristic1(self, current, corners, visited_corners):
remaining_corners = sum(1 for corner in corners if corner not in visited_corners)
first_corner = next(iter(corners))
return abs(current[0] - first_corner[0]) + abs(current[1] - first_corner[1]) + remaining_corners
"""
Alternative heuristic function for A* search.
:param current: Current position.
:param corners: Set of corner positions.
:param visited_corners: Set of visited corner positions.
:return: Heuristic value.
"""
def heuristic2(self, current, corners, visited_corners):
remaining_corners = sum(1 for corner in corners if corner not in visited_corners)
return ((current[0] - corners[0][0]) ** 2 + (current[1] - corners[0][1]) ** 2) ** 0.5 + remaining_corners
"""
Checks if all corners have been visited.
:param corners: Set of corner positions.
:param visited_corners: Set of visited corner positions.
:return: True if all corners are visited, False otherwise.
"""
def all_corners_visited(self, corners, visited_corners):
return all(corner in visited_corners for corner in corners)
"""
Finds the position of the goal in the maze.
:param maze: The maze to search.
:return: The position of the goal or None if not found.
"""
def find_goal_position(self, maze):
for i in range(maze.num_rows()):
for j in range(len(self.maze[0])):
if self.maze[i][j] == 3:
return (i, j)
return None
"""
Finds the shortest path from the start to the goal using A* search.
:param heuristic: Heuristic function to use.
:return: The shortest path as a list of positions, or None if no path is found.
"""
def find_shortest_path(self, heuristic):
start = (1, 1)
goal = None
visited = set()
frontier = [(0, start, [])]
heapq.heapify(frontier)
while frontier:
total_cost, current, path = heapq.heappop(frontier)
if self.maze[current[0]][current[1]] == 3:
goal = current
break
if current not in visited:
visited.add(current)
for dx, dy in self.directions:
x, y = current[0] + dx, current[1] + dy
if 0 <= x < self.maze.num_rows() and 0 <= y < self.maze.num_columns() and self.maze[x][y] != 1:
new_cost = total_cost + 1
heapq.heappush(frontier,
(new_cost + heuristic((x, y), self.corners, visited), (x, y), path + [current]))
if goal:
return path + [goal]
else:
return None
from collections import deque
class BFS:
"""
Initializes a BFS (Breadth-First Search) solver object.
"""
def __init__(self):
self.visited = set()
self.queue = deque()
self.parent = {} # Dictionary to store parent relationships for path reconstruction
self.path = [] # List to store the final path
"""
Returns a list of valid neighbours for a given cell in the maze.
:param maze: The maze grid.
:param i: The row index of the current cell.
:param j: The column index of the current cell.
:return: A list of valid neighbours.
"""
def get_neighbours(self, maze, i, j):
neighbours = []
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
for direction in directions:
ni, nj = i + direction[0], j + direction[1]
if 0 <= ni < len(maze) and 0 <= nj < len(maze[0]) and maze[ni][nj] != 1:
neighbours.append((ni, nj))
return neighbours
"""
Performs Breadth-First Search on the maze to find a path from the start to the goal.
:param maze: The maze grid.
:param start: The starting position.
:param goal: The goal position.
:return: True if a path is found, False otherwise.
"""
def bfs(self, maze, start, goal):
self.visited.clear()
self.queue.clear()
self.parent.clear() # Clear parent dictionary
self.path.clear() # Clear path
self.queue.append(start)
self.visited.add(start)
while self.queue:
current = self.queue.popleft()
if current == goal:
self.construct_path(start, goal)
return True # Goal found
for neighbour in self.get_neighbours(maze, current[0], current[1]):
if neighbour not in self.visited:
self.queue.append(neighbour)
self.visited.add(neighbour)
self.parent[neighbour] = current
return False # Goal not reached
"""
Constructs the path from the start to the goal using the parent dictionary.
:param start: The starting position.
:param goal: The goal position.
"""
def construct_path(self, start, goal):
current = goal
while current != start:
self.path.append(current)
current = self.parent[current]
self.path.append(start)
"""
Returns the reconstructed path from start to goal.
:return: The path as a list of positions.
"""
def get_path(self):
return list(reversed(self.path))
blank.png

239 B

from config import *
class Button:
"""
Initializes a Button object with specified attributes.
:param image: The image representing the button.
:param x: The x-coordinate of the button's position.
:param y: The y-coordinate of the button's position.
:param level: The level associated with the button.
:param player_controller: The player controller associated with the button.
"""
def __init__(self, image, x, y, level, player_controller):
self.image = image
self.x = x
self.y = y
self.level = level
self.is_clicked = False
self.player_controller = player_controller
"""
Draws the button on the game screen.
This method scales the button's image, positions it, and blits it onto the screen.
"""
def draw(self):
width = self.image.get_width()
height = self.image.get_height()
scaled_image = pygame.transform.scale(self.image, (int(width * 0.7), int(height * 0.7)))
image_rect = scaled_image.get_rect()
image_rect.right = self.x
image_rect.top = self.y
screen.blit(scaled_image, image_rect)
"""
Handles mouse clicks on the button.
If the button is clicked, it sets the maze level, resets the player position, and updates the 'is_clicked'
attribute.
:param mouse_pos: The current mouse position (x, y).
:param maze: The maze associated with the button.
"""
def handle_mouse_click(self, mouse_pos, maze):
if self.check_click(mouse_pos):
maze.set_level(self.level)
self.player_controller.reset_player_position()
self.is_clicked = True
else:
self.is_clicked = False
"""
Checks if the mouse click is within the button's boundaries.
:param mouse_pos: The current mouse position (x, y).
:return: True if the mouse click is within the button's boundaries, False otherwise.
"""
def check_click(self, mouse_pos):
x, y = mouse_pos
return self.x <= x <= self.x + self.image.get_width() and self.y <= y <= self.y + self.image.get_height()
button_easy.png

2.02 KiB

button_hard.png

1.72 KiB

button_normal.png

2.34 KiB

import pygame
TILE_SIZE = 64
screen = pygame.display.set_mode([640, 512])
timer = pygame.time.Clock()
FPS = 60
small_maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 4, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 4, 1],
[1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 2, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1],
[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1],
[1, 4, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
medium_maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 1],
[1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1],
[1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 4, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
large_maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 4, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 2, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 1],
[1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 2, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]
levels = [small_maze, medium_maze, large_maze]
current_level = levels[0]
player_model = pygame.Rect(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)
tiles = [pygame.image.load('blank.png'), pygame.image.load('wall.png'), pygame.image.load('blank.png'),
pygame.image.load('goal.png'), pygame.image.load('blank.png')]
easy_button = pygame.image.load('button_easy.png').convert_alpha()
normal_button = pygame.image.load('button_normal.png').convert_alpha()
hard_button = pygame.image.load('button_hard.png').convert_alpha()
WIDTH, HEIGHT = TILE_SIZE * len(current_level[0]), TILE_SIZE * len(current_level)
class DFS:
"""
Initializes a DFS (Depth-First Search) solver object.
"""
def __init__(self):
self.visited = set()
self.path = []
"""
Returns a list of valid neighbours for a given cell in the maze.
:param maze: The maze grid.
:param i: The row index of the current cell.
:param j: The column index of the current cell.
:return: A list of valid neighbours.
"""
def get_neighbours(self, maze, i, j):
neighbours = []
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
for direction in directions:
ni, nj = i + direction[0], j + direction[1]
if 0 <= ni < len(maze) and 0 <= nj < len(maze[0]) and maze[ni][nj] != 1:
neighbours.append((ni, nj))
return neighbours
"""
Performs Depth-First Search on the maze to find a path from the start to the goal.
:param maze: The maze grid.
:param start: The starting position.
:param goal: The goal position.
:return: True if a path is found, False otherwise.
"""
def dfs(self, maze, start, goal):
self.visited.clear()
self.path.clear()
return self._dfs(maze, start, goal)
"""
Recursive helper function for DFS.
:param maze: The maze grid.
:param current: The current position.
:param goal: The goal position.
:return: True if a path is found, False otherwise.
"""
def _dfs(self, maze, current, goal):
if current == goal:
self.path.append(current)
return True
self.visited.add(current)
for neighbour in self.get_neighbours(maze, current[0], current[1]):
if neighbour not in self.visited and self._dfs(maze, neighbour, goal):
self.path.append(current)
return True
return False
"""
Returns the reconstructed path from start to goal.
:return: The path as a list of positions.
"""
def get_path(self):
return list(reversed(self.path))
import heapq
class Dijkstra:
"""
Initializes a Dijkstra object.
:param maze: The maze used for navigation and solving.
"""
def __init__(self, maze):
self.maze = maze
"""
Calculates the cost of moving from one cell to another based on the cell type.
:param cell_type: The type of cell (0 for empty, 1 for wall, 2 for goal, etc.).
:return: The cost of moving to the cell.
"""
def calculate_cost(self, cell):
if cell == 0:
return 1
elif cell == 2:
return 100
elif cell == 3:
return 1
else:
return float('inf')
"""
Finds the shortest path from the player's current position to a specific location using Dijkstra's algorithm.
:param start_position: The (row, column) position of the start.
:param goal_position: The (row, column) position of the goal.
:return: The shortest path from the start position to the goal position.
"""
def find_shortest_path(self, start_position, goal_position):
frontier = [(0, start_position)]
came_from = {}
cost_so_far = {}
came_from[start_position] = None
cost_so_far[start_position] = 0
while frontier:
current_cost, current = heapq.heappop(frontier)
if current == goal_position:
break
for next in self.get_neighbors(current):
new_cost = cost_so_far[current] + self.calculate_cost(self.maze.current_level[next[0]][next[1]])
if next not in cost_so_far or new_cost < cost_so_far[next]:
cost_so_far[next] = new_cost
priority = new_cost
heapq.heappush(frontier, (priority, next))
came_from[next] = current
current = goal_position
path = []
while current != start_position:
path.append(current)
current = came_from[current]
path.append(start_position)
path.reverse()
return path
"""
Gets the valid neighboring cells of a given cell.
:param cell: The (row, column) position of the cell.
:return: A list of valid neighboring cells.
"""
def get_neighbors(self, cell):
neighbors = []
for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
x, y = cell[1] + dx, cell[0] + dy
if 0 <= x < len(self.maze.current_level[0]) and 0 <= y < len(self.maze.current_level):
neighbors.append((y, x))
return neighbors
\ No newline at end of file
enemy.png

232 B

import random
from config import *
"""
Class representing an enemy agent in the game.
"""
class Enemy:
"""
Initialise the Enemy object.
:param maze (Maze): The maze object representing the game environment.
:param x (int): The x-coordinate of the enemy's position.
:param y (int): The y-coordinate of the enemy's position.
"""
def __init__(self, maze):
self.maze = maze
self.x, self.y = self.reset_position()
"""
Reset the position of the enemy to a valid starting position in the maze.
:return: tuple: A tuple containing the x and y coordinates of the enemy's position.
"""
def reset_position(self):
while True:
num_rows = self.maze.num_rows()
num_cols = self.maze.num_columns()
random_row = random.randint(0, num_rows - 1)
start_col = num_cols - 2
self.x = start_col * TILE_SIZE
self.y = random_row * TILE_SIZE
if self.maze[random_row][start_col] != 1:
return self.x, self.y
"""
Draw the enemy on the screen.
:param screen: The Pygame screen surface to draw on.
"""
def draw(self, screen):
pygame.draw.rect(screen, 'blue', (self.x, self.y, TILE_SIZE, TILE_SIZE))
from minmax import MinMax
class Expectimax(MinMax):
"""
Initializes an Expectimax object.
:param maze: The maze used for navigation and solving.
"""
def __init__(self, maze):
super().__init__(maze)
"""
Performs the Expectimax algorithm to determine the best move the enemy can take.
:param player_position: The current position of the player agent.
:param enemy_position: The current position of the enemy agent.
:param depth: The depth of the Expectimax search tree.
:param maximising_player: True if maximising player (enemy), False if minimising player (player).
:return: The best move for the enemy agent.
"""
def expectimax(self, player_position, enemy_position, depth, maximising_player):
if depth == 0 or self.game_over():
# Evaluate the current game state
return self.evaluate(player_position, enemy_position)
if maximising_player:
max_eval = float('-inf')
valid_moves = self.get_valid_moves(enemy_position)
for move in valid_moves:
if self.is_valid_move(move):
eval = self.expectimax(player_position, move, depth - 1, False)
max_eval = max(max_eval, eval)
return max_eval
else:
total_eval = 0
valid_moves = self.get_valid_moves(player_position)
num_moves = len(valid_moves)
for move in valid_moves:
if self.is_valid_move(move):
eval = self.expectimax(move, enemy_position, depth - 1, True)
total_eval += eval
return total_eval / num_moves
\ No newline at end of file
"""
Class representing the main game logic.
"""
class Game:
"""
Initialize the Game object.
:param maze: The maze grid representing the game environment.
"""
def __init__(self, maze):
self.maze = maze
self.player_position = (1, 1)
self.enemy_positions = self.get_enemy_positions()
"""
Find the current position of the player in the maze.
:return: The (row, column) position of the player, or None if not found.
"""
def get_player_position(self):
for i in range(len(self.maze)):
for j in range(len(self.maze[0])):
if self.maze[i][j] == 0:
return i, j
return None
"""
Find the positions of the enemy agents in the maze.
:return: A list containing the positions of the enemy agents.
"""
def get_enemy_positions(self):
# Implement logic to find the opponents' positions in the maze
pass
"""
Update the positions of the player and enemy agents based on the current state of the maze.
"""
def update_positions(self):
self.player_position = self.get_player_position()
self.enemy_positions = self.get_enemy_positions()
"""
Check if the game is over.
:return: True if the game is over, False otherwise.
"""
def check_game_over(self):
# Implement logic to check if the game is over (e.g., player reached the goal or caught by opponent)
pass
goal.png

238 B

from maze import *
from player import *
from button import *
from playerController import *
from dfs import *
from bfs import *
from dijkstra import *
from aStar import *
from enemy import *
from minmax import *
pygame.init()
maze = Maze(small_maze)
player = Player(TILE_SIZE, TILE_SIZE)
enemy1 = Enemy(maze)
depth = 3
player_controller = PlayerController(player, maze)
easy_button = Button(easy_button, 150, 10, small_maze, player_controller)
normal_button = Button(normal_button, 300, 10, medium_maze, player_controller)
hard_button = Button(hard_button, 426, 10, large_maze, player_controller)
dfs_solver = DFS()
bfs_solver = BFS()
dijkstra_solver = Dijkstra(maze)
aStar_solver = AStar(maze)
minmax_solver = MinMax(maze)
player_controller.set_dfs_solver(dfs_solver)
player_controller.set_bfs_solver(bfs_solver)
player_controller.set_dijkstra_solver(dijkstra_solver)
player_controller.set_astar_solver(aStar_solver)
# best_move = minmax_solver.minmax(player_position, enemy_position, depth, True)
run = True
while run:
timer.tick(FPS)
maze.draw()
player.draw(screen)
enemy1.draw(screen)
easy_button.draw()
normal_button.draw()
hard_button.draw()
# player_controller.move_to_goal_dfs()
player_controller.move_to_goal_bfs()
# player_controller.move_to_goal_dijkstra()
# player_controller.move_to_goal_astar()
direction = pygame.key.get_pressed()
player_controller.move_player(direction)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
easy_button.handle_mouse_click(mouse_pos, maze)
normal_button.handle_mouse_click(mouse_pos, maze)
hard_button.handle_mouse_click(mouse_pos, maze)
if easy_button.is_clicked or normal_button.is_clicked or hard_button.is_clicked:
player_controller.reset_player_position()
enemy1.reset_position()
pygame.display.flip()
pygame.quit()
from config import *
class Maze:
"""
Initializes a Maze object with an initial maze level.
:param initial_level: The initial maze level.
"""
def __init__(self, initial_level):
self.current_level = initial_level
self.update_screen_size()
"""
Get the length of the maze, which is the number of rows in the maze.
:return: The number of rows in the maze.
"""
def num_rows(self):
return len(self.current_level)
"""
Get the number of columns in the maze.
:return: The number of columns in the maze.
"""
def num_columns(self):
return len(self.current_level[0])
"""
Retrieves the item at the specified index.
:param index: The index of the item to retrieve.
:return: The item at the specified index.
"""
def __getitem__(self, index):
return self.current_level[index]
"""
Sets a new maze level and updates the screen size accordingly.
:param new_level: The new maze level.
"""
def set_level(self, new_level):
self.current_level = new_level
self.update_screen_size()
"""
Updates the size of the game screen based on the current maze level.
"""
def update_screen_size(self):
screen_size = (len(self.current_level[0]) * TILE_SIZE, len(self.current_level) * TILE_SIZE)
screen = pygame.display.set_mode(screen_size)
"""
Draws the current maze level on the game screen.
"""
def draw(self):
for row in range(len(self.current_level)):
for column in range(len(self.current_level[row])):
x = column * TILE_SIZE
y = row * TILE_SIZE
tile = tiles[self.current_level[row][column]]
screen.blit(tile, (x, y))
class MinMax:
"""
Initializes a MinMax object.
:param maze: The maze used for navigation and solving.
"""
def __init__(self, maze):
self.maze = maze
"""
Gets the valid moves that an enemy agent can take.
:param player_position: The current position of the player agent.
:return: A list of valid moves for the enemy agent.
"""
def get_valid_moves(self, player_position):
valid_moves = []
for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
x, y = player_position[1] + dx, player_position[0] + dy
if (0 <= x < len(self.maze.current_level[0]) and 0 <= y < len(self.maze.current_level) and
self.maze.current_level[y][x] != 1):
valid_moves.append((y, x))
return valid_moves
"""
Checks if a move to a given position is valid.
:param position: The position to move to.
:return: True if the move is valid, False otherwise.
"""
def is_valid_move(self, position):
x, y = position[1], position[0]
return (0 <= x < len(self.maze.current_level[0]) and 0 <= y < len(self.maze.current_level) and
self.maze.current_level[y][x] != 1)
"""
Performs the MinMax algorithm with Alpha-beta pruning to determine the best move the enemy can take.
:param player_position: The current position of the player agent.
:param enemy_position: The current position of the enemy agent.
:param depth: The depth of the MinMax search tree.
:param alpha: The best value that the maximizing player currently can guarantee.
:param beta: The best value that the minimizing player currently can guarantee.
:param maximizing_player: True if maximizing player (enemy), False if minimizing player (player).
:return: The best move for the enemy agent.
"""
def minmax(self, player_position, enemy_position, depth, alpha, beta, maximizing_player):
if depth == 0 or self.game_over():
# Evaluate the current game state
return self.evaluate(player_position, enemy_position)
if maximizing_player:
max_eval = float('-inf')
valid_moves = self.get_valid_moves(enemy_position)
for move in valid_moves:
if self.is_valid_move(move):
eval = self.minmax(player_position, move, depth - 1, alpha, beta, False)
max_eval = max(max_eval, eval)
alpha = max(alpha, eval)
if beta <= alpha:
break
return max_eval
else:
min_eval = float('inf')
valid_moves = self.get_valid_moves(player_position)
for move in valid_moves:
if self.is_valid_move(move):
eval = self.minmax(move, enemy_position, depth - 1, alpha, beta, True)
min_eval = min(min_eval, eval)
beta = min(beta, eval)
if beta <= alpha:
break
return min_eval
"""
Evaluate the current game state.
:param player_position: The current position of the player agent.
:param enemy_position: The current position of the enemy agent.
:return: The evaluation of the current game state.
"""
def evaluate(self, player_position, enemy_position):
# Implement the evaluation logic here
pass
"""
Check if the game is over.
:return: True if the game is over, False otherwise.
"""
def game_over(self):
# Implement the game over condition here
pass
from config import *
class Player:
"""
Initialises a Player object with a specified position.
:param x: The x-coordinate of the player's position.
:param y: The y-coordinate of the player's position
"""
def __init__(self, x, y):
self.x = x
self.y = y
"""
Draws the player on the game screen using Pygame.
This method utilizes the Pygame library to draw a white rectangle representing the player
at the specified (x, y) coordinates with the size of TILE_SIZE.
"""
def draw(self, screen):
pygame.draw.rect(screen, 'white', (self.x, self.y, TILE_SIZE, TILE_SIZE))
from config import *
from dfs import *
from bfs import *
from dijkstra import *
from game import *
class PlayerController:
"""
Initializes a PlayerController object.
:param player: The player controlled by this controller.
:param maze: The maze used for navigation and solving.
"""
def __init__(self, player, maze):
self.player = player
self.maze = maze
self.player_position = (1, 1)
self.dfs_solver = DFS()
self.bfs_solver = BFS()
self.dijkstra_solver = None
self.astar_solver = None
"""
Moves the player in the specified direction based on keyboard input.
:param direction: A dictionary representing the keys pressed.
"""
def move_player(self, direction):
new_x = self.player.x
new_y = self.player.y
if direction[pygame.K_UP]:
new_y -= TILE_SIZE
new_position = (self.player_position[0] - 1, self.player_position[1])
elif direction[pygame.K_DOWN]:
new_y += TILE_SIZE
new_position = (self.player_position[0] + 1, self.player_position[1])
elif direction[pygame.K_LEFT]:
new_x -= TILE_SIZE
new_position = (self.player_position[0], self.player_position[1] - 1)
elif direction[pygame.K_RIGHT]:
new_x += TILE_SIZE
new_position = (self.player_position[0], self.player_position[1] + 1)
else:
return
if self.is_valid_position(new_position):
self.player_position = new_position
if self.check_collision(new_x, new_y):
self.player.x = new_x
self.player.y = new_y
"""
Checks if a given position is valid within the maze.
:param position: The position to check in the form of (x, y) coordinates.
:return: True if the position is valid and does not collide with walls, False otherwise.
"""
def is_valid_position(self, position):
x, y = position
return 0 <= x < len(self.maze.current_level) and 0 <= y < len(self.maze.current_level[0]) and \
self.maze.current_level[x][y] != 1
"""
Checks if the player will collide with walls at the specified position.
:param x: The x-coordinate of the potential position.
:param y: The y-coordinate of the potential position.
:return: True if collision is detected, False otherwise.
"""
def check_collision(self, x, y):
row = y // TILE_SIZE
column = x // TILE_SIZE
return self.maze.current_level[row][column] != 1
"""Resets the player's position to the starting point."""
def reset_player_position(self):
self.player.x = TILE_SIZE
self.player.y = TILE_SIZE
"""
Sets the DFS solver used by the controller.
:param dfs_solver: The DFS solver object.
"""
def set_dfs_solver(self, dfs_solver):
self.dfs_solver = dfs_solver
"""
Sets the BFS solver used by the controller.
:param bfs_solver: The BFS solver object.
"""
def set_bfs_solver(self, bfs_solver):
self.bfs_solver = bfs_solver
"""
Sets the Dijkstra solver used by the controller.
:param dijkstra_solver: The Dijkstra solver object.
"""
def set_dijkstra_solver(self, dijkstra_solver):
self.dijkstra_solver = dijkstra_solver
"""
Sets the A* solver used by the controller.
:param dijkstra_solver: The A* solver object.
"""
def set_astar_solver(self, astar_solver):
self.astar_solver = astar_solver
"""
Moves the player towards the goal using DFS.
"""
def move_to_goal_dfs(self):
start_position = (self.player.y // TILE_SIZE, self.player.x // TILE_SIZE)
goal_position = self.find_goal_position()
if goal_position:
if self.dfs_solver.dfs(self.maze.current_level, start_position, goal_position):
path = self.dfs_solver.get_path()
self.follow_path(path)
"""
Moves the player towards the goal using BFS pathfinding.
"""
def move_to_goal_bfs(self):
start_position = (self.player.y // TILE_SIZE, self.player.x // TILE_SIZE)
goal_position = self.find_goal_position()
if goal_position:
if self.bfs_solver.bfs(self.maze.current_level, start_position, goal_position):
path = self.bfs_solver.get_path()
self.follow_path(path)
"""
Moves the player towards the goal using Dijkstra's algorithm.
"""
def move_to_goal_dijkstra(self):
if self.dijkstra_solver is not None:
start_position = (self.player.y // TILE_SIZE, self.player.x // TILE_SIZE)
goal_position = self.find_goal_position()
if goal_position:
path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
if path:
self.follow_path(path)
"""
Moves the player to all 4 corners using A* algorithm.
"""
def move_to_goal_astar(self):
if self.astar_solver is None:
print("A* solver not initialized. Please call set_astar_solver first.")
return
if not self.maze:
print("Maze not initialized.")
return
start_position = (self.player.y // TILE_SIZE, self.player.x // TILE_SIZE)
goal_position = self.astar_solver.find_goal_position(self.maze)
if goal_position:
path = self.astar_solver.find_shortest_path(self.astar_solver.heuristic1)
if path:
self.follow_path(path)
"""
Moves the player along the specified path.
:param path: The path to follow.
"""
def follow_path(self, path):
for position in path:
goal_y, goal_x = position
target_y, target_x = goal_y * TILE_SIZE, goal_x * TILE_SIZE
while self.player.x != target_x or self.player.y != target_y:
direction_x = 1 if target_x > self.player.x else -1 if target_x < self.player.x else 0
direction_y = 1 if target_y > self.player.y else -1 if target_y < self.player.y else 0
new_x, new_y = self.player.x + direction_x * TILE_SIZE, self.player.y + direction_y * TILE_SIZE
if self.maze.current_level[new_y // TILE_SIZE][new_x // TILE_SIZE] != 1:
# Draw the path on the screen
self.maze.draw()
self.draw_path(path)
# Move the player
self.player.x, self.player.y = new_x, new_y
pygame.display.flip()
pygame.time.delay(100)
"""
Draws the path on the screen for visualization.
:param path: The path to draw.
"""
def draw_path(self, path):
for position in path:
pygame.draw.rect(
screen,
(0, 255, 0), # Green color for the path
(position[1] * TILE_SIZE, position[0] * TILE_SIZE, TILE_SIZE, TILE_SIZE),
)
"""
Finds the position of the goal in the maze.
:return: The (row, column) position of the goal or None if not found.
"""
def find_goal_position(self):
for i in range(len(self.maze.current_level)):
for j in range(len(self.maze.current_level[0])):
if self.maze.current_level[i][j] == 3:
return i, j
return None
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment