Skip to content

Commit b90d874

Browse files
REFACTOR: Auto-Format Python Codebase with Ruff and Black
1 parent 24b2d32 commit b90d874

File tree

149 files changed

+16285
-12872
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+16285
-12872
lines changed

8-Puzzle/main.py

Lines changed: 141 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,176 @@
1-
import heapq # For priority queue
2-
from colorama import Style # For coloring the terminal
1+
import heapq # For priority queue
2+
from colorama import Style # For coloring the terminal
3+
34

45
# Macros:
5-
class BackgroundColors: # Colors for the terminal
6-
CYAN = "\033[96m" # Cyan
7-
GREEN = "\033[92m" # Green
8-
YELLOW = "\033[93m" # Yellow
9-
RED = "\033[91m" # Red
10-
BOLD = "\033[1m" # Bold
11-
UNDERLINE = "\033[4m" # Underline
12-
CLEAR_TERMINAL = "\033[H\033[J" # Clear the terminal
6+
class BackgroundColors: # Colors for the terminal
7+
CYAN = "\033[96m" # Cyan
8+
GREEN = "\033[92m" # Green
9+
YELLOW = "\033[93m" # Yellow
10+
RED = "\033[91m" # Red
11+
BOLD = "\033[1m" # Bold
12+
UNDERLINE = "\033[4m" # Underline
13+
CLEAR_TERMINAL = "\033[H\033[J" # Clear the terminal
14+
1315

1416
# Constants:
15-
GOAL_STATE = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] # Goal state. 0 represents the empty tile
16-
INITIAL_STATE = [[1, 0, 3], [4, 2, 5], [7, 8, 6]] # Initial state
17-
MOVES = [(0, 1), (1, 0), (0, -1), (-1, 0)] # All of the possible moves
18-
MOVE_NAMES = ["right", "down", "left", "up"] # Names of the moves
17+
GOAL_STATE = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] # Goal state. 0 represents the empty tile
18+
INITIAL_STATE = [[1, 0, 3], [4, 2, 5], [7, 8, 6]] # Initial state
19+
MOVES = [(0, 1), (1, 0), (0, -1), (-1, 0)] # All of the possible moves
20+
MOVE_NAMES = ["right", "down", "left", "up"] # Names of the moves
21+
1922

2023
# Define a class to represent the puzzle state
2124
class PuzzleState:
22-
# Initialize the class
23-
def __init__(self, state, parent=None, move=""):
24-
self.state = state # Current state of the puzzle
25-
self.parent = parent # Parent state
26-
self.move = move # Move that led to the current state
27-
self.g = 0 # Cost from start node to current node
28-
self.h = self.calculate_heuristic() # Heuristic (estimated cost to goal)
29-
self.f = self.g + self.h # Total estimated cost
30-
31-
# Define a function to calculate the heuristic
32-
def calculate_heuristic(self):
33-
h = 0 # Number of misplaced tiles
34-
for i in range(3): # For each row
35-
for j in range(3): # For each column
36-
if self.state[i][j] != 0: # If the tile is not empty
37-
goal_row, goal_col = divmod(self.state[i][j] - 1, 3) # Find the goal position of the tile
38-
h += abs(i - goal_row) + abs(j - goal_col) # Add the Manhattan distance to the heuristic
39-
return h # Return the heuristic
40-
41-
# Define a function to compare two states
42-
def __lt__(self, other):
43-
return self.f < other.f # Compare the f values of the states
25+
# Initialize the class
26+
def __init__(self, state, parent=None, move=""):
27+
self.state = state # Current state of the puzzle
28+
self.parent = parent # Parent state
29+
self.move = move # Move that led to the current state
30+
self.g = 0 # Cost from start node to current node
31+
self.h = self.calculate_heuristic() # Heuristic (estimated cost to goal)
32+
self.f = self.g + self.h # Total estimated cost
33+
34+
# Define a function to calculate the heuristic
35+
def calculate_heuristic(self):
36+
h = 0 # Number of misplaced tiles
37+
for i in range(3): # For each row
38+
for j in range(3): # For each column
39+
if self.state[i][j] != 0: # If the tile is not empty
40+
goal_row, goal_col = divmod(self.state[i][j] - 1, 3) # Find the goal position of the tile
41+
h += abs(i - goal_row) + abs(j - goal_col) # Add the Manhattan distance to the heuristic
42+
return h # Return the heuristic
43+
44+
# Define a function to compare two states
45+
def __lt__(self, other):
46+
return self.f < other.f # Compare the f values of the states
47+
4448

