1
1
from queue import PriorityQueue
2
+ from typing import List , Tuple , Optional , Set
2
3
3
4
4
5
class PuzzleState :
5
- def __init__ (self , board , goal , moves = 0 , previous = None ):
6
- self .board = board
7
- self .goal = goal
8
- self .moves = moves
9
- self .previous = previous
10
-
11
- def __lt__ (self , other ):
6
+ """Represents a state in 8-puzzle solving with A* algorithm."""
7
+
8
+ def __init__ (
9
+ self ,
10
+ board : List [List [int ]],
11
+ goal : List [List [int ]],
12
+ moves : int = 0 ,
13
+ previous : Optional ["PuzzleState" ] = None ,
14
+ ) -> None :
15
+ self .board = board # Current 3x3 board configuration
16
+ self .goal = goal # Target 3x3 configuration
17
+ self .moves = moves # Number of moves taken to reach here
18
+ self .previous = previous # Previous state in solution path
19
+
20
+ def __lt__ (self , other : "PuzzleState" ) -> bool :
21
+ """For PriorityQueue ordering: compare priorities."""
12
22
return self .priority () < other .priority ()
13
23
14
- def priority (self ):
24
+ def priority (self ) -> int :
25
+ """A* priority: moves + Manhattan distance."""
15
26
return self .moves + self .manhattan ()
16
27
17
- def manhattan (self ):
28
+ def manhattan (self ) -> int :
29
+ """Calculate Manhattan distance from current to goal state."""
18
30
distance = 0
19
31
for i in range (3 ):
20
32
for j in range (3 ):
@@ -23,68 +35,66 @@ def manhattan(self):
23
35
distance += abs (x - i ) + abs (y - j )
24
36
return distance
25
37
26
- def is_goal (self ):
38
+ def is_goal (self ) -> bool :
39
+ """Check if current state matches goal."""
27
40
return self .board == self .goal
28
41
29
- def neighbors (self ):
42
+ def neighbors (self ) -> List ["PuzzleState" ]:
43
+ """Generate all valid neighboring states by moving empty tile (0)."""
30
44
neighbors = []
31
45
x , y = next ((i , j ) for i in range (3 ) for j in range (3 ) if self .board [i ][j ] == 0 )
32
- directions = [(- 1 , 0 ), (1 , 0 ), (0 , - 1 ), (0 , 1 )]
33
-
34
- for dx , dy in directions :
46
+ for dx , dy in [(- 1 , 0 ), (1 , 0 ), (0 , - 1 ), (0 , 1 )]:
35
47
nx , ny = x + dx , y + dy
36
48
if 0 <= nx < 3 and 0 <= ny < 3 :
37
49
new_board = [row [:] for row in self .board ]
38
50
new_board [x ][y ], new_board [nx ][ny ] = new_board [nx ][ny ], new_board [x ][y ]
39
51
neighbors .append (
40
52
PuzzleState (new_board , self .goal , self .moves + 1 , self )
41
53
)
42
-
43
54
return neighbors
44
55
45
56
46
- def solve_puzzle (initial_board , goal_board ):
47
- initial_state = PuzzleState (initial_board , goal_board )
57
+ def solve_puzzle (
58
+ initial_board : List [List [int ]], goal_board : List [List [int ]]
59
+ ) -> Optional [PuzzleState ]:
60
+ """
61
+ Solve 8-puzzle using A* algorithm.
62
+
63
+ >>> solve_puzzle([[1,2,3],[4,0,5],[7,8,6]], [[1,2,3],[4,5,6],[7,8,0]]) is not None
64
+ True
65
+ """
66
+ initial = PuzzleState (initial_board , goal_board )
48
67
frontier = PriorityQueue ()
49
- frontier .put (initial_state )
50
- explored = set ()
68
+ frontier .put (initial )
69
+ explored : Set [ Tuple [ Tuple [ int , ...], ...]] = set ()
51
70
52
71
while not frontier .empty ():
53
- current_state = frontier .get ()
54
-
55
- if current_state .is_goal ():
56
- return current_state
57
-
58
- explored .add (tuple (map (tuple , current_state .board )))
59
-
60
- for neighbor in current_state .neighbors ():
72
+ current = frontier .get ()
73
+ if current .is_goal ():
74
+ return current
75
+ explored .add (tuple (map (tuple , current .board )))
76
+ for neighbor in current .neighbors ():
61
77
if tuple (map (tuple , neighbor .board )) not in explored :
62
78
frontier .put (neighbor )
63
-
64
79
return None
65
80
66
81
67
- def print_solution (solution ):
82
+ def print_solution (solution : Optional [PuzzleState ]) -> None :
83
+ """Print step-by-step solution from initial to goal state."""
84
+ if not solution :
85
+ print ("No solution found." )
86
+ return
68
87
steps = []
69
88
while solution :
70
89
steps .append (solution .board )
71
90
solution = solution .previous
72
- steps .reverse ()
73
-
74
- for step in steps :
91
+ for step in reversed (steps ):
75
92
for row in step :
76
93
print (" " .join (map (str , row )))
77
94
print ()
78
95
79
96
80
- # Example usage
81
- initial_board = [[1 , 2 , 3 ], [4 , 0 , 5 ], [7 , 8 , 6 ]]
82
-
83
- goal_board = [[1 , 2 , 3 ], [4 , 5 , 6 ], [7 , 8 , 0 ]]
97
+ if __name__ == "__main__" :
98
+ import doctest
84
99
85
- solution = solve_puzzle (initial_board , goal_board )
86
- if solution :
87
- print ("Solution found:" )
88
- print_solution (solution )
89
- else :
90
- print ("No solution found." )
100
+ doctest .testmod (verbose = True )
0 commit comments