Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 9

a program of 8-Puzzle using A* Algorithm in Python.

In an 8 puzzle problem :
Eight (positive integer) tiles are given in a matrix.

 The size(number of rows and columns) of the matrix depends on the number of tiles.
 The number of rows in a matrix is equal to the square root of the number of tiles plus
one (square root(Number plus one)).
 The number of columns in a matrix is equal to the square root of the number of tiles
plus one (square root(Number plus one)).

 
After the above calculations, the number of rows and columns are 3 in a matrix.

 
In this problem:

 A start matrix and goal matrix is given (both the matrix contain 8 elements and a
blank space).
 The task of the program is to take the input as a start matrix and convert it into a goal
matrix by use of various algorithms available such as uninformed and informed search
methods.
 Uninformed means algorithms do not know where to search and search for what but
informed methods have this capability.

 
Uninformed methods:

 Breadth-first search 
 Depth-first search
 Linear search
 Binary search

 
Informed methods:

 A* Algorithms

(Here A* algorithm is used to solve the 8-Puzzle problem)

 
A* Algorithm:
 Is a type of informed search technique.
 For search optimization, cost value or heuristic value is used.
 A function is created to store and keep track of the node level and cost return the sum
of two.

(f = node level  plus cost or heuristics)

 A matrix containing the lowest function value will be processed in the next step.
 This process is repeated until the function value is lowest with a heuristic value equal
to zero.

(Example is given in explanation part)

 
Run the code at the link given in the question to verify the output and code explanation.

 
Note: Examples of the 8-Puzzle problem and its solution using the A* algorithm,
detailed explanation of the code, and output snips are given in the explanation part.

 
Step-by-step explanation
Example of the 8-Puzzle problem and its solution using A* algorithm:

Image transcription text

Start Goal Matrix Matrix 3 2 5 6 - 4 5 6 4 7 8 7 8 level = 1, cost = 3, f = 4 level = 1, cost =


4, f = 5 level = 1, cost = 4, f = 5 level = 1, cost = 4, f = 5 1 2 3 1 2 3 2 3 1 3 5 6 5 6 5 7 6 5 2
6 4 7 8 4 7 4 8 4 7 level = 2, cost = 1, f= 3 level = 2, cost = 4, f =6 level = 2, cost = 3, f = 5
2 3 2 3 1 2 3 4 5 6 5 6 5 6 7 8 4 7 1 1 1 level = 3, cost = 0, f = 3 level = 3, cost = 2, f = 5 1 2
3123456567478

 
The complete code is divided into two classes:

 The eight_puzzle_node class (in the provided reference it is equivalent to Node


class)
 The Eight_Puzzle (in the provided reference it is equivalent to Puzzle class)

 
I used these notations to explain the code so that plagiarism can be avoided.

 
Code explanation:

 
Methods in the class eight_puzzle_node (same as Node in reference)

 
[1]

   def __init__(self,node_data,node_level,f_value):
       self.node_data = node_data
       self.node_level = node_level
       self.f_value = f_value

 
Init method initializes the variables when the object is created and an instance of the
class is represented by a self keyword. 
The variables:

 node_data is the same as the start matrix as shown in the example above.
 node_level  is the same as the level in the above diagram which is used to keep