4549
# Define a function to find possible moves from a given state
4650
# Input: state (2D list)
4751
# Returns a list of possible moves
4852
def find_possible_moves(state):
49-
empty_row, empty_col = None, None
50-
for i in range(3): # For each row
51-
for j in range(3): # For each column
52-
if state[i][j] == 0: # If the tile is empty
53-
empty_row, empty_col = i, j # Store the row and column of the empty tile
54-
break # Break out of the loop
55-
56-
possible_moves = [] # List of possible moves
57-
for move, move_name in zip(MOVES, MOVE_NAMES): # For each move
58-
new_row, new_col = empty_row + move[0], empty_col + move[1] # Find the new row and column of the empty tile
59-
if 0 <= new_row < 3 and 0 <= new_col < 3: # If the new row and column are valid
60-
possible_moves.append(move_name) # Add the move to the list of possible moves
61-
62-
return possible_moves # Return the list of possible moves
53+
empty_row, empty_col = None, None
54+
for i in range(3): # For each row
55+
for j in range(3): # For each column
56+
if state[i][j] == 0: # If the tile is empty
57+
empty_row, empty_col = i, j # Store the row and column of the empty tile
58+
break # Break out of the loop
59+
60+
possible_moves = [] # List of possible moves
61+
for move, move_name in zip(MOVES, MOVE_NAMES): # For each move
62+
new_row, new_col = empty_row + move[0], empty_col + move[1] # Find the new row and column of the empty tile
63+
if 0 <= new_row < 3 and 0 <= new_col < 3: # If the new row and column are valid
64+
possible_moves.append(move_name) # Add the move to the list of possible moves
65+
66+
return possible_moves # Return the list of possible moves
67+
6368

6469
# Define a function to perform a move and generate a new state
6570
# Input: state (2D list), move (string)
6671
# Returns a new state (2D list)
6772
def perform_move(state, move):
68-
empty_row, empty_col = None, None
69-
for i in range(3): # For each row
70-
for j in range(3): # For each column
71-
if state[i][j] == 0: # If the tile is empty
72-
empty_row, empty_col = i, j # Store the row and column of the empty tile
73-
break # Break out of the loop
74-
75-
# Create a copy of the state
76-
new_state = [row[:] for row in state]
77-
if move == "right": # If the move is right
78-
new_state[empty_row][empty_col], new_state[empty_row][empty_col + 1] = new_state[empty_row][empty_col + 1], new_state[empty_row][empty_col]
79-
elif move == "down": # If the move is down
80-
new_state[empty_row][empty_col], new_state[empty_row + 1][empty_col] = new_state[empty_row + 1][empty_col], new_state[empty_row][empty_col]
81-
elif move == "left": # If the move is left
82-
new_state[empty_row][empty_col], new_state[empty_row][empty_col - 1] = new_state[empty_row][empty_col - 1], new_state[empty_row][empty_col]
83-
elif move == "up": # If the move is up
84-
new_state[empty_row][empty_col], new_state[empty_row - 1][empty_col] = new_state[empty_row - 1][empty_col], new_state[empty_row][empty_col]
85-
86-
return new_state # Return the new state
73+
empty_row, empty_col = None, None
74+
for i in range(3): # For each row
75+
for j in range(3): # For each column
76+
if state[i][j] == 0: # If the tile is empty
77+
empty_row, empty_col = i, j # Store the row and column of the empty tile
78+
break # Break out of the loop
79+
80+
# Create a copy of the state
81+
new_state = [row[:] for row in state]
82+
if move == "right": # If the move is right
83+
new_state[empty_row][empty_col], new_state[empty_row][empty_col + 1] = (
84+
new_state[empty_row][empty_col + 1],
85+
new_state[empty_row][empty_col],
86+
)
87+
elif move == "down": # If the move is down
88+
new_state[empty_row][empty_col], new_state[empty_row + 1][empty_col] = (
89+
new_state[empty_row + 1][empty_col],
90+
new_state[empty_row][empty_col],
91+
)
92+
elif move == "left": # If the move is left
93+
new_state[empty_row][empty_col], new_state[empty_row][empty_col - 1] = (
94+
new_state[empty_row][empty_col - 1],
95+
new_state[empty_row][empty_col],
96+
)
97+
elif move == "up": # If the move is up
98+
new_state[empty_row][empty_col], new_state[empty_row - 1][empty_col] = (
99+
new_state[empty_row - 1][empty_col],
100+
new_state[empty_row][empty_col],
101+
)
102+
103+
return new_state # Return the new state
104+
87105

