11from queue import PriorityQueue
2+ from typing import List , Tuple , Optional , Set
23
34
45class 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."""
1222 return self .priority () < other .priority ()
1323
14- def priority (self ):
24+ def priority (self ) -> int :
25+ """A* priority: moves + Manhattan distance."""
1526 return self .moves + self .manhattan ()
1627
17- def manhattan (self ):
28+ def manhattan (self ) -> int :
29+ """Calculate Manhattan distance from current to goal state."""
1830 distance = 0
1931 for i in range (3 ):
2032 for j in range (3 ):
@@ -23,68 +35,66 @@ def manhattan(self):
2335 distance += abs (x - i ) + abs (y - j )
2436 return distance
2537
26- def is_goal (self ):
38+ def is_goal (self ) -> bool :
39+ """Check if current state matches goal."""
2740 return self .board == self .goal
2841
29- def neighbors (self ):
42+ def neighbors (self ) -> List ["PuzzleState" ]:
43+ """Generate all valid neighboring states by moving empty tile (0)."""
3044 neighbors = []
3145 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 )]:
3547 nx , ny = x + dx , y + dy
3648 if 0 <= nx < 3 and 0 <= ny < 3 :
3749 new_board = [row [:] for row in self .board ]
3850 new_board [x ][y ], new_board [nx ][ny ] = new_board [nx ][ny ], new_board [x ][y ]
3951 neighbors .append (
4052 PuzzleState (new_board , self .goal , self .moves + 1 , self )
4153 )
42-
4354 return neighbors
4455
4556
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 )
4867 frontier = PriorityQueue ()
49- frontier .put (initial_state )
50- explored = set ()
68+ frontier .put (initial )
69+ explored : Set [ Tuple [ Tuple [ int , ...], ...]] = set ()
5170
5271 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 ():
6177 if tuple (map (tuple , neighbor .board )) not in explored :
6278 frontier .put (neighbor )
63-
6479 return None
6580
6681
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
6887 steps = []
6988 while solution :
7089 steps .append (solution .board )
7190 solution = solution .previous
72- steps .reverse ()
73-
74- for step in steps :
91+ for step in reversed (steps ):
7592 for row in step :
7693 print (" " .join (map (str , row )))
7794 print ()
7895
7996
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
8499
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