Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addition of Maze Solver Application #1583

Merged
merged 7 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Desktop Application/Basic/Python/Maze Solver/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Maze Solver Application
This application provides an interactive way to visualize and solve a maze using a depth-first search (DFS) algorithm. Users can load a maze from a .txt file, solve it, and view the solution and explored states visually.

## Components and Functions
### Load Maze:
- Button: Loads a maze file.
- File Format: The maze must be in a .txt format, using:
- A for the starting point,
- B for the goal,
- '#' for walls,
- Spaces ( ) for open paths.
- Usage: Click β€œLoad Maze” and select a .txt file with a properly formatted maze.

### Solve Maze:

- Button: Solves the loaded maze using the depth-first search algorithm.
- Algorithm: A StackFrontier implements DFS by exploring paths until the goal (B) is reached, recording explored states and marking the solution path.
- Output: Displays the number of explored states and highlights the solution path on the maze.

## How to Load and Solve a Maze
- Load the Maze: Click β€œLoad Maze” and select a .txt file with the maze layout.
- Solve the Maze: After loading, click β€œSolve Maze” to visualize the solution.
- View Results: The solution path and explored cells appear in the image, with counts of explored states displayed.

## Example

Input maze.txt in format:
![Screenshot (3094)](https://github.com/user-attachments/assets/23d03ef6-23dc-4fd7-afc1-08173969359c)

Loading image into the interface:
![Screenshot (3097)](https://github.com/user-attachments/assets/49559047-c2bd-452c-95ff-af77cb7ba775)
The file has been loaded and saved in the form of .png file with the name 'temp_maze.png'

After solving the maze:
![Screenshot (3098)](https://github.com/user-attachments/assets/6bd72a2c-6962-455e-95cc-468917caebb9)
After solving the maze, the image is saved as 'solved_maze.png'
Kushal997-das marked this conversation as resolved.
Show resolved Hide resolved
216 changes: 216 additions & 0 deletions Desktop Application/Basic/Python/Maze Solver/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk

class Node:
def __init__(self, state, parent, action):
self.state = state
self.parent = parent
self.action = action

class StackFrontier:
def __init__(self):
self.frontier = []

def add(self, node):
self.frontier.append(node)

def contains_state(self, state):
return any(node.state == state for node in self.frontier)

def empty(self):
return len(self.frontier) == 0

def remove(self):
if self.empty():
raise Exception("Frontier is empty")
else:
node = self.frontier[-1]
self.frontier = self.frontier[:-1]
return node

class Maze:
def __init__(self, filename):
with open(filename) as f:
contents = f.read()

if contents.count("A") != 1:
raise Exception("Maze must have exactly one starting point.")
if contents.count("B") != 1:
raise Exception("Maze must have exactly one finishing point.")

contents = contents.splitlines()
self.height = len(contents)
self.width = max(len(line) for line in contents)

self.walls = []
for i in range(self.height):
row = []
for j in range(self.width):
try:
if contents[i][j] == "A":
self.start = (i, j)
row.append(False)
elif contents[i][j] == "B":
self.goal = (i, j)
row.append(False)
elif contents[i][j] == " ":
row.append(False)
else:
row.append(True)
except IndexError:
row.append(False)
self.walls.append(row)

self.solution = None

def neighbors(self, state):
row, col = state
candidates = [
("up", (row - 1, col)),
("down", (row + 1, col)),
("left", (row, col - 1)),
("right", (row, col + 1))
]
result = []
for action, (r, c) in candidates:
if 0 <= r < self.height and 0 <= c < self.width and not self.walls[r][c]:
result.append((action, (r, c)))
return result

def solve(self):
self.num_explored = 0
start = Node(state=self.start, parent=None, action=None)
frontier = StackFrontier()
frontier.add(start)
self.explored = set()

while True:
if frontier.empty():
raise Exception("No solution")

node = frontier.remove()
self.num_explored += 1

if node.state == self.goal:
actions = []
cells = []
while node.parent is not None:
actions.append(node.action)
cells.append(node.state)
node = node.parent
actions.reverse()
cells.reverse()
self.solution = (actions, cells)
return

self.explored.add(node.state)

for action, state in self.neighbors(node.state):
if not frontier.contains_state(state) and state not in self.explored:
child = Node(state=state, parent=node, action=action)
frontier.add(child)

def output_image(self, filename, show_solution=True, show_explored=False):
from PIL import Image, ImageDraw
cell_size = 50
cell_border = 2

img = Image.new("RGBA", (self.width * cell_size, self.height * cell_size), "black")
draw = ImageDraw.Draw(img)
solution = self.solution[1] if self.solution is not None else None

for i, row in enumerate(self.walls):
for j, col in enumerate(row):
if col:
fill = (40, 40, 40)
elif (i, j) == self.start:
fill = (38, 32, 224)
elif (i, j) == self.goal:
fill = (0, 171, 28)
elif solution is not None and show_solution and (i, j) in solution:
fill = (220, 235, 113)
elif solution is not None and show_explored and (i, j) in self.explored:
fill = (212, 97, 85)
else:
fill = (237, 240, 252)

draw.rectangle(
[
(j * cell_size + cell_border, i * cell_size + cell_border),
((j + 1) * cell_size - cell_border, (i + 1) * cell_size - cell_border)
],
fill=fill
)

img.save(filename)

class MazeApp:
def __init__(self, root):
self.root = root
self.root.title("Maze Solver")
self.root.geometry("600x700")

# Frame for buttons
self.controls_frame = tk.Frame(self.root)
self.controls_frame.pack(pady=10)

# Load Maze Button
self.load_button = tk.Button(self.controls_frame, text="Load Maze", command=self.load_maze, background="white")
self.load_button.pack(side=tk.LEFT, padx=5)

# Solve Maze Button
self.solve_button = tk.Button(self.controls_frame, text="Solve Maze", command=self.solve_maze, background="white")
self.solve_button.pack(side=tk.LEFT, padx=5)

# Canvas to display the maze
self.canvas = tk.Canvas(self.root, width=500, height=500, bg="white")
self.canvas.pack(pady=20)

# Label for displaying stats
self.stats_label = tk.Label(self.root, text="", font=("Arial", 12))
self.stats_label.pack()

self.maze = None
self.image = None

def load_maze(self):
filename = filedialog.askopenfilename(title="Select Maze File", filetypes=[("Text Files", "*.txt")])

if filename:
try:
self.maze = Maze(filename)
self.display_maze()
except Exception as e:
messagebox.showerror("Error", f"Failed to load maze: {str(e)}")

def display_maze(self):
if self.maze:
self.maze.output_image("temp_maze.png", show_solution=False)
self.image = Image.open("temp_maze.png")
self.image = self.image.resize((500, 500), Image.LANCZOS)
self.photo = ImageTk.PhotoImage(self.image)
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo)

def solve_maze(self):
if self.maze:
try:
self.maze.solve()
self.stats_label.config(text=f"States Explored: {self.maze.num_explored} \n Understanding pigmentation:\nBlue: Starting Point\tGreen: Ending Point\nYellow: Solution path of the maze\tRed: Incorrect path traversed by the program\nWhite: All possible path in the maze\tBlack: Walls")
self.display_solution()
except Exception as e:
messagebox.showerror("Error", f"Failed to solve maze: {str(e)}")
else:
messagebox.showwarning("Warning", "Please load a maze first.")

def display_solution(self):
self.maze.output_image("solved_maze.png", show_solution=True, show_explored=True)
self.image = Image.open("solved_maze.png")
self.image = self.image.resize((500, 500), Image.LANCZOS)
self.photo = ImageTk.PhotoImage(self.image)
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo)

if __name__ == "__main__":
root = tk.Tk()
app = MazeApp(root)
root.mainloop()
2 changes: 2 additions & 0 deletions Desktop Application/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
| 30. | [PDF Utility Tool](https://github.com/Kushal997-das/Project-Guidance/tree/main/Desktop%20Application/Basic/Python/PDF%20Utility%20Tool) |
| 31. | [Quiz_Application](https://github.com/Kushal997-das/Project-Guidance/tree/main/Desktop%20Application/Basic/Python/Quiz_Application) |
| 32. | [Selenium Automated game playing bot](https://github.com/Kushal997-das/Project-Guidance/tree/main/Desktop%20Application/Basic/Python/Selenium-Automated-Cookie-PlayerGame) |
| 33. | [Maze Solver Application](https://github.com/Kushal997-das/Project-Guidance/tree/main/Desktop%20Application/Basic/Python/Maze%20Solver) |

</br>

## Java πŸš€
Expand Down