track of the position of the transformed matrix. Later it can be used to visualize
the working of the algorithm.
 f_value  is the sum of node_level and heuristic or cost. (Matrix with minimum
f_value is selected as the answer.

 
[2]Method for child generation:

   def child_generation(self):
       x,y = self.find_blank_space(self.node_data,'_')
       cord_list = [[x,y-1],[x,y+1],[x-1,y],[x+1,y]]
       n_children = []
       for i in cord_list:
           child = self.shuffle(self.node_data,x,y,i[0],i[1])
           # if the blank space does not have any position value to move, it will
return None but if  #position is not out of limit swapping of coordinates is
done in order to move the blank spaces in
# the given direction
           if child is not None:
               child_node = eight_puzzle_node(child,self.node_level+1,0)
# In the case the child is none, class is called with the level increment.
               n_children.append(child_node)
# n_children is the list
       return n_children

 
The blank space "_" moved in directions (left, right, down and up) to create the child
node.
(in the example above, blank space is at the centre and it moved left, right, up and down
to create 4 matrices in the next level)

 
x , y = self.find_blank_space(self.node_data, '_')

This line uses the function described below to find the x and y coordinates of the blank
space.
Initial blank space position:

cord_list = [[x , y-1] , [x , y+1] , [x-1 , y] , [x+1 , y]]

The coordinate x and y-1 for the movement above of initial blank space position.

The coordinate x and y+1 for the movement to one step down.

 
The coordinate x-1 and y for the left movement.

The coordinate x+1 and y for the movement one step right.

[3] The shuffle method

   def shuffle_method(self,puzzle,x1,y1,x2,y2):
       if x2 >= 0 and x2 < len(self.node_data) and y2 >= 0 and y2 <
len(self.node_data):
           temp_puzzle = []
           temp_puzzle = self.copy_matrix(puzzle)
# copy the of the puzzle created
#swapping
           temp = temp_puzzle[x2][y2]
           temp_puzzle[x2][y2] = temp_puzzle[x1][y1]
           temp_puzzle[x1][y1] = temp
           return temp_puzzle
       else:
           return None

 
Parameters:
Puzzle: is the matrix to be transformed to the goal state
x1, x2, y1 and y2 are coordinates of blank spaces.

 if x2 >= 0 and x2 < len(self.node_data) and y2 >= 0 and y2 < len(self.node_data)

 
To check if the positions are out of limits or not.

temp_puzzle = []
temp_puzzle = self.copy_matrix(puzzle)

 
The copy of the puzzle or matrix is created and stored in the list temp_puzzle. The copy
is created to avoid the modification of the original matrix.
If copying is not added, the system can lose the existence of the original values of the
matrix or puzzle.

 
At the last line of code, it returns None if there are no positions to move blank space.

 
[4] The method copy matrix:

   def copy_matrix(self , mat):


      # list the store the copy of matrix mat
       tem = []
       # loop over the given matrix
       # we need double for loop and nested because mat is two dimensional
       # example [ [], [], [] ]
       for k in mat:
           p = []
# The list p stores rows
# variable s is a single row that is appended to list p
           for s in k:
               p.append(s)
# all the rows are added to them to form a complete matrix.
           tem.append(p)
       return tem

This method is used to create a copy of a matrix.


Argument mat is the matrix whose copy is to be created.

 
[5] The method to find blank spaces:

   def find_blank_space(self , puzzle , t):


# method to find blank space
       for m in range(0 , len(self.node_data)):
           for n in range(0,len(self.node_data)):
               if puzzle[m][n] == t:
                   return m , n
 
Parameters:
Node_list  is the matrix
t  is the blank space character (here "_")
A puzzle is a matrix that is passed. (its blank space coordinates are returned)
Loop will run len(self.node_data) times which is equal to the size of the matrix.

 
Second class methods:

 
[1] init method

   def __init__(self , puzzle_size):


       self.n_size = puzzle_size
       self.open_matrix = []
       self.closed_matrix = []

 
Parameters:
The puzzle is the matrix (start matrix)
Puzzle_size is the size of the start matrix
Two empty lists open_matrix and cloased_matrix are initialized to store the start matrix
and goal matrix.

 
[2] method to input from the user    

def accept_user_puzzle(self):
      #empty list to store user entry
       puzzle = []
       for k in range(0,self.n_size):
# split methos is used to split input string
# example "1 2 3 4".split() gives ["1", "2", "3", "4"]
           tem = input().split(" ")
           puzzle.append(tem)
       return puzzle

 
This method is created to take input from the user.
The input provided by the user is string and split() method is used to convert the input
string to the list.

 
[3] Method to calculate the f value

   def calc_fvalue(self,start_matrix,goal_matrix):
       return self.hueristic(start_matrix.node_data,goal_matrix)
+start_matrix.node_level

Parameters:
Strat_matrix is the matrix given by the user
Goal_matrx is the final matrix that is obtained from the algorithm
F value is the sum of the cost and level of the node

 
[4] Method to calculate the cost which is also called heuristics.

   def hueristic(self, start_matrix , goal_matrix):


      # cost is initialized to zero
       cost = 0
      # double for loops are used because matrices are two dimensional.
       for k in range(0,self.n_size):
           for l in range(0,self.n_size):
               if start_matrix[k][l] != goal_matrix[k][l] and start_matrix[k]
[l] != '_':
                   cost += 1
       return cost

 
This method calculates the cost at different levels.
Parameters:
Start_matrix
Goal_matrix

if start_matrix[k][l] != goal_matrix[k][l] and start_matrix[k][l] != '_':

 
This condition is checking that the start_matrix and goal_matrix are equal or not, and
start_matrix element is blank space or not.
If the above condition holds, the cost is incremented by one.
This process will stop if the algorithm is able to convert start_matrix to goal_matrix

 
[5] The final method to combine all the processes

   def final_process(self):
       # take input from user
       print("Input the start puzzle \n")
       start_matrix = self.accept_user_puzzle()
# the function accepts the input from user and converts it into list.
       print("input the goal_puzzle \n")       
       goal_matrix = self.accept_user_puzzle()
       start_matrix = eight_puzzle_node(start_matrix,0,0)
       # call the class with parameters as start_matrix entered by the user
# calculate the f value
       start_matrix.f_value = self.calc_fvalue(start_matrix,goal_matrix)
# append the start_matrix to the open_matrix
       self.open_matrix.append(start_matrix)
 
       while True:
           cur = self.open[0]
           print("")
           print("  | ")
           print("  | ")
           print(" \\\'/ \n")
          # above three lines are used to print arrows in the output.
           for p in cur.node_data:
               for q in p:
                   print(q,end=" ")
               print("")
           # code will stop if algorithm produced or transformed start_matrix to
goal matrix
           # given if block compares start_matrix and goal_matrix in order to
check if they are equal #to stop the code execution.
           if(self.hueristic(cur.node_data,goal_matrix) == 0):
               break
# if above condition does not hold , next lines will be executed.
           for i in cur.child_generation():
               i.f_value = self.calc_fvalue(i,goal_matrix)
               self.open.append(i)
           self.closed.append(cur)
           del self.open[0]
           # the list is sorted using f value (level plus cost)
           self.open.sort(key = lambda x:x.f_value,reverse=False)

 
[6] driver code

puzzle = Eight_Puzzle(3)
puzzle.final_process()

 
The object of the class  Eight_Puzzle is created.
        

 
Output snip:
[1]

 
[2]

You might also like