88106
# Define the A* search algorithm
89107
# Input: initial_state (2D list)
90108
# Returns the solution node
91109
def solve_8_puzzle(initial_state):
92-
open_list = [] # Priority queue
93-
closed_set = set() # Set of visited states
94-
initial_node = PuzzleState(initial_state) # Initial state
95-
heapq.heappush(open_list, initial_node) # Add the initial state to the priority queue
96-
97-
# While the priority queue is not empty
98-
while open_list:
99-
current_node = heapq.heappop(open_list) # Pop the node with the lowest f value
100-
if current_node.state == GOAL_STATE: # If the current state is the goal state
101-
return current_node # Return the current node
102-
103-
# Print the current state
104-
closed_set.add(tuple(map(tuple, current_node.state)))
105-
106-
# Print the current state
107-
possible_moves = find_possible_moves(current_node.state)
108-
for move in possible_moves: # For each possible move
109-
new_state = perform_move(current_node.state, move) # Generate a new state
110-
if tuple(map(tuple, new_state)) not in closed_set: # If the new state has not been visited
111-
child_node = PuzzleState(new_state, current_node, move) # Create a child node
112-
child_node.g = current_node.g + 1 # Update the cost from start node to current node
113-
child_node.f = child_node.g + child_node.h # Update the total estimated cost
114-
heapq.heappush(open_list, child_node) # Add the child node to the priority queue
110+
open_list = [] # Priority queue
111+
closed_set = set() # Set of visited states
112+
initial_node = PuzzleState(initial_state) # Initial state
113+
heapq.heappush(open_list, initial_node) # Add the initial state to the priority queue
114+
115+
# While the priority queue is not empty
116+
while open_list:
117+
current_node = heapq.heappop(open_list) # Pop the node with the lowest f value
118+
if current_node.state == GOAL_STATE: # If the current state is the goal state
119+
return current_node # Return the current node
120+
121+
# Print the current state
122+
closed_set.add(tuple(map(tuple, current_node.state)))
123+
124+
# Print the current state
125+
possible_moves = find_possible_moves(current_node.state)
126+
for move in possible_moves: # For each possible move
127+
new_state = perform_move(current_node.state, move) # Generate a new state
128+
if tuple(map(tuple, new_state)) not in closed_set: # If the new state has not been visited
129+
child_node = PuzzleState(new_state, current_node, move) # Create a child node
130+
child_node.g = current_node.g + 1 # Update the cost from start node to current node
131+
child_node.f = child_node.g + child_node.h # Update the total estimated cost
132+
heapq.heappush(open_list, child_node) # Add the child node to the priority queue
133+
115134

116135
# Function to print the solution path
117136
# Input: solution_node (PuzzleState)
118137
# Returns nothing
119138
def print_solution(solution_node):
120-
path = [] # List of states and moves in the solution path
121-
122-
# Print the solution path
123-
while solution_node:
124-
path.append((solution_node.state, solution_node.move)) # Add the state and move to the list
125-
solution_node = solution_node.parent # Go to the parent node
126-
path.reverse() # Reverse the list
127-
128-
# Print the solution path
129-
for state, move in path: # For each state and move
130-
for row in state: # For each row
131-
# change the background color of the terminal
132-
print(f"{BackgroundColors.CYAN}{' '.join(map(str, row))}{Style.RESET_ALL}")
133-
print(f"{BackgroundColors.GREEN}Move: {move}{Style.RESET_ALL}") # Print the move
134-
print(f"{BackgroundColors.YELLOW}------------------{Style.RESET_ALL}")
139+
path = [] # List of states and moves in the solution path
140+
141+
# Print the solution path
142+
while solution_node:
143+
path.append((solution_node.state, solution_node.move)) # Add the state and move to the list
144+
solution_node = solution_node.parent # Go to the parent node
145+
path.reverse() # Reverse the list
146+
147+
# Print the solution path
148+
for state, move in path: # For each state and move
149+
for row in state: # For each row
150+
# change the background color of the terminal
151+
print(f"{BackgroundColors.CYAN}{' '.join(map(str, row))}{Style.RESET_ALL}")
152+
print(f"{BackgroundColors.GREEN}Move: {move}{Style.RESET_ALL}") # Print the move
153+
print(f"{BackgroundColors.YELLOW}------------------{Style.RESET_ALL}")
154+
135155

136156
# This is the main function
137157
def main():
138-
print(f"{BackgroundColors.GREEN}Welcome to the {BackgroundColors.CYAN}8-Puzzle Solver{BackgroundColors.GREEN}!{Style.RESET_ALL}") # Print a welcome message
158+
print(
159+
f"{BackgroundColors.GREEN}Welcome to the {BackgroundColors.CYAN}8-Puzzle Solver{BackgroundColors.GREEN}!{Style.RESET_ALL}"
160+
) # Print a welcome message
161+
162+
# Solve the 8-puzzle problem
163+
solution_node = solve_8_puzzle(INITIAL_STATE)
139164

140-
# Solve the 8-puzzle problem
141-
solution_node = solve_8_puzzle(INITIAL_STATE)
165+
# Print the solution
166+
if solution_node:
167+
print(f"{BackgroundColors.GREEN}Solution found in {solution_node.g} moves!{Style.RESET_ALL}")
168+
print(f"{BackgroundColors.GREEN}Initial state: {Style.RESET_ALL}")
169+
print_solution(solution_node)
170+
else:
171+
print(f"{BackgroundColors.RED}Solution not found!{Style.RESET_ALL}")
142172

143-
# Print the solution
144-
if solution_node:
145-
print(f"{BackgroundColors.GREEN}Solution found in {solution_node.g} moves!{Style.RESET_ALL}")
146-
print(f"{BackgroundColors.GREEN}Initial state: {Style.RESET_ALL}")
147-
print_solution(solution_node)
148-
else:
149-
print(f"{BackgroundColors.RED}Solution not found!{Style.RESET_ALL}")
150173

151174
# This is the standard boilerplate that calls the main() function.
152175
if __name__ == "__main__":
153-
main() # Call the main function
176+
main() # Call the main function

0 commit comments

Comments
 (0)