From f05b40aa84ba561abc2ffae1b2f86ac22106bb3e Mon Sep 17 00:00:00 2001
From: DannyAbdi <dannyabdi13@gmail.com>
Date: Thu, 4 Apr 2024 17:07:58 +0100
Subject: [PATCH 1/2] fixed test bugs

---
 Classes/aStar.py                | 100 --------------
 Classes/bfs.py                  |  90 -------------
 Classes/button.py               |  63 ---------
 Classes/config.py               |  75 -----------
 Classes/dfs.py                  |  70 ----------
 Classes/dijkstra.py             |  78 -----------
 Classes/enemy.py                |  50 -------
 Classes/expectimax.py           |  43 ------
 Classes/game.py                 |  50 -------
 Classes/main.py                 |  65 ---------
 Classes/maze.py                 |  64 ---------
 Classes/minmax.py               |  93 -------------
 Classes/player.py               |  22 ----
 Classes/playerController.py     | 223 -------------------------------
 Classes/testAStar.py            |  68 ----------
 Classes/testBFS.py              | 109 ---------------
 Classes/testButton.py           |  56 --------
 Classes/testDFS.py              |  80 -----------
 Classes/testDijkstra.py         |  79 -----------
 Classes/testEnemy.py            |  46 -------
 Classes/testExpectimax.py       |  69 ----------
 Classes/testGame.py             |  60 ---------
 Classes/testMaze.py             |  92 -------------
 Classes/testMinMax.py           | 112 ----------------
 Classes/testPlayer.py           |  18 ---
 Classes/testPlayerController.py | 226 --------------------------------
 Images/blank.png                | Bin 239 -> 0 bytes
 Images/button_easy.png          | Bin 2072 -> 0 bytes
 Images/button_hard.png          | Bin 1757 -> 0 bytes
 Images/button_normal.png        | Bin 2395 -> 0 bytes
 Images/enemy.png                | Bin 232 -> 0 bytes
 Images/goal.png                 | Bin 238 -> 0 bytes
 Images/wall.png                 | Bin 144 -> 0 bytes
 33 files changed, 2101 deletions(-)
 delete mode 100644 Classes/aStar.py
 delete mode 100644 Classes/bfs.py
 delete mode 100644 Classes/button.py
 delete mode 100644 Classes/config.py
 delete mode 100644 Classes/dfs.py
 delete mode 100644 Classes/dijkstra.py
 delete mode 100644 Classes/enemy.py
 delete mode 100644 Classes/expectimax.py
 delete mode 100644 Classes/game.py
 delete mode 100644 Classes/main.py
 delete mode 100644 Classes/maze.py
 delete mode 100644 Classes/minmax.py
 delete mode 100644 Classes/player.py
 delete mode 100644 Classes/playerController.py
 delete mode 100644 Classes/testAStar.py
 delete mode 100644 Classes/testBFS.py
 delete mode 100644 Classes/testButton.py
 delete mode 100644 Classes/testDFS.py
 delete mode 100644 Classes/testDijkstra.py
 delete mode 100644 Classes/testEnemy.py
 delete mode 100644 Classes/testExpectimax.py
 delete mode 100644 Classes/testGame.py
 delete mode 100644 Classes/testMaze.py
 delete mode 100644 Classes/testMinMax.py
 delete mode 100644 Classes/testPlayer.py
 delete mode 100644 Classes/testPlayerController.py
 delete mode 100644 Images/blank.png
 delete mode 100644 Images/button_easy.png
 delete mode 100644 Images/button_hard.png
 delete mode 100644 Images/button_normal.png
 delete mode 100644 Images/enemy.png
 delete mode 100644 Images/goal.png
 delete mode 100644 Images/wall.png

diff --git a/Classes/aStar.py b/Classes/aStar.py
deleted file mode 100644
index 091306c..0000000
--- a/Classes/aStar.py
+++ /dev/null
@@ -1,100 +0,0 @@
-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
diff --git a/Classes/bfs.py b/Classes/bfs.py
deleted file mode 100644
index 8e1753e..0000000
--- a/Classes/bfs.py
+++ /dev/null
@@ -1,90 +0,0 @@
-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))
diff --git a/Classes/button.py b/Classes/button.py
deleted file mode 100644
index d26239e..0000000
--- a/Classes/button.py
+++ /dev/null
@@ -1,63 +0,0 @@
-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()
-
diff --git a/Classes/config.py b/Classes/config.py
deleted file mode 100644
index 25fad33..0000000
--- a/Classes/config.py
+++ /dev/null
@@ -1,75 +0,0 @@
-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)
-
diff --git a/Classes/dfs.py b/Classes/dfs.py
deleted file mode 100644
index b01cdd3..0000000
--- a/Classes/dfs.py
+++ /dev/null
@@ -1,70 +0,0 @@
-
-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))
diff --git a/Classes/dijkstra.py b/Classes/dijkstra.py
deleted file mode 100644
index 3d29284..0000000
--- a/Classes/dijkstra.py
+++ /dev/null
@@ -1,78 +0,0 @@
-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
diff --git a/Classes/enemy.py b/Classes/enemy.py
deleted file mode 100644
index 86df1c2..0000000
--- a/Classes/enemy.py
+++ /dev/null
@@ -1,50 +0,0 @@
-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))
diff --git a/Classes/expectimax.py b/Classes/expectimax.py
deleted file mode 100644
index 3efea7a..0000000
--- a/Classes/expectimax.py
+++ /dev/null
@@ -1,43 +0,0 @@
-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
diff --git a/Classes/game.py b/Classes/game.py
deleted file mode 100644
index 469b138..0000000
--- a/Classes/game.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-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
diff --git a/Classes/main.py b/Classes/main.py
deleted file mode 100644
index 8f69e29..0000000
--- a/Classes/main.py
+++ /dev/null
@@ -1,65 +0,0 @@
-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()
diff --git a/Classes/maze.py b/Classes/maze.py
deleted file mode 100644
index 8dc1cab..0000000
--- a/Classes/maze.py
+++ /dev/null
@@ -1,64 +0,0 @@
-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))
diff --git a/Classes/minmax.py b/Classes/minmax.py
deleted file mode 100644
index cb745b9..0000000
--- a/Classes/minmax.py
+++ /dev/null
@@ -1,93 +0,0 @@
-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
diff --git a/Classes/player.py b/Classes/player.py
deleted file mode 100644
index 4a4840a..0000000
--- a/Classes/player.py
+++ /dev/null
@@ -1,22 +0,0 @@
-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))
diff --git a/Classes/playerController.py b/Classes/playerController.py
deleted file mode 100644
index c591ce2..0000000
--- a/Classes/playerController.py
+++ /dev/null
@@ -1,223 +0,0 @@
-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
diff --git a/Classes/testAStar.py b/Classes/testAStar.py
deleted file mode 100644
index 1d98f72..0000000
--- a/Classes/testAStar.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import unittest
-from aStar import AStar
-
-
-class TestAStar(unittest.TestCase):
-    def setUp(self):
-        self.maze = [
-            [1, 0, 0, 0],
-            [0, 1, 1, 0],
-            [0, 0, 0, 1],
-            [1, 0, 0, 0]
-        ]
-        self.a_star_solver = AStar(self.maze)
-
-    def test_heuristic1(self):
-        current_position = (1, 1)
-        visited_corners = set()
-        heuristic_result = self.a_star_solver.heuristic1(current_position, self.a_star_solver.corners, visited_corners)
-        self.assertEqual(heuristic_result, 3)
-
-        current_position = (2, 2)
-        visited_corners = set()
-        heuristic_result = self.a_star_solver.heuristic1(current_position, self.a_star_solver.corners, visited_corners)
-        self.assertEqual(heuristic_result, ...)
-
-    def test_heuristic2(self):
-        current_position = (1, 1)
-        visited_corners = set()
-        heuristic_result = self.a_star_solver.heuristic2(current_position, self.a_star_solver.corners, visited_corners)
-        self.assertEqual(heuristic_result, ...)
-
-        current_position = (2, 2)
-        visited_corners = set()
-        heuristic_result = self.a_star_solver.heuristic2(current_position, self.a_star_solver.corners, visited_corners)
-        self.assertEqual(heuristic_result, ...)
-
-    def test_all_corners_visited(self):
-        corners = {(1, 1), (2, 2), (3, 3)}
-        visited_corners = {(1, 1), (2, 2)}
-        result = self.a_star_solver.all_corners_visited(corners, visited_corners)
-        self.assertFalse(result)
-
-    def test_find_goal_position(self):
-        maze_with_goal = ...
-        goal_position = self.a_star_solver.find_goal_position(maze_with_goal)
-        self.assertIsNotNone(goal_position)
-
-        maze_without_goal = ...
-        goal_position = self.a_star_solver.find_goal_position(maze_without_goal)
-        self.assertIsNone(goal_position)
-
-    def test_find_shortest_path_with_valid_heuristic(self):
-        def valid_heuristic(current, corners, visited_corners):
-            return 0
-
-        path = self.a_star_solver.find_shortest_path(valid_heuristic)
-        self.assertIsNotNone(path)
-
-    def test_find_shortest_path_with_invalid_heuristic(self):
-        def invalid_heuristic(current, corners, visited_corners):
-            return None
-
-        path = self.a_star_solver.find_shortest_path(invalid_heuristic)
-        self.assertIsNone(path)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/Classes/testBFS.py b/Classes/testBFS.py
deleted file mode 100644
index 3d5f40a..0000000
--- a/Classes/testBFS.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import unittest
-from collections import deque
-from bfs import BFS
-
-
-class TestBFS(unittest.TestCase):
-    def setUp(self):
-        self.bfs_solver = BFS()
-
-    def test_init(self):
-        bfs = BFS()
-        self.assertEqual(len(bfs.visited), 0)
-        self.assertIsInstance(bfs.queue, deque)
-        self.assertEqual(len(bfs.parent), 0)
-        self.assertEqual(len(bfs.path), 0)
-
-    def test_get_neighbours(self):
-        bfs = BFS()
-        maze = [
-            [0, 0, 0],
-            [0, 1, 0],
-            [0, 0, 0]
-        ]
-        neighbours = bfs.get_neighbours(maze, 1, 1)
-        expected_neighbours = [(0, 1), (1, 0), (1, 2), (2, 1)]
-        self.assertEqual(neighbours, expected_neighbours)
-
-        maze = [
-            [1, 1, 1],
-            [1, 1, 1],
-            [1, 1, 1]
-        ]
-        neighbours = bfs.get_neighbours(maze, 1, 1)
-        self.assertEqual(neighbours, [])
-
-    def test_bfs_path_found(self):
-        maze = [
-            [0, 0, 0],
-            [1, 1, 0],
-            [0, 0, 0]
-        ]
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertTrue(self.bfs_solver.bfs(maze, start, goal))
-
-    def test_bfs_no_path(self):
-        maze = [
-            [0, 1, 0],
-            [1, 1, 0],
-            [0, 1, 0]
-        ]
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
-
-    def test_bfs_empty_maze(self):
-        maze = []
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
-
-    def test_bfs_unreachable_goal(self):
-        maze = [
-            [0, 0, 0],
-            [1, 1, 0],
-            [0, 0, 1]
-        ]
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
-
-    def test_bfs_unreachable_start(self):
-        maze = [
-            [1, 0, 0],
-            [1, 1, 0],
-            [0, 0, 0]
-        ]
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
-
-    def test_bfs_single_cell_maze(self):
-        maze = [[0]]
-        start = (0, 0)
-        goal = (0, 0)
-        self.assertTrue(self.bfs_solver.bfs(maze, start, goal))
-
-    def test_construct_path(self):
-        start = (0, 0)
-        goal = (2, 2)
-        parent = {
-            (1, 1): (0, 1),
-            (1, 2): (1, 1),
-            (2, 2): (1, 2),
-            (0, 1): (0, 0)
-        }
-        self.bfs_solver.parent = parent
-        self.bfs_solver.construct_path(start, goal)
-        expected_path = [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)]
-        self.assertEqual(self.bfs_solver.path, expected_path)
-
-    def test_get_path(self):
-        self.bfs_solver.path = [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)]
-        expected_path = [(2, 2), (1, 2), (1, 1), (0, 1), (0, 0)]
-        self.assertEqual(self.bfs_solver.get_path(), expected_path)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testButton.py b/Classes/testButton.py
deleted file mode 100644
index c191dbb..0000000
--- a/Classes/testButton.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import unittest
-from unittest.mock import Mock
-from button import Button
-
-
-class TestButton(unittest.TestCase):
-    def setUp(self):
-        pygame = Mock()
-        self.mock_screen = pygame.display.set_mode.return_value
-        self.mock_player_controller = Mock()
-        self.mock_maze = Mock()
-        self.mock_image = Mock()
-        self.mock_image.get_width.return_value = 100
-        self.mock_image.get_height.return_value = 50
-        self.button = Button(self.mock_image, 100, 100, 1, None)
-
-    def test_draw(self):
-        mock_image = Mock()
-        button = Button(mock_image, 100, 100, 1, Mock())
-
-        button.draw()
-
-        expected_calls = [
-            ((mock_image, (100, 100)),),
-        ]
-        self.assertEqual(self.mock_screen.blit.call_args_list, expected_calls)
-
-    def test_handle_mouse_click_when_clicked(self):
-        button = Button(Mock(), 100, 100, 1, self.mock_player_controller)
-
-        button.handle_mouse_click((110, 110), self.mock_maze)
-
-        self.mock_maze.set_level.assert_called_once_with(1)
-        self.mock_player_controller.reset_player_position.assert_called_once()
-        self.assertTrue(button.is_clicked)
-
-    def test_handle_mouse_click_when_not_clicked(self):
-        button = Button(Mock(), 100, 100, 1, self.mock_player_controller)
-
-        button.handle_mouse_click((90, 90), self.mock_maze)
-
-        self.assertFalse(self.mock_maze.set_level.called)
-        self.assertFalse(self.mock_player_controller.reset_player_position.called)
-        self.assertFalse(button.is_clicked)
-
-    def test_check_click_within_boundaries(self):
-        result = self.button.check_click((150, 125))
-        self.assertTrue(result)
-
-    def test_check_click_outside_boundaries(self):
-        result = self.button.check_click((50, 75))
-        self.assertFalse(result)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testDFS.py b/Classes/testDFS.py
deleted file mode 100644
index 53afc4f..0000000
--- a/Classes/testDFS.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import unittest
-from dfs import DFS
-
-
-class TestDFS(unittest.TestCase):
-    def test_init(self):
-        dfs = DFS()
-        self.assertEqual(dfs.visited, set())
-        self.assertEqual(dfs.path, [])
-
-    def test_get_neighbours(self):
-        maze = [
-            [0, 0, 0, 1],
-            [1, 1, 0, 0],
-            [0, 1, 0, 1],
-            [0, 0, 0, 0]
-        ]
-        dfs = DFS()
-
-        self.assertEqual(dfs.get_neighbours(maze, 0, 0), [(0, 1), (1, 0)])
-        self.assertEqual(dfs.get_neighbours(maze, 1, 1), [(1, 2), (2, 1)])
-        self.assertEqual(dfs.get_neighbours(maze, 2, 2), [(2, 1), (3, 2)])
-
-    def test_dfs(self):
-        maze = [
-            [0, 0, 0, 1],
-            [1, 1, 0, 0],
-            [0, 1, 0, 1],
-            [0, 0, 0, 0]
-        ]
-        dfs = DFS()
-
-        start = (0, 0)
-        goal = (3, 3)
-        self.assertTrue(dfs.dfs(maze, start, goal))
-
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertFalse(dfs.dfs(maze, start, goal))
-
-    def test__dfs(self):
-        maze = [
-            [0, 0, 0, 1],
-            [1, 1, 0, 0],
-            [0, 1, 0, 1],
-            [0, 0, 0, 0]
-        ]
-        dfs = DFS()
-
-        start = (0, 0)
-        goal = (3, 3)
-        self.assertTrue(dfs._dfs(maze, start, goal))
-
-        start = (0, 0)
-        goal = (2, 2)
-        self.assertFalse(dfs._dfs(maze, start, goal))
-
-    def test_get_path(self):
-        maze = [
-            [0, 0, 0, 1],
-            [1, 1, 0, 0],
-            [0, 1, 0, 1],
-            [0, 0, 0, 0]
-        ]
-        dfs = DFS()
-
-        start = (0, 0)
-        goal = (3, 3)
-        dfs._dfs(maze, start, goal)
-        expected_path = [(0, 0), (0, 1), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3)]
-        self.assertEqual(dfs.get_path(), expected_path)
-
-        start = (0, 0)
-        goal = (2, 2)
-        dfs._dfs(maze, start, goal)
-        self.assertEqual(dfs.get_path(), [])
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testDijkstra.py b/Classes/testDijkstra.py
deleted file mode 100644
index b6e6804..0000000
--- a/Classes/testDijkstra.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import unittest
-from dijkstra import Dijkstra
-
-
-class TestDijkstra(unittest.TestCase):
-    def setUp(self):
-        self.dijkstra_solver = Dijkstra(None)
-
-    def test_calculate_cost_empty(self):
-        cell_type = 0
-        cost = self.dijkstra_solver.calculate_cost(cell_type)
-        self.assertEqual(cost, 1)
-
-    def test_calculate_cost_wall(self):
-        cell_type = 1
-        cost = self.dijkstra_solver.calculate_cost(cell_type)
-        self.assertEqual(cost, float('inf'))
-
-    def test_calculate_cost_goal(self):
-        cell_type = 2
-        cost = self.dijkstra_solver.calculate_cost(cell_type)
-        self.assertEqual(cost, 100)
-
-    def test_calculate_cost_start(self):
-        cell_type = 3
-        cost = self.dijkstra_solver.calculate_cost(cell_type)
-        self.assertEqual(cost, 1)
-
-    def test_find_shortest_path_simple(self):
-
-        start_position = (0, 0)
-        goal_position = (3, 3)
-        path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
-        expected_path = [(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (3, 2), (3, 3)]
-        self.assertEqual(path, expected_path)
-
-    def test_find_shortest_path_blocked_goal(self):
-
-        start_position = (0, 0)
-        goal_position = (3, 3)
-        path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
-        expected_path = []
-        self.assertEqual(path, expected_path)
-
-    def test_find_shortest_path_complex(self):
-
-        start_position = (0, 0)
-        goal_position = (4, 4)
-        path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
-        expected_path = [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (3, 3), (4, 3), (4, 4)]
-        self.assertEqual(path, expected_path)
-
-    def test_get_neighbors_middle(self):
-        maze = Dijkstra([
-            [0, 0, 0, 1],
-            [1, 1, 0, 0],
-            [0, 1, 0, 1],
-            [0, 0, 0, 0]
-        ])
-        cell = (1, 1)
-        neighbors = maze.get_neighbors(cell)
-        expected_neighbors = [(0, 1), (2, 1), (1, 0), (1, 2)]
-        self.assertEqual(neighbors, expected_neighbors)
-
-    def test_get_neighbors_corner(self):
-        maze = Dijkstra([
-            [0, 0, 0, 1],
-            [1, 1, 0, 0],
-            [0, 1, 0, 1],
-            [0, 0, 0, 0]
-        ])
-        cell = (0, 0)
-        neighbors = maze.get_neighbors(cell)
-        expected_neighbors = [(1, 0), (0, 1)]
-        self.assertEqual(neighbors, expected_neighbors)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testEnemy.py b/Classes/testEnemy.py
deleted file mode 100644
index 048d0ff..0000000
--- a/Classes/testEnemy.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import unittest
-from unittest.mock import MagicMock, patch
-from enemy import Enemy
-from config import TILE_SIZE
-
-
-class TestEnemy(unittest.TestCase):
-    @patch('enemy.random')
-    def test_init(self, mock_random):
-        mock_maze = MagicMock()
-
-        mock_random.randint.return_value = 2
-        mock_maze.num_rows.return_value = 5
-        mock_maze.num_columns.return_value = 6
-        mock_maze.__getitem__.return_value = 0
-
-        enemy = Enemy(mock_maze)
-
-        self.assertEqual(enemy.x, 2 * 32)
-        self.assertEqual(enemy.y, 0)
-
-    @patch('enemy.random')
-    def test_reset_position(self, mock_random):
-        mock_maze = MagicMock()
-
-        mock_random.randint.return_value = 2
-        mock_maze.num_rows.return_value = 5
-        mock_maze.num_columns.return_value = 6
-        mock_maze.__getitem__.return_value = 0
-
-        enemy = Enemy(mock_maze)
-        enemy.reset_position()
-
-        self.assertEqual(enemy.x, 2 * 32)
-        self.assertEqual(enemy.y, 0)
-
-    def test_draw(self):
-        mock_screen = MagicMock()
-
-        enemy = Enemy(None)
-        enemy.draw(mock_screen)
-        mock_screen.assert_called_once_with('blue', (enemy.x, enemy.y, enemy.TILE_SIZE, enemy.TILE_SIZE))
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testExpectimax.py b/Classes/testExpectimax.py
deleted file mode 100644
index d456122..0000000
--- a/Classes/testExpectimax.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import unittest
-from expectimax import Expectimax
-
-
-class TestExpectimax(unittest.TestCase):
-
-    def test_valid_scenario(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        expectimax_solver = Expectimax(maze)
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = 3
-        maximizing_player = True
-        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
-        self.assertIsNotNone(result)
-
-    def test_invalid_scenario_negative_depth(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        expectimax_solver = Expectimax(maze)
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = -1
-        maximizing_player = True
-        with self.assertRaises(ValueError):
-            expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
-
-    def test_no_valid_moves(self):
-        maze = [[1, 1, 1],
-                [1, 1, 1],
-                [1, 1, 1]]
-        expectimax_solver = Expectimax(maze)
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = 3
-        maximizing_player = True
-        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
-        self.assertEqual(result, 0)
-
-    def test_game_over(self):
-        maze = [[0, 0, 0],
-                [0, 0, 0],
-                [0, 0, 0]]
-        expectimax_solver = Expectimax(maze)
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = 3
-        maximizing_player = True
-        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
-        self.assertEqual(result, 0)
-
-    def test_max_depth(self):
-        maze = [[0, 0, 0],
-                [0, 0, 0],
-                [0, 0, 0]]
-        expectimax_solver = Expectimax(maze)
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = 10
-        maximizing_player = True
-        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
-        self.assertIsNotNone(result)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/Classes/testGame.py b/Classes/testGame.py
deleted file mode 100644
index dbbfc54..0000000
--- a/Classes/testGame.py
+++ /dev/null
@@ -1,60 +0,0 @@
-import unittest
-from game import Game
-
-
-class TestGame(unittest.TestCase):
-
-    def setUp(self):
-        self.maze = [
-            [1, 1, 1, 1, 1],
-            [1, 0, 0, 0, 1],
-            [1, 0, 1, 0, 1],
-            [1, 0, 0, 0, 1],
-            [1, 1, 1, 1, 1]
-        ]
-        self.game = Game(self.maze)
-
-    def test_init(self):
-        maze = [
-            [1, 1, 1, 1, 1],
-            [1, 0, 0, 0, 1],
-            [1, 0, 1, 0, 1],
-            [1, 0, 0, 0, 1],
-            [1, 1, 1, 1, 1]
-        ]
-
-        game = Game(maze)
-
-        self.assertEqual(game.maze, maze)
-        self.assertEqual(game.player_position, (1, 1))
-        self.assertEqual(game.enemy_positions, [(2, 2)])
-
-    def test_get_player_position(self):
-        maze = [
-            [1, 1, 1, 1, 1],
-            [1, 0, 0, 0, 1],
-            [1, 0, 1, 0, 1],
-            [1, 0, 0, 0, 1],
-            [1, 1, 1, 1, 1]
-        ]
-
-        game = Game(maze)
-        player_position = game.get_player_position()
-        self.assertEqual(player_position, (1, 1))
-
-    def test_get_enemy_positions(self):
-        enemy_positions = self.game.get_enemy_positions()
-        self.assertEqual(enemy_positions, [(2, 2)])
-
-    def test_update_positions(self):
-        self.game.update_positions()
-        self.assertEqual(self.game.player_position, (1, 1))
-        self.assertEqual(self.game.enemy_positions, [(2, 2)])
-
-    def test_check_game_over(self):
-        game_over = self.game.check_game_over()
-        self.assertFalse(game_over)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testMaze.py b/Classes/testMaze.py
deleted file mode 100644
index da1c135..0000000
--- a/Classes/testMaze.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import unittest
-from unittest.mock import Mock
-
-import pygame
-
-from maze import Maze
-
-
-class TestMaze(unittest.TestCase):
-    def test_num_rows(self):
-        initial_level = [
-            [0, 0, 0],
-            [0, 0, 0],
-            [0, 0, 0]
-        ]
-        maze = Maze(initial_level)
-        self.assertEqual(maze.num_rows(), 3)
-
-    def test_num_columns(self):
-        initial_level = [
-            [0, 0, 0, 0],
-            [0, 0, 0, 0],
-            [0, 0, 0, 0]
-        ]
-        maze = Maze(initial_level)
-        self.assertEqual(maze.num_columns(), 4)
-
-    def test_getitem(self):
-        initial_level = [
-            [0, 0, 0],
-            [0, 0, 0],
-            [0, 0, 0]
-        ]
-        maze = Maze(initial_level)
-        self.assertEqual(maze[0], [0, 0, 0])
-        self.assertEqual(maze[1], [0, 0, 0])
-        self.assertEqual(maze[2], [0, 0, 0])
-
-    def test_set_level(self):
-        initial_level = [
-            [0, 0, 0],
-            [0, 0, 0],
-            [0, 0, 0]
-        ]
-        maze = Maze(initial_level)
-        new_level = [
-            [1, 1, 1],
-            [1, 1, 1],
-            [1, 1, 1]
-        ]
-        maze.set_level(new_level)
-        self.assertEqual(maze.current_level, new_level)
-
-    def test_update_screen_size(self):
-        initial_level = [
-            [0, 0, 0],
-            [0, 0, 0],
-            [0, 0, 0]
-        ]
-        maze = Maze(initial_level)
-        pygame.display.set_mode((800, 600))
-        maze.update_screen_size()
-        self.assertEqual(pygame.display.get_surface().get_size(), (3 * 32, 3 * 32))
-
-    def test_draw(self):
-        initial_level = [
-            [0, 0, 0],
-            [0, 1, 0],
-            [0, 0, 0]
-        ]
-        maze = Maze(initial_level)
-        tiles = {0: Mock(), 1: Mock()}
-        maze.tiles = tiles
-
-        maze.draw()
-
-        expected_calls = [
-            ((tiles[0], (0, 0)),),
-            ((tiles[0], (32, 0)),),
-            ((tiles[0], (64, 0)),),
-            ((tiles[0], (0, 32)),),
-            ((tiles[1], (32, 32)),),
-            ((tiles[0], (64, 32)),),
-            ((tiles[0], (0, 64)),),
-            ((tiles[0], (32, 64)),),
-            ((tiles[0], (64, 64)),),
-        ]
-        self.assertEqual(self.mock_screen.blit.call_args_list, expected_calls)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testMinMax.py b/Classes/testMinMax.py
deleted file mode 100644
index a3ae88d..0000000
--- a/Classes/testMinMax.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import unittest
-from minmax import MinMax
-
-
-class TestMinMax(unittest.TestCase):
-
-    def test_get_valid_moves_middle(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        minmax = MinMax(maze)
-        player_position = (1, 1)
-        valid_moves = minmax.get_valid_moves(player_position)
-        expected_moves = [(0, 1), (2, 1), (1, 0), (1, 2)]
-        self.assertEqual(valid_moves, expected_moves)
-
-    def test_get_valid_moves_corner(self):
-        maze = [[0, 1, 0],
-                [1, 1, 0],
-                [0, 0, 0]]
-        minmax = MinMax(maze)
-        player_position = (0, 0)
-        valid_moves = minmax.get_valid_moves(player_position)
-        expected_moves = [(1, 0), (0, 1)]
-        self.assertEqual(valid_moves, expected_moves)
-
-    def test_valid_move(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        minmax = MinMax(maze)
-        valid_position = (1, 0)
-        self.assertTrue(minmax.is_valid_move(valid_position))
-
-    def test_invalid_move_wall(self):
-        maze = [[0, 1, 0],
-                [1, 1, 0],
-                [0, 0, 0]]
-        minmax = MinMax(maze)
-        wall_position = (0, 1)
-        self.assertFalse(minmax.is_valid_move(wall_position))
-
-    def test_invalid_move_outside(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        minmax = MinMax(maze)
-        outside_position = (3, 0)
-        self.assertFalse(minmax.is_valid_move(outside_position))
-
-    def test_minmax(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        minmax = MinMax(maze)
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = 3
-        alpha = float('-inf')
-        beta = float('inf')
-        maximizing_player = True
-        result = minmax.minmax(player_position, enemy_position, depth, alpha, beta, maximizing_player)
-        self.assertIsInstance(result, int)
-
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        player_position = (0, 0)
-        enemy_position = (2, 2)
-        depth = 1
-
-        result = minmax.minmax(player_position, enemy_position, depth, float('-inf'), float('inf'), maximizing_player)
-        print("Result:", result)
-
-    def test_evaluate(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        minmax_object = MinMax(maze)
-        print("Testing the evaluate method...")
-
-        player_position = (1, 1)
-        enemy_position = (1, 1)
-        evaluation_result = minmax_object.evaluate(player_position, enemy_position)
-        print("Evaluation result when player and enemy are at the same position:", evaluation_result)
-
-        player_position = (2, 2)
-        enemy_position = (1, 1)
-        evaluation_result = minmax_object.evaluate(player_position, enemy_position)
-        print("Evaluation result when player is closer to the goal than the enemy:", evaluation_result)
-
-        player_position = (1, 1)
-        enemy_position = (2, 2)
-        evaluation_result = minmax_object.evaluate(player_position, enemy_position)
-        print("Evaluation result when enemy is closer to the goal than the player:", evaluation_result)
-
-    def test_game_over(self):
-        maze = [[0, 0, 0],
-                [0, 1, 0],
-                [0, 0, 0]]
-        minmax_object = MinMax(maze)
-        print("Testing the game_over method...")
-
-        game_over_result = minmax_object.game_over()
-        print("Game over result when the game is not over:", game_over_result)
-
-        game_over_result = minmax_object.game_over()
-        print("Game over result when the game is over:", game_over_result)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/Classes/testPlayer.py b/Classes/testPlayer.py
deleted file mode 100644
index a3386cc..0000000
--- a/Classes/testPlayer.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import unittest
-from unittest.mock import Mock, patch
-from config import TILE_SIZE
-from player import Player
-
-
-class TestPlayer(unittest.TestCase):
-    @patch('pygame.draw.rect')
-    def test_draw(self, mock_rect):
-        # Mock the screen surface
-        screen_surface_mock = Mock()
-        player = Player(10, 20)
-        player.draw(screen_surface_mock)
-        mock_rect.assert_called_once_with(screen_surface_mock, 'white', (10, 20, TILE_SIZE, TILE_SIZE))
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Classes/testPlayerController.py b/Classes/testPlayerController.py
deleted file mode 100644
index 591c217..0000000
--- a/Classes/testPlayerController.py
+++ /dev/null
@@ -1,226 +0,0 @@
-import unittest
-from unittest.mock import Mock
-
-import pygame
-
-from config import TILE_SIZE
-from player import Player
-from playerController import PlayerController  # Import the PlayerController class
-
-
-class TestPlayerController(unittest.TestCase):
-    def setUp(self):
-        self.player_mock = Mock()
-        self.maze_mock = Mock()
-        self.player_controller = PlayerController(self.player_mock, self.maze_mock)
-
-    def test_move_player_up(self):
-        direction = {pygame.K_UP: True, pygame.K_DOWN: False, pygame.K_LEFT: False, pygame.K_RIGHT: False}
-        self.player_controller.move_player(direction)
-        self.assertEqual(self.player_mock.y, self.player_mock.y - TILE_SIZE)
-
-    def test_move_player_down(self):
-        direction = {pygame.K_UP: False, pygame.K_DOWN: True, pygame.K_LEFT: False, pygame.K_RIGHT: False}
-        self.player_controller.move_player(direction)
-        self.assertEqual(self.player_mock.y, self.player_mock.y + TILE_SIZE)
-
-    def test_move_player_left(self):
-        direction = {pygame.K_UP: False, pygame.K_DOWN: False, pygame.K_LEFT: True, pygame.K_RIGHT: False}
-        self.player_controller.move_player(direction)
-        self.assertEqual(self.player_mock.x, self.player_mock.x - TILE_SIZE)
-
-    def test_move_player_right(self):
-        direction = {pygame.K_UP: False, pygame.K_DOWN: False, pygame.K_LEFT: False, pygame.K_RIGHT: True}
-        self.player_controller.move_player(direction)
-        self.assertEqual(self.player_mock.x, self.player_mock.x + TILE_SIZE)
-
-    def test_valid_position_within_maze(self):
-        position = (1, 1)
-        self.assertTrue(self.player_controller.is_valid_position(position))
-
-    def test_position_outside_maze_bounds(self):
-        position = (-1, 1)
-        self.assertFalse(self.player_controller.is_valid_position(position))
-        position = (10, 5)
-        self.assertFalse(self.player_controller.is_valid_position(position))
-        position = (1, 10)
-        self.assertFalse(self.player_controller.is_valid_position(position))
-
-    def test_position_collides_with_wall(self):
-        self.player_controller.maze = Mock(current_level=[[0, 0, 0],
-                                                          [0, 1, 0],
-                                                          [0, 0, 0]])
-        position = (1, 1)
-        self.assertFalse(self.player_controller.is_valid_position(position))
-        position = (0, 1)
-        self.assertTrue(self.player_controller.is_valid_position(position))
-
-    def test_collision_with_wall(self):
-        self.player_controller.maze = [[0, 0, 0],
-                                       [0, 1, 0],
-                                       [0, 0, 0]]
-        x, y = 50, 50
-        self.assertTrue(self.player_controller.check_collision(x, y))
-
-    def test_no_collision_with_wall(self):
-        self.player_controller.maze = [[0, 0, 0],
-                                       [0, 0, 0],
-                                       [0, 0, 0]]
-        x, y = 50, 50
-        self.assertFalse(self.player_controller.check_collision(x, y))
-
-    def test_reset_player_position(self):
-        initial_x, initial_y = 100, 100
-        self.player_controller.player = Player(initial_x, initial_y)
-        self.player_controller.reset_player_position()
-        self.assertEqual(self.player_controller.player.x, TILE_SIZE)
-        self.assertEqual(self.player_controller.player.y, TILE_SIZE)
-
-    def test_set_dfs_solver(self):
-        dfs_solver_mock = Mock()
-        self.player_controller.set_dfs_solver(dfs_solver_mock)
-        self.assertEqual(self.player_controller.dfs_solver, dfs_solver_mock)
-
-    def test_set_bfs_solver(self):
-        bfs_solver_mock = Mock()
-        self.player_controller.set_bfs_solver(bfs_solver_mock)
-        self.assertEqual(self.player_controller.bfs_solver, bfs_solver_mock)
-
-    def test_set_dijkstra_solver(self):
-        dijkstra_solver_mock = Mock()
-        self.player_controller.set_dijkstra_solver(dijkstra_solver_mock)
-        self.assertEqual(self.player_controller.dijkstra_solver, dijkstra_solver_mock)
-
-    def test_set_astar_solver(self):
-        astar_solver_mock = Mock()
-        self.player_controller.set_astar_solver(astar_solver_mock)
-        self.assertEqual(self.player_controller.astar_solver, astar_solver_mock)
-
-    def test_move_to_goal_dfs(self):
-        dfs_solver_mock = Mock()
-        dfs_solver_mock.dfs.return_value = True
-        dfs_solver_mock.get_path.return_value = [(1, 1), (1, 2), (1, 3)]
-
-        self.player_controller.set_dfs_solver(dfs_solver_mock)
-        self.player_controller.maze = Mock()
-        self.player_controller.maze.current_level = [
-            [0, 0, 0, 0],
-            [0, 0, 3, 0],
-            [0, 0, 0, 0],
-        ]
-
-        self.player_controller.player.x = TILE_SIZE
-        self.player_controller.player.y = TILE_SIZE
-        self.player_controller.move_to_goal_dfs()
-
-        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
-        self.assertEqual(
-            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
-            expected_path)
-
-    def test_move_to_goal_bfs(self):
-        bfs_solver_mock = Mock()
-        bfs_solver_mock.bfs.return_value = True
-        bfs_solver_mock.get_path.return_value = [(1, 1), (1, 2), (1, 3)]
-
-        self.player_controller.set_bfs_solver(bfs_solver_mock)
-        self.player_controller.maze = Mock()
-        self.player_controller.maze.current_level = [
-            [0, 0, 0, 0],
-            [0, 0, 3, 0],
-            [0, 0, 0, 0],
-        ]
-
-        self.player_controller.player.x = TILE_SIZE
-        self.player_controller.player.y = TILE_SIZE
-        self.player_controller.move_to_goal_bfs()
-
-        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
-        self.assertEqual(
-            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
-            expected_path)
-
-    def test_move_to_goal_dijkstra(self):
-        dijkstra_solver_mock = Mock()
-        dijkstra_solver_mock.find_shortest_path.return_value = [(1, 1), (1, 2), (1, 3)]  # Simulate a path
-
-        self.player_controller.set_dijkstra_solver(dijkstra_solver_mock)
-        self.player_controller.maze = Mock()
-        self.player_controller.maze.current_level = [
-            [0, 0, 0, 0],
-            [0, 0, 3, 0],
-            [0, 0, 0, 0],
-        ]
-
-        self.player_controller.player.x = TILE_SIZE
-        self.player_controller.player.y = TILE_SIZE
-        self.player_controller.move_to_goal_dijkstra()
-
-        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
-        self.assertEqual(
-            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
-            expected_path)
-
-    def test_move_to_goal_astar(self):
-        astar_solver_mock = Mock()
-        astar_solver_mock.find_goal_position.return_value = (2, 2)  # Simulate a goal position
-        astar_solver_mock.find_shortest_path.return_value = [(1, 1), (1, 2), (1, 3)]  # Simulate a path
-
-        self.player_controller.set_astar_solver(astar_solver_mock)
-        self.player_controller.maze = Mock()
-        self.player_controller.maze.current_level = [
-            [0, 0, 0, 0],
-            [0, 0, 3, 0],
-            [0, 0, 0, 0],
-        ]
-
-        self.player_controller.player.x = TILE_SIZE
-        self.player_controller.player.y = TILE_SIZE
-        self.player_controller.move_to_goal_astar()
-
-        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
-        self.assertEqual(
-            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
-            expected_path)
-
-    def test_follow_path(self):
-        player_mock = Mock()
-        self.player_controller.player = player_mock
-        maze_mock = Mock()
-        self.player_controller.maze = maze_mock
-        path = [(0, 0), (0, 1), (0, 2)]
-        self.player_controller.follow_path(path)
-        expected_positions = [(0, 0), (0, 1), (0, 2)]
-        self.assertEqual([(args[0], args[1]) for args in player_mock.x.call_args_list], expected_positions)
-
-    def test_draw_path(self):
-        screen_mock = Mock()
-        self.player_controller.screen = screen_mock
-        path = [(0, 0), (0, 1), (1, 1)]
-        self.player_controller.draw_path(path)
-
-        expected_calls = [
-            ((screen_mock,), {'color': (0, 255, 0), 'rect': (0, 0, TILE_SIZE, TILE_SIZE)}),
-            ((screen_mock,), {'color': (0, 255, 0), 'rect': (TILE_SIZE, 0, TILE_SIZE, TILE_SIZE)}),
-            ((screen_mock,), {'color': (0, 255, 0), 'rect': (TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)})
-        ]
-
-        self.assertEqual(screen_mock.mock_calls,
-                         [unittest.mock.call.draw.rect(*args, **kwargs) for args, kwargs in expected_calls])
-
-    def test_find_goal_position(self):
-        mock_maze = Mock()
-        mock_maze.current_level = [
-            [0, 0, 0],
-            [0, 0, 0],
-            [0, 0, 3]
-        ]
-
-        self.player_controller.maze = mock_maze
-        goal_position = self.player_controller.find_goal_position()
-        expected_goal_position = (2, 2)
-        self.assertEqual(goal_position, expected_goal_position)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Images/blank.png b/Images/blank.png
deleted file mode 100644
index 50c9e2a0f81462186450944aba69ee033b822c4a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 239
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1quc!4^*!$B>G+w-*lb9xz~Nb};|_e$KOlVg-|q
z-oL-?_p;IzVeA2G`4&XUI&8IS$SP&H`if!IRmPCj%v6)e`pp=yg|YrWN62!Zdl)=j
L{an^LB{Ts5@cL7P

diff --git a/Images/button_easy.png b/Images/button_easy.png
deleted file mode 100644
index 4601185db715287698992947896da6796e7cf000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

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

diff --git a/Images/button_hard.png b/Images/button_hard.png
deleted file mode 100644
index 88cf8b3ae18ed0ce2479f2873a0aa554bde73d4c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

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

diff --git a/Images/button_normal.png b/Images/button_normal.png
deleted file mode 100644
index 821d5a79d1c98f2a6680072c39c502a7c0bcef7d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

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

diff --git a/Images/enemy.png b/Images/enemy.png
deleted file mode 100644
index 4c40921e35a8caf9813b8644e7dcc76b55487a0b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 232
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1quc!CFrj$B>G+w-**NGAJ;#9Jv0s{MsS0g4T04
l@6EokG>Uft?NHEd_CRx{y4>_m6QDyFJYD@<);T3K0RXN7Nm2j+

diff --git a/Images/goal.png b/Images/goal.png
deleted file mode 100644
index 0a650e44fdceee5be61a68040c1b8838ae83716a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 238
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1quc!Ddev$B>G+w-*lb9xz~Nb};|_e$KOlVg;Ly
z+`oVK-X6ZIuNYQcWei!(ydsP}U@hN*C|QTCRt;ID4AheF%C!$RWvV;czR4fx90pHU
KKbLh*2~7aPcT$M}

diff --git a/Images/wall.png b/Images/wall.png
deleted file mode 100644
index eb06cd2a45741ab3b9b0d54f0fa47a7fd6493295..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 144
zcmeAS@N?(olHy`uVBq!ia0vp^4?&oN8Ax*GDtQ1Y<^Z1%*To+HGoO7)268<-T^vIy
t=Da=F2;?vvUhp$G{sBiQBZxx^_`rK?5d&j5fAnIIE>BlKmvv4FO#nnu8wdaZ

-- 
GitLab


From 8b063a30b7c63ab1b153194e8750dc89e24dc822 Mon Sep 17 00:00:00 2001
From: DannyAbdi <dannyabdi13@gmail.com>
Date: Thu, 4 Apr 2024 17:08:40 +0100
Subject: [PATCH 2/2] fixed test bugs

---
 aStar.py                | 100 ++++++++++++++++++
 bfs.py                  |  90 ++++++++++++++++
 blank.png               | Bin 0 -> 239 bytes
 button.py               |  63 +++++++++++
 button_easy.png         | Bin 0 -> 2072 bytes
 button_hard.png         | Bin 0 -> 1757 bytes
 button_normal.png       | Bin 0 -> 2395 bytes
 config.py               |  75 +++++++++++++
 dfs.py                  |  70 +++++++++++++
 dijkstra.py             |  78 ++++++++++++++
 enemy.png               | Bin 0 -> 232 bytes
 enemy.py                |  50 +++++++++
 expectimax.py           |  43 ++++++++
 game.py                 |  50 +++++++++
 goal.png                | Bin 0 -> 238 bytes
 main.py                 |  65 ++++++++++++
 maze.py                 |  64 ++++++++++++
 minmax.py               |  93 +++++++++++++++++
 player.py               |  22 ++++
 playerController.py     | 223 +++++++++++++++++++++++++++++++++++++++
 testAStar.py            |  68 ++++++++++++
 testBFS.py              | 109 +++++++++++++++++++
 testButton.py           |  56 ++++++++++
 testDFS.py              |  80 ++++++++++++++
 testDijkstra.py         |  79 ++++++++++++++
 testEnemy.py            |  46 ++++++++
 testExpectimax.py       |  69 ++++++++++++
 testGame.py             |  60 +++++++++++
 testMaze.py             |  92 ++++++++++++++++
 testMinMax.py           | 112 ++++++++++++++++++++
 testPlayer.py           |  18 ++++
 testPlayerController.py | 226 ++++++++++++++++++++++++++++++++++++++++
 wall.png                | Bin 0 -> 144 bytes
 33 files changed, 2101 insertions(+)
 create mode 100644 aStar.py
 create mode 100644 bfs.py
 create mode 100644 blank.png
 create mode 100644 button.py
 create mode 100644 button_easy.png
 create mode 100644 button_hard.png
 create mode 100644 button_normal.png
 create mode 100644 config.py
 create mode 100644 dfs.py
 create mode 100644 dijkstra.py
 create mode 100644 enemy.png
 create mode 100644 enemy.py
 create mode 100644 expectimax.py
 create mode 100644 game.py
 create mode 100644 goal.png
 create mode 100644 main.py
 create mode 100644 maze.py
 create mode 100644 minmax.py
 create mode 100644 player.py
 create mode 100644 playerController.py
 create mode 100644 testAStar.py
 create mode 100644 testBFS.py
 create mode 100644 testButton.py
 create mode 100644 testDFS.py
 create mode 100644 testDijkstra.py
 create mode 100644 testEnemy.py
 create mode 100644 testExpectimax.py
 create mode 100644 testGame.py
 create mode 100644 testMaze.py
 create mode 100644 testMinMax.py
 create mode 100644 testPlayer.py
 create mode 100644 testPlayerController.py
 create mode 100644 wall.png

diff --git a/aStar.py b/aStar.py
new file mode 100644
index 0000000..091306c
--- /dev/null
+++ b/aStar.py
@@ -0,0 +1,100 @@
+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
diff --git a/bfs.py b/bfs.py
new file mode 100644
index 0000000..8e1753e
--- /dev/null
+++ b/bfs.py
@@ -0,0 +1,90 @@
+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))
diff --git a/blank.png b/blank.png
new file mode 100644
index 0000000000000000000000000000000000000000..50c9e2a0f81462186450944aba69ee033b822c4a
GIT binary patch
literal 239
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1quc!4^*!$B>G+w-*lb9xz~Nb};|_e$KOlVg-|q
z-oL-?_p;IzVeA2G`4&XUI&8IS$SP&H`if!IRmPCj%v6)e`pp=yg|YrWN62!Zdl)=j
L{an^LB{Ts5@cL7P

literal 0
HcmV?d00001

diff --git a/button.py b/button.py
new file mode 100644
index 0000000..d26239e
--- /dev/null
+++ b/button.py
@@ -0,0 +1,63 @@
+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()
+
diff --git a/button_easy.png b/button_easy.png
new file mode 100644
index 0000000000000000000000000000000000000000..4601185db715287698992947896da6796e7cf000
GIT binary patch
literal 2072
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

literal 0
HcmV?d00001

diff --git a/button_hard.png b/button_hard.png
new file mode 100644
index 0000000000000000000000000000000000000000..88cf8b3ae18ed0ce2479f2873a0aa554bde73d4c
GIT binary patch
literal 1757
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

literal 0
HcmV?d00001

diff --git a/button_normal.png b/button_normal.png
new file mode 100644
index 0000000000000000000000000000000000000000..821d5a79d1c98f2a6680072c39c502a7c0bcef7d
GIT binary patch
literal 2395
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

literal 0
HcmV?d00001

diff --git a/config.py b/config.py
new file mode 100644
index 0000000..25fad33
--- /dev/null
+++ b/config.py
@@ -0,0 +1,75 @@
+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)
+
diff --git a/dfs.py b/dfs.py
new file mode 100644
index 0000000..b01cdd3
--- /dev/null
+++ b/dfs.py
@@ -0,0 +1,70 @@
+
+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))
diff --git a/dijkstra.py b/dijkstra.py
new file mode 100644
index 0000000..3d29284
--- /dev/null
+++ b/dijkstra.py
@@ -0,0 +1,78 @@
+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
diff --git a/enemy.png b/enemy.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c40921e35a8caf9813b8644e7dcc76b55487a0b
GIT binary patch
literal 232
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1quc!CFrj$B>G+w-**NGAJ;#9Jv0s{MsS0g4T04
l@6EokG>Uft?NHEd_CRx{y4>_m6QDyFJYD@<);T3K0RXN7Nm2j+

literal 0
HcmV?d00001

diff --git a/enemy.py b/enemy.py
new file mode 100644
index 0000000..86df1c2
--- /dev/null
+++ b/enemy.py
@@ -0,0 +1,50 @@
+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))
diff --git a/expectimax.py b/expectimax.py
new file mode 100644
index 0000000..3efea7a
--- /dev/null
+++ b/expectimax.py
@@ -0,0 +1,43 @@
+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
diff --git a/game.py b/game.py
new file mode 100644
index 0000000..469b138
--- /dev/null
+++ b/game.py
@@ -0,0 +1,50 @@
+"""
+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
diff --git a/goal.png b/goal.png
new file mode 100644
index 0000000000000000000000000000000000000000..0a650e44fdceee5be61a68040c1b8838ae83716a
GIT binary patch
literal 238
zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1quc!Ddev$B>G+w-*lb9xz~Nb};|_e$KOlVg;Ly
z+`oVK-X6ZIuNYQcWei!(ydsP}U@hN*C|QTCRt;ID4AheF%C!$RWvV;czR4fx90pHU
KKbLh*2~7aPcT$M}

literal 0
HcmV?d00001

diff --git a/main.py b/main.py
new file mode 100644
index 0000000..8f69e29
--- /dev/null
+++ b/main.py
@@ -0,0 +1,65 @@
+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()
diff --git a/maze.py b/maze.py
new file mode 100644
index 0000000..8dc1cab
--- /dev/null
+++ b/maze.py
@@ -0,0 +1,64 @@
+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))
diff --git a/minmax.py b/minmax.py
new file mode 100644
index 0000000..cb745b9
--- /dev/null
+++ b/minmax.py
@@ -0,0 +1,93 @@
+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
diff --git a/player.py b/player.py
new file mode 100644
index 0000000..4a4840a
--- /dev/null
+++ b/player.py
@@ -0,0 +1,22 @@
+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))
diff --git a/playerController.py b/playerController.py
new file mode 100644
index 0000000..c591ce2
--- /dev/null
+++ b/playerController.py
@@ -0,0 +1,223 @@
+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
diff --git a/testAStar.py b/testAStar.py
new file mode 100644
index 0000000..1d98f72
--- /dev/null
+++ b/testAStar.py
@@ -0,0 +1,68 @@
+import unittest
+from aStar import AStar
+
+
+class TestAStar(unittest.TestCase):
+    def setUp(self):
+        self.maze = [
+            [1, 0, 0, 0],
+            [0, 1, 1, 0],
+            [0, 0, 0, 1],
+            [1, 0, 0, 0]
+        ]
+        self.a_star_solver = AStar(self.maze)
+
+    def test_heuristic1(self):
+        current_position = (1, 1)
+        visited_corners = set()
+        heuristic_result = self.a_star_solver.heuristic1(current_position, self.a_star_solver.corners, visited_corners)
+        self.assertEqual(heuristic_result, 3)
+
+        current_position = (2, 2)
+        visited_corners = set()
+        heuristic_result = self.a_star_solver.heuristic1(current_position, self.a_star_solver.corners, visited_corners)
+        self.assertEqual(heuristic_result, ...)
+
+    def test_heuristic2(self):
+        current_position = (1, 1)
+        visited_corners = set()
+        heuristic_result = self.a_star_solver.heuristic2(current_position, self.a_star_solver.corners, visited_corners)
+        self.assertEqual(heuristic_result, ...)
+
+        current_position = (2, 2)
+        visited_corners = set()
+        heuristic_result = self.a_star_solver.heuristic2(current_position, self.a_star_solver.corners, visited_corners)
+        self.assertEqual(heuristic_result, ...)
+
+    def test_all_corners_visited(self):
+        corners = {(1, 1), (2, 2), (3, 3)}
+        visited_corners = {(1, 1), (2, 2)}
+        result = self.a_star_solver.all_corners_visited(corners, visited_corners)
+        self.assertFalse(result)
+
+    def test_find_goal_position(self):
+        maze_with_goal = ...
+        goal_position = self.a_star_solver.find_goal_position(maze_with_goal)
+        self.assertIsNotNone(goal_position)
+
+        maze_without_goal = ...
+        goal_position = self.a_star_solver.find_goal_position(maze_without_goal)
+        self.assertIsNone(goal_position)
+
+    def test_find_shortest_path_with_valid_heuristic(self):
+        def valid_heuristic(current, corners, visited_corners):
+            return 0
+
+        path = self.a_star_solver.find_shortest_path(valid_heuristic)
+        self.assertIsNotNone(path)
+
+    def test_find_shortest_path_with_invalid_heuristic(self):
+        def invalid_heuristic(current, corners, visited_corners):
+            return None
+
+        path = self.a_star_solver.find_shortest_path(invalid_heuristic)
+        self.assertIsNone(path)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/testBFS.py b/testBFS.py
new file mode 100644
index 0000000..3d5f40a
--- /dev/null
+++ b/testBFS.py
@@ -0,0 +1,109 @@
+import unittest
+from collections import deque
+from bfs import BFS
+
+
+class TestBFS(unittest.TestCase):
+    def setUp(self):
+        self.bfs_solver = BFS()
+
+    def test_init(self):
+        bfs = BFS()
+        self.assertEqual(len(bfs.visited), 0)
+        self.assertIsInstance(bfs.queue, deque)
+        self.assertEqual(len(bfs.parent), 0)
+        self.assertEqual(len(bfs.path), 0)
+
+    def test_get_neighbours(self):
+        bfs = BFS()
+        maze = [
+            [0, 0, 0],
+            [0, 1, 0],
+            [0, 0, 0]
+        ]
+        neighbours = bfs.get_neighbours(maze, 1, 1)
+        expected_neighbours = [(0, 1), (1, 0), (1, 2), (2, 1)]
+        self.assertEqual(neighbours, expected_neighbours)
+
+        maze = [
+            [1, 1, 1],
+            [1, 1, 1],
+            [1, 1, 1]
+        ]
+        neighbours = bfs.get_neighbours(maze, 1, 1)
+        self.assertEqual(neighbours, [])
+
+    def test_bfs_path_found(self):
+        maze = [
+            [0, 0, 0],
+            [1, 1, 0],
+            [0, 0, 0]
+        ]
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertTrue(self.bfs_solver.bfs(maze, start, goal))
+
+    def test_bfs_no_path(self):
+        maze = [
+            [0, 1, 0],
+            [1, 1, 0],
+            [0, 1, 0]
+        ]
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
+
+    def test_bfs_empty_maze(self):
+        maze = []
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
+
+    def test_bfs_unreachable_goal(self):
+        maze = [
+            [0, 0, 0],
+            [1, 1, 0],
+            [0, 0, 1]
+        ]
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
+
+    def test_bfs_unreachable_start(self):
+        maze = [
+            [1, 0, 0],
+            [1, 1, 0],
+            [0, 0, 0]
+        ]
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertFalse(self.bfs_solver.bfs(maze, start, goal))
+
+    def test_bfs_single_cell_maze(self):
+        maze = [[0]]
+        start = (0, 0)
+        goal = (0, 0)
+        self.assertTrue(self.bfs_solver.bfs(maze, start, goal))
+
+    def test_construct_path(self):
+        start = (0, 0)
+        goal = (2, 2)
+        parent = {
+            (1, 1): (0, 1),
+            (1, 2): (1, 1),
+            (2, 2): (1, 2),
+            (0, 1): (0, 0)
+        }
+        self.bfs_solver.parent = parent
+        self.bfs_solver.construct_path(start, goal)
+        expected_path = [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)]
+        self.assertEqual(self.bfs_solver.path, expected_path)
+
+    def test_get_path(self):
+        self.bfs_solver.path = [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)]
+        expected_path = [(2, 2), (1, 2), (1, 1), (0, 1), (0, 0)]
+        self.assertEqual(self.bfs_solver.get_path(), expected_path)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testButton.py b/testButton.py
new file mode 100644
index 0000000..c191dbb
--- /dev/null
+++ b/testButton.py
@@ -0,0 +1,56 @@
+import unittest
+from unittest.mock import Mock
+from button import Button
+
+
+class TestButton(unittest.TestCase):
+    def setUp(self):
+        pygame = Mock()
+        self.mock_screen = pygame.display.set_mode.return_value
+        self.mock_player_controller = Mock()
+        self.mock_maze = Mock()
+        self.mock_image = Mock()
+        self.mock_image.get_width.return_value = 100
+        self.mock_image.get_height.return_value = 50
+        self.button = Button(self.mock_image, 100, 100, 1, None)
+
+    def test_draw(self):
+        mock_image = Mock()
+        button = Button(mock_image, 100, 100, 1, Mock())
+
+        button.draw()
+
+        expected_calls = [
+            ((mock_image, (100, 100)),),
+        ]
+        self.assertEqual(self.mock_screen.blit.call_args_list, expected_calls)
+
+    def test_handle_mouse_click_when_clicked(self):
+        button = Button(Mock(), 100, 100, 1, self.mock_player_controller)
+
+        button.handle_mouse_click((110, 110), self.mock_maze)
+
+        self.mock_maze.set_level.assert_called_once_with(1)
+        self.mock_player_controller.reset_player_position.assert_called_once()
+        self.assertTrue(button.is_clicked)
+
+    def test_handle_mouse_click_when_not_clicked(self):
+        button = Button(Mock(), 100, 100, 1, self.mock_player_controller)
+
+        button.handle_mouse_click((90, 90), self.mock_maze)
+
+        self.assertFalse(self.mock_maze.set_level.called)
+        self.assertFalse(self.mock_player_controller.reset_player_position.called)
+        self.assertFalse(button.is_clicked)
+
+    def test_check_click_within_boundaries(self):
+        result = self.button.check_click((150, 125))
+        self.assertTrue(result)
+
+    def test_check_click_outside_boundaries(self):
+        result = self.button.check_click((50, 75))
+        self.assertFalse(result)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testDFS.py b/testDFS.py
new file mode 100644
index 0000000..53afc4f
--- /dev/null
+++ b/testDFS.py
@@ -0,0 +1,80 @@
+import unittest
+from dfs import DFS
+
+
+class TestDFS(unittest.TestCase):
+    def test_init(self):
+        dfs = DFS()
+        self.assertEqual(dfs.visited, set())
+        self.assertEqual(dfs.path, [])
+
+    def test_get_neighbours(self):
+        maze = [
+            [0, 0, 0, 1],
+            [1, 1, 0, 0],
+            [0, 1, 0, 1],
+            [0, 0, 0, 0]
+        ]
+        dfs = DFS()
+
+        self.assertEqual(dfs.get_neighbours(maze, 0, 0), [(0, 1), (1, 0)])
+        self.assertEqual(dfs.get_neighbours(maze, 1, 1), [(1, 2), (2, 1)])
+        self.assertEqual(dfs.get_neighbours(maze, 2, 2), [(2, 1), (3, 2)])
+
+    def test_dfs(self):
+        maze = [
+            [0, 0, 0, 1],
+            [1, 1, 0, 0],
+            [0, 1, 0, 1],
+            [0, 0, 0, 0]
+        ]
+        dfs = DFS()
+
+        start = (0, 0)
+        goal = (3, 3)
+        self.assertTrue(dfs.dfs(maze, start, goal))
+
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertFalse(dfs.dfs(maze, start, goal))
+
+    def test__dfs(self):
+        maze = [
+            [0, 0, 0, 1],
+            [1, 1, 0, 0],
+            [0, 1, 0, 1],
+            [0, 0, 0, 0]
+        ]
+        dfs = DFS()
+
+        start = (0, 0)
+        goal = (3, 3)
+        self.assertTrue(dfs._dfs(maze, start, goal))
+
+        start = (0, 0)
+        goal = (2, 2)
+        self.assertFalse(dfs._dfs(maze, start, goal))
+
+    def test_get_path(self):
+        maze = [
+            [0, 0, 0, 1],
+            [1, 1, 0, 0],
+            [0, 1, 0, 1],
+            [0, 0, 0, 0]
+        ]
+        dfs = DFS()
+
+        start = (0, 0)
+        goal = (3, 3)
+        dfs._dfs(maze, start, goal)
+        expected_path = [(0, 0), (0, 1), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3)]
+        self.assertEqual(dfs.get_path(), expected_path)
+
+        start = (0, 0)
+        goal = (2, 2)
+        dfs._dfs(maze, start, goal)
+        self.assertEqual(dfs.get_path(), [])
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testDijkstra.py b/testDijkstra.py
new file mode 100644
index 0000000..b6e6804
--- /dev/null
+++ b/testDijkstra.py
@@ -0,0 +1,79 @@
+import unittest
+from dijkstra import Dijkstra
+
+
+class TestDijkstra(unittest.TestCase):
+    def setUp(self):
+        self.dijkstra_solver = Dijkstra(None)
+
+    def test_calculate_cost_empty(self):
+        cell_type = 0
+        cost = self.dijkstra_solver.calculate_cost(cell_type)
+        self.assertEqual(cost, 1)
+
+    def test_calculate_cost_wall(self):
+        cell_type = 1
+        cost = self.dijkstra_solver.calculate_cost(cell_type)
+        self.assertEqual(cost, float('inf'))
+
+    def test_calculate_cost_goal(self):
+        cell_type = 2
+        cost = self.dijkstra_solver.calculate_cost(cell_type)
+        self.assertEqual(cost, 100)
+
+    def test_calculate_cost_start(self):
+        cell_type = 3
+        cost = self.dijkstra_solver.calculate_cost(cell_type)
+        self.assertEqual(cost, 1)
+
+    def test_find_shortest_path_simple(self):
+
+        start_position = (0, 0)
+        goal_position = (3, 3)
+        path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
+        expected_path = [(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (3, 2), (3, 3)]
+        self.assertEqual(path, expected_path)
+
+    def test_find_shortest_path_blocked_goal(self):
+
+        start_position = (0, 0)
+        goal_position = (3, 3)
+        path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
+        expected_path = []
+        self.assertEqual(path, expected_path)
+
+    def test_find_shortest_path_complex(self):
+
+        start_position = (0, 0)
+        goal_position = (4, 4)
+        path = self.dijkstra_solver.find_shortest_path(start_position, goal_position)
+        expected_path = [(0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (3, 3), (4, 3), (4, 4)]
+        self.assertEqual(path, expected_path)
+
+    def test_get_neighbors_middle(self):
+        maze = Dijkstra([
+            [0, 0, 0, 1],
+            [1, 1, 0, 0],
+            [0, 1, 0, 1],
+            [0, 0, 0, 0]
+        ])
+        cell = (1, 1)
+        neighbors = maze.get_neighbors(cell)
+        expected_neighbors = [(0, 1), (2, 1), (1, 0), (1, 2)]
+        self.assertEqual(neighbors, expected_neighbors)
+
+    def test_get_neighbors_corner(self):
+        maze = Dijkstra([
+            [0, 0, 0, 1],
+            [1, 1, 0, 0],
+            [0, 1, 0, 1],
+            [0, 0, 0, 0]
+        ])
+        cell = (0, 0)
+        neighbors = maze.get_neighbors(cell)
+        expected_neighbors = [(1, 0), (0, 1)]
+        self.assertEqual(neighbors, expected_neighbors)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testEnemy.py b/testEnemy.py
new file mode 100644
index 0000000..048d0ff
--- /dev/null
+++ b/testEnemy.py
@@ -0,0 +1,46 @@
+import unittest
+from unittest.mock import MagicMock, patch
+from enemy import Enemy
+from config import TILE_SIZE
+
+
+class TestEnemy(unittest.TestCase):
+    @patch('enemy.random')
+    def test_init(self, mock_random):
+        mock_maze = MagicMock()
+
+        mock_random.randint.return_value = 2
+        mock_maze.num_rows.return_value = 5
+        mock_maze.num_columns.return_value = 6
+        mock_maze.__getitem__.return_value = 0
+
+        enemy = Enemy(mock_maze)
+
+        self.assertEqual(enemy.x, 2 * 32)
+        self.assertEqual(enemy.y, 0)
+
+    @patch('enemy.random')
+    def test_reset_position(self, mock_random):
+        mock_maze = MagicMock()
+
+        mock_random.randint.return_value = 2
+        mock_maze.num_rows.return_value = 5
+        mock_maze.num_columns.return_value = 6
+        mock_maze.__getitem__.return_value = 0
+
+        enemy = Enemy(mock_maze)
+        enemy.reset_position()
+
+        self.assertEqual(enemy.x, 2 * 32)
+        self.assertEqual(enemy.y, 0)
+
+    def test_draw(self):
+        mock_screen = MagicMock()
+
+        enemy = Enemy(None)
+        enemy.draw(mock_screen)
+        mock_screen.assert_called_once_with('blue', (enemy.x, enemy.y, enemy.TILE_SIZE, enemy.TILE_SIZE))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testExpectimax.py b/testExpectimax.py
new file mode 100644
index 0000000..d456122
--- /dev/null
+++ b/testExpectimax.py
@@ -0,0 +1,69 @@
+import unittest
+from expectimax import Expectimax
+
+
+class TestExpectimax(unittest.TestCase):
+
+    def test_valid_scenario(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        expectimax_solver = Expectimax(maze)
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = 3
+        maximizing_player = True
+        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
+        self.assertIsNotNone(result)
+
+    def test_invalid_scenario_negative_depth(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        expectimax_solver = Expectimax(maze)
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = -1
+        maximizing_player = True
+        with self.assertRaises(ValueError):
+            expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
+
+    def test_no_valid_moves(self):
+        maze = [[1, 1, 1],
+                [1, 1, 1],
+                [1, 1, 1]]
+        expectimax_solver = Expectimax(maze)
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = 3
+        maximizing_player = True
+        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
+        self.assertEqual(result, 0)
+
+    def test_game_over(self):
+        maze = [[0, 0, 0],
+                [0, 0, 0],
+                [0, 0, 0]]
+        expectimax_solver = Expectimax(maze)
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = 3
+        maximizing_player = True
+        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
+        self.assertEqual(result, 0)
+
+    def test_max_depth(self):
+        maze = [[0, 0, 0],
+                [0, 0, 0],
+                [0, 0, 0]]
+        expectimax_solver = Expectimax(maze)
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = 10
+        maximizing_player = True
+        result = expectimax_solver.expectimax(player_position, enemy_position, depth, maximizing_player)
+        self.assertIsNotNone(result)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/testGame.py b/testGame.py
new file mode 100644
index 0000000..dbbfc54
--- /dev/null
+++ b/testGame.py
@@ -0,0 +1,60 @@
+import unittest
+from game import Game
+
+
+class TestGame(unittest.TestCase):
+
+    def setUp(self):
+        self.maze = [
+            [1, 1, 1, 1, 1],
+            [1, 0, 0, 0, 1],
+            [1, 0, 1, 0, 1],
+            [1, 0, 0, 0, 1],
+            [1, 1, 1, 1, 1]
+        ]
+        self.game = Game(self.maze)
+
+    def test_init(self):
+        maze = [
+            [1, 1, 1, 1, 1],
+            [1, 0, 0, 0, 1],
+            [1, 0, 1, 0, 1],
+            [1, 0, 0, 0, 1],
+            [1, 1, 1, 1, 1]
+        ]
+
+        game = Game(maze)
+
+        self.assertEqual(game.maze, maze)
+        self.assertEqual(game.player_position, (1, 1))
+        self.assertEqual(game.enemy_positions, [(2, 2)])
+
+    def test_get_player_position(self):
+        maze = [
+            [1, 1, 1, 1, 1],
+            [1, 0, 0, 0, 1],
+            [1, 0, 1, 0, 1],
+            [1, 0, 0, 0, 1],
+            [1, 1, 1, 1, 1]
+        ]
+
+        game = Game(maze)
+        player_position = game.get_player_position()
+        self.assertEqual(player_position, (1, 1))
+
+    def test_get_enemy_positions(self):
+        enemy_positions = self.game.get_enemy_positions()
+        self.assertEqual(enemy_positions, [(2, 2)])
+
+    def test_update_positions(self):
+        self.game.update_positions()
+        self.assertEqual(self.game.player_position, (1, 1))
+        self.assertEqual(self.game.enemy_positions, [(2, 2)])
+
+    def test_check_game_over(self):
+        game_over = self.game.check_game_over()
+        self.assertFalse(game_over)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testMaze.py b/testMaze.py
new file mode 100644
index 0000000..da1c135
--- /dev/null
+++ b/testMaze.py
@@ -0,0 +1,92 @@
+import unittest
+from unittest.mock import Mock
+
+import pygame
+
+from maze import Maze
+
+
+class TestMaze(unittest.TestCase):
+    def test_num_rows(self):
+        initial_level = [
+            [0, 0, 0],
+            [0, 0, 0],
+            [0, 0, 0]
+        ]
+        maze = Maze(initial_level)
+        self.assertEqual(maze.num_rows(), 3)
+
+    def test_num_columns(self):
+        initial_level = [
+            [0, 0, 0, 0],
+            [0, 0, 0, 0],
+            [0, 0, 0, 0]
+        ]
+        maze = Maze(initial_level)
+        self.assertEqual(maze.num_columns(), 4)
+
+    def test_getitem(self):
+        initial_level = [
+            [0, 0, 0],
+            [0, 0, 0],
+            [0, 0, 0]
+        ]
+        maze = Maze(initial_level)
+        self.assertEqual(maze[0], [0, 0, 0])
+        self.assertEqual(maze[1], [0, 0, 0])
+        self.assertEqual(maze[2], [0, 0, 0])
+
+    def test_set_level(self):
+        initial_level = [
+            [0, 0, 0],
+            [0, 0, 0],
+            [0, 0, 0]
+        ]
+        maze = Maze(initial_level)
+        new_level = [
+            [1, 1, 1],
+            [1, 1, 1],
+            [1, 1, 1]
+        ]
+        maze.set_level(new_level)
+        self.assertEqual(maze.current_level, new_level)
+
+    def test_update_screen_size(self):
+        initial_level = [
+            [0, 0, 0],
+            [0, 0, 0],
+            [0, 0, 0]
+        ]
+        maze = Maze(initial_level)
+        pygame.display.set_mode((800, 600))
+        maze.update_screen_size()
+        self.assertEqual(pygame.display.get_surface().get_size(), (3 * 32, 3 * 32))
+
+    def test_draw(self):
+        initial_level = [
+            [0, 0, 0],
+            [0, 1, 0],
+            [0, 0, 0]
+        ]
+        maze = Maze(initial_level)
+        tiles = {0: Mock(), 1: Mock()}
+        maze.tiles = tiles
+
+        maze.draw()
+
+        expected_calls = [
+            ((tiles[0], (0, 0)),),
+            ((tiles[0], (32, 0)),),
+            ((tiles[0], (64, 0)),),
+            ((tiles[0], (0, 32)),),
+            ((tiles[1], (32, 32)),),
+            ((tiles[0], (64, 32)),),
+            ((tiles[0], (0, 64)),),
+            ((tiles[0], (32, 64)),),
+            ((tiles[0], (64, 64)),),
+        ]
+        self.assertEqual(self.mock_screen.blit.call_args_list, expected_calls)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testMinMax.py b/testMinMax.py
new file mode 100644
index 0000000..a3ae88d
--- /dev/null
+++ b/testMinMax.py
@@ -0,0 +1,112 @@
+import unittest
+from minmax import MinMax
+
+
+class TestMinMax(unittest.TestCase):
+
+    def test_get_valid_moves_middle(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        minmax = MinMax(maze)
+        player_position = (1, 1)
+        valid_moves = minmax.get_valid_moves(player_position)
+        expected_moves = [(0, 1), (2, 1), (1, 0), (1, 2)]
+        self.assertEqual(valid_moves, expected_moves)
+
+    def test_get_valid_moves_corner(self):
+        maze = [[0, 1, 0],
+                [1, 1, 0],
+                [0, 0, 0]]
+        minmax = MinMax(maze)
+        player_position = (0, 0)
+        valid_moves = minmax.get_valid_moves(player_position)
+        expected_moves = [(1, 0), (0, 1)]
+        self.assertEqual(valid_moves, expected_moves)
+
+    def test_valid_move(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        minmax = MinMax(maze)
+        valid_position = (1, 0)
+        self.assertTrue(minmax.is_valid_move(valid_position))
+
+    def test_invalid_move_wall(self):
+        maze = [[0, 1, 0],
+                [1, 1, 0],
+                [0, 0, 0]]
+        minmax = MinMax(maze)
+        wall_position = (0, 1)
+        self.assertFalse(minmax.is_valid_move(wall_position))
+
+    def test_invalid_move_outside(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        minmax = MinMax(maze)
+        outside_position = (3, 0)
+        self.assertFalse(minmax.is_valid_move(outside_position))
+
+    def test_minmax(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        minmax = MinMax(maze)
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = 3
+        alpha = float('-inf')
+        beta = float('inf')
+        maximizing_player = True
+        result = minmax.minmax(player_position, enemy_position, depth, alpha, beta, maximizing_player)
+        self.assertIsInstance(result, int)
+
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        player_position = (0, 0)
+        enemy_position = (2, 2)
+        depth = 1
+
+        result = minmax.minmax(player_position, enemy_position, depth, float('-inf'), float('inf'), maximizing_player)
+        print("Result:", result)
+
+    def test_evaluate(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        minmax_object = MinMax(maze)
+        print("Testing the evaluate method...")
+
+        player_position = (1, 1)
+        enemy_position = (1, 1)
+        evaluation_result = minmax_object.evaluate(player_position, enemy_position)
+        print("Evaluation result when player and enemy are at the same position:", evaluation_result)
+
+        player_position = (2, 2)
+        enemy_position = (1, 1)
+        evaluation_result = minmax_object.evaluate(player_position, enemy_position)
+        print("Evaluation result when player is closer to the goal than the enemy:", evaluation_result)
+
+        player_position = (1, 1)
+        enemy_position = (2, 2)
+        evaluation_result = minmax_object.evaluate(player_position, enemy_position)
+        print("Evaluation result when enemy is closer to the goal than the player:", evaluation_result)
+
+    def test_game_over(self):
+        maze = [[0, 0, 0],
+                [0, 1, 0],
+                [0, 0, 0]]
+        minmax_object = MinMax(maze)
+        print("Testing the game_over method...")
+
+        game_over_result = minmax_object.game_over()
+        print("Game over result when the game is not over:", game_over_result)
+
+        game_over_result = minmax_object.game_over()
+        print("Game over result when the game is over:", game_over_result)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/testPlayer.py b/testPlayer.py
new file mode 100644
index 0000000..a3386cc
--- /dev/null
+++ b/testPlayer.py
@@ -0,0 +1,18 @@
+import unittest
+from unittest.mock import Mock, patch
+from config import TILE_SIZE
+from player import Player
+
+
+class TestPlayer(unittest.TestCase):
+    @patch('pygame.draw.rect')
+    def test_draw(self, mock_rect):
+        # Mock the screen surface
+        screen_surface_mock = Mock()
+        player = Player(10, 20)
+        player.draw(screen_surface_mock)
+        mock_rect.assert_called_once_with(screen_surface_mock, 'white', (10, 20, TILE_SIZE, TILE_SIZE))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/testPlayerController.py b/testPlayerController.py
new file mode 100644
index 0000000..591c217
--- /dev/null
+++ b/testPlayerController.py
@@ -0,0 +1,226 @@
+import unittest
+from unittest.mock import Mock
+
+import pygame
+
+from config import TILE_SIZE
+from player import Player
+from playerController import PlayerController  # Import the PlayerController class
+
+
+class TestPlayerController(unittest.TestCase):
+    def setUp(self):
+        self.player_mock = Mock()
+        self.maze_mock = Mock()
+        self.player_controller = PlayerController(self.player_mock, self.maze_mock)
+
+    def test_move_player_up(self):
+        direction = {pygame.K_UP: True, pygame.K_DOWN: False, pygame.K_LEFT: False, pygame.K_RIGHT: False}
+        self.player_controller.move_player(direction)
+        self.assertEqual(self.player_mock.y, self.player_mock.y - TILE_SIZE)
+
+    def test_move_player_down(self):
+        direction = {pygame.K_UP: False, pygame.K_DOWN: True, pygame.K_LEFT: False, pygame.K_RIGHT: False}
+        self.player_controller.move_player(direction)
+        self.assertEqual(self.player_mock.y, self.player_mock.y + TILE_SIZE)
+
+    def test_move_player_left(self):
+        direction = {pygame.K_UP: False, pygame.K_DOWN: False, pygame.K_LEFT: True, pygame.K_RIGHT: False}
+        self.player_controller.move_player(direction)
+        self.assertEqual(self.player_mock.x, self.player_mock.x - TILE_SIZE)
+
+    def test_move_player_right(self):
+        direction = {pygame.K_UP: False, pygame.K_DOWN: False, pygame.K_LEFT: False, pygame.K_RIGHT: True}
+        self.player_controller.move_player(direction)
+        self.assertEqual(self.player_mock.x, self.player_mock.x + TILE_SIZE)
+
+    def test_valid_position_within_maze(self):
+        position = (1, 1)
+        self.assertTrue(self.player_controller.is_valid_position(position))
+
+    def test_position_outside_maze_bounds(self):
+        position = (-1, 1)
+        self.assertFalse(self.player_controller.is_valid_position(position))
+        position = (10, 5)
+        self.assertFalse(self.player_controller.is_valid_position(position))
+        position = (1, 10)
+        self.assertFalse(self.player_controller.is_valid_position(position))
+
+    def test_position_collides_with_wall(self):
+        self.player_controller.maze = Mock(current_level=[[0, 0, 0],
+                                                          [0, 1, 0],
+                                                          [0, 0, 0]])
+        position = (1, 1)
+        self.assertFalse(self.player_controller.is_valid_position(position))
+        position = (0, 1)
+        self.assertTrue(self.player_controller.is_valid_position(position))
+
+    def test_collision_with_wall(self):
+        self.player_controller.maze = [[0, 0, 0],
+                                       [0, 1, 0],
+                                       [0, 0, 0]]
+        x, y = 50, 50
+        self.assertTrue(self.player_controller.check_collision(x, y))
+
+    def test_no_collision_with_wall(self):
+        self.player_controller.maze = [[0, 0, 0],
+                                       [0, 0, 0],
+                                       [0, 0, 0]]
+        x, y = 50, 50
+        self.assertFalse(self.player_controller.check_collision(x, y))
+
+    def test_reset_player_position(self):
+        initial_x, initial_y = 100, 100
+        self.player_controller.player = Player(initial_x, initial_y)
+        self.player_controller.reset_player_position()
+        self.assertEqual(self.player_controller.player.x, TILE_SIZE)
+        self.assertEqual(self.player_controller.player.y, TILE_SIZE)
+
+    def test_set_dfs_solver(self):
+        dfs_solver_mock = Mock()
+        self.player_controller.set_dfs_solver(dfs_solver_mock)
+        self.assertEqual(self.player_controller.dfs_solver, dfs_solver_mock)
+
+    def test_set_bfs_solver(self):
+        bfs_solver_mock = Mock()
+        self.player_controller.set_bfs_solver(bfs_solver_mock)
+        self.assertEqual(self.player_controller.bfs_solver, bfs_solver_mock)
+
+    def test_set_dijkstra_solver(self):
+        dijkstra_solver_mock = Mock()
+        self.player_controller.set_dijkstra_solver(dijkstra_solver_mock)
+        self.assertEqual(self.player_controller.dijkstra_solver, dijkstra_solver_mock)
+
+    def test_set_astar_solver(self):
+        astar_solver_mock = Mock()
+        self.player_controller.set_astar_solver(astar_solver_mock)
+        self.assertEqual(self.player_controller.astar_solver, astar_solver_mock)
+
+    def test_move_to_goal_dfs(self):
+        dfs_solver_mock = Mock()
+        dfs_solver_mock.dfs.return_value = True
+        dfs_solver_mock.get_path.return_value = [(1, 1), (1, 2), (1, 3)]
+
+        self.player_controller.set_dfs_solver(dfs_solver_mock)
+        self.player_controller.maze = Mock()
+        self.player_controller.maze.current_level = [
+            [0, 0, 0, 0],
+            [0, 0, 3, 0],
+            [0, 0, 0, 0],
+        ]
+
+        self.player_controller.player.x = TILE_SIZE
+        self.player_controller.player.y = TILE_SIZE
+        self.player_controller.move_to_goal_dfs()
+
+        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
+        self.assertEqual(
+            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
+            expected_path)
+
+    def test_move_to_goal_bfs(self):
+        bfs_solver_mock = Mock()
+        bfs_solver_mock.bfs.return_value = True
+        bfs_solver_mock.get_path.return_value = [(1, 1), (1, 2), (1, 3)]
+
+        self.player_controller.set_bfs_solver(bfs_solver_mock)
+        self.player_controller.maze = Mock()
+        self.player_controller.maze.current_level = [
+            [0, 0, 0, 0],
+            [0, 0, 3, 0],
+            [0, 0, 0, 0],
+        ]
+
+        self.player_controller.player.x = TILE_SIZE
+        self.player_controller.player.y = TILE_SIZE
+        self.player_controller.move_to_goal_bfs()
+
+        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
+        self.assertEqual(
+            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
+            expected_path)
+
+    def test_move_to_goal_dijkstra(self):
+        dijkstra_solver_mock = Mock()
+        dijkstra_solver_mock.find_shortest_path.return_value = [(1, 1), (1, 2), (1, 3)]  # Simulate a path
+
+        self.player_controller.set_dijkstra_solver(dijkstra_solver_mock)
+        self.player_controller.maze = Mock()
+        self.player_controller.maze.current_level = [
+            [0, 0, 0, 0],
+            [0, 0, 3, 0],
+            [0, 0, 0, 0],
+        ]
+
+        self.player_controller.player.x = TILE_SIZE
+        self.player_controller.player.y = TILE_SIZE
+        self.player_controller.move_to_goal_dijkstra()
+
+        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
+        self.assertEqual(
+            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
+            expected_path)
+
+    def test_move_to_goal_astar(self):
+        astar_solver_mock = Mock()
+        astar_solver_mock.find_goal_position.return_value = (2, 2)  # Simulate a goal position
+        astar_solver_mock.find_shortest_path.return_value = [(1, 1), (1, 2), (1, 3)]  # Simulate a path
+
+        self.player_controller.set_astar_solver(astar_solver_mock)
+        self.player_controller.maze = Mock()
+        self.player_controller.maze.current_level = [
+            [0, 0, 0, 0],
+            [0, 0, 3, 0],
+            [0, 0, 0, 0],
+        ]
+
+        self.player_controller.player.x = TILE_SIZE
+        self.player_controller.player.y = TILE_SIZE
+        self.player_controller.move_to_goal_astar()
+
+        expected_path = [(1 * TILE_SIZE, 1 * TILE_SIZE), (1 * TILE_SIZE, 2 * TILE_SIZE), (1 * TILE_SIZE, 3 * TILE_SIZE)]
+        self.assertEqual(
+            [(self.player_controller.player.x, self.player_controller.player.y) for _ in range(len(expected_path))],
+            expected_path)
+
+    def test_follow_path(self):
+        player_mock = Mock()
+        self.player_controller.player = player_mock
+        maze_mock = Mock()
+        self.player_controller.maze = maze_mock
+        path = [(0, 0), (0, 1), (0, 2)]
+        self.player_controller.follow_path(path)
+        expected_positions = [(0, 0), (0, 1), (0, 2)]
+        self.assertEqual([(args[0], args[1]) for args in player_mock.x.call_args_list], expected_positions)
+
+    def test_draw_path(self):
+        screen_mock = Mock()
+        self.player_controller.screen = screen_mock
+        path = [(0, 0), (0, 1), (1, 1)]
+        self.player_controller.draw_path(path)
+
+        expected_calls = [
+            ((screen_mock,), {'color': (0, 255, 0), 'rect': (0, 0, TILE_SIZE, TILE_SIZE)}),
+            ((screen_mock,), {'color': (0, 255, 0), 'rect': (TILE_SIZE, 0, TILE_SIZE, TILE_SIZE)}),
+            ((screen_mock,), {'color': (0, 255, 0), 'rect': (TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)})
+        ]
+
+        self.assertEqual(screen_mock.mock_calls,
+                         [unittest.mock.call.draw.rect(*args, **kwargs) for args, kwargs in expected_calls])
+
+    def test_find_goal_position(self):
+        mock_maze = Mock()
+        mock_maze.current_level = [
+            [0, 0, 0],
+            [0, 0, 0],
+            [0, 0, 3]
+        ]
+
+        self.player_controller.maze = mock_maze
+        goal_position = self.player_controller.find_goal_position()
+        expected_goal_position = (2, 2)
+        self.assertEqual(goal_position, expected_goal_position)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/wall.png b/wall.png
new file mode 100644
index 0000000000000000000000000000000000000000..eb06cd2a45741ab3b9b0d54f0fa47a7fd6493295
GIT binary patch
literal 144
zcmeAS@N?(olHy`uVBq!ia0vp^4?&oN8Ax*GDtQ1Y<^Z1%*To+HGoO7)268<-T^vIy
t=Da=F2;?vvUhp$G{sBiQBZxx^_`rK?5d&j5fAnIIE>BlKmvv4FO#nnu8wdaZ

literal 0
HcmV?d00001

-- 
GitLab