I am trying to solve 8 puzzle problem in python given here in this assignment -https://www.cs.princeton.edu/courses/archive/fall12/cos226/assignments/8puzzle.html
My goal state is a little different from what is mentioned in the assignment -
#GOAL STATE
goal_state = [[0,1,2],[3,4,5],[6,7,8]]
The buggy part, it seems, is the isSolvable function. It is implemented correctly but while testing the board, it considers the goal state to be the one in which relative order is maintained and blank can be anywhere. So it might be the case that a board is solvable but it might not lead to the current defined goal state. So I am unable to think of a method in which I can test for all the possible goal states while running the solver function *
Also, my solver function was wrongly implemented. I was only considering the neighbor which had the minimum manhattan value and when I was hitting a dead end, I was not considering other states. This can be done by using a priority queue. I am not exactly sure as to how to proceed to implement it. I have written a part of it(see below) which is also kind of wrong as I not pushing the parent into the heap. Kindly provide me guidance for that.
Here is my complete code -
https://pastebin.com/q7sAKS6a
Updated code with incomplete solver function -
https://pastebin.com/n4CcQaks
I have used manhattan values to calculate heuristic values and hamming value to break the tie.
my isSolvable function, manhattan function and solver function:
isSolvable function -
#Conditions for unsolvability -->
#https://www.geeksforgeeks.org/check-instance-8-puzzle-solvable/
def isSolvable(self):
self.one_d_array = []
for i in range(0,len(self.board)):
for j in range(0,len(self.board)):
self.one_d_array.append(self.board[i][j])
inv_count = 0
for i in range(0,len(self.one_d_array)-1):
for j in range(i+1, len(self.one_d_array)):
if (self.one_d_array[i] != 0 and self.one_d_array[j] != 0 and self.one_d_array[i] > self.one_d_array[j]):
inv_count = inv_count + 1
if(inv_count % 2 == 0):
print("board is solvable")
return True
else:
print("board is not solvable")
return False
Manhattan function
def manhattan_value(self,data=None):
manhattan_distance = 0
for i in range(0,len(data)):
for j in range(0,len(data)):
if(data[i][j] != self.goal_state[i][j] and data[i][j] != 0):
#correct position of the element
x_goal , y_goal = divmod(data[i][j],3)
manhattan_distance = manhattan_distance + abs(i-x_goal) + abs(j-y_goal)
return manhattan_distance
Updated Solver function
#implement A* algorithm
def solver(self):
moves = 0
heuristic_value = []
prev_state = []
curr_state = self.board
output = []
heap_array = []
store_manhattan_values = []
if(curr_state == self.goal_state):
print("goal state reached!")
print(curr_state)
print("number of moves required to reach goal state --> {}".format(moves))
else:
while(True):
min_heuristic_value = 99999999999
min_pos = None
moves = moves + 1
output = self.get_neighbours(curr_state)
for i in range(len(output)):
store_manhattan_values.append([self.manhattan_value(output[i]),i])
#print(store_manhattan_values)
for i in range(len(store_manhattan_values)):
heapq.heappush(heap_array,store_manhattan_values[i])
#print(heap_array)
#print(heapq.heappop(heap_array)[1])
#if(moves > 1):
# return
return
Please refer to the PASTEBIN link for complete code and all the references (https://pastebin.com/r7TngdFc).
Updated code with incomplete solver function -
https://pastebin.com/n4CcQaks
In the given link for my code (based on my tests and debugging so far) -
These functions are working correctly - manhatten_value, hamming_value, append_in_list, get_neighbours
What does these functions do -
isSolvable - tells if the board can be solved or not
manhattan_value - calculates the manhattan value of the board passed to it.
hamming_value - calculates the hamming value of the board passed to it.
append_in_list - helper function for getting neighbours. It swaps values then save the resultant state in an array and then reswaps them to return to original position for further swapping and getting other possible states.
get_neighbours - gets all the possible neighbors which can be formed by swapping places with blank element(0 element).
solver - implements the A* algorithm
I am unable to find my mistake. Kindly guide me in this problem. Thank you in advance for your help!
I am apologizing in advance as I am unable to produce a minimal version of my code for this problem. I can not think of any way to use all the functions and produce a minimal version of the code.
(Note, this answer is different than the earlier revision about which many of the comments below were relating to.)
I don't see how the current code implements a queue. It seems like the while loop in the solver picks one new board state each time from a list of possible moves, then considers the next list generated by this new board state.
On the other hand, a priority queue, from what I understand, would have all the (valid) neighbours from the current board state inserted into it and prioritised such that the next chosen board state to be removed from the queue and examined will be the one with highest priority.
(To be completely sure in debugging, I might add a memoisation to detect if the code ends up also revisiting board states -- ah, on second thought, I believe the stipulation in the assignment description that the number of current moves be added to the priority assignment would rule out the same board state being revisited if the priority queue is correctly observed, so memoisation may not be needed.)
I'm trying to display data from a csv in a text table. I've got to the point where it displays everything that I need, however the table width still has to be set, meaning if the data is longer than the number set then issues begin.
I currently print the table using .format to sort out formatting, is there a way to set the width of the data to a variable that is dependant on the length of the longest piece of data?
for i in range(len(list_l)):
if i == 0:
print(h_dashes)
print('{:^1s}{:^26s}{:^1s}{:^26s}{:^1s}{:^26s}{:^1s}{:^26s}{:^1s}'.format('|', (list_l[i][0].upper()),'|', (list_l[i][1].upper()),'|',(list_l[i][2].upper()),'|', (list_l[i][3].upper()),'|'))
print(h_dashes)
else:
print('{:^1s}{:^26s}{:^1s}{:^26s}{:^1s}{:^26s}{:^1s}{:^26s}{:^1s}'.format('|', list_l[i][0], '|', list_l[i][1], '|', list_l[i][2],'|', list_l[i][3],'|'))
I realise that the code is far from perfect, however I'm still a newbie so it's piecemeal from various tutorials
You can actually use a two-pass approach to first get the correct lengths. As per your example with four fields per line, the following shows the basic idea you can use.
What follows is an example of the two-pass approach, first to get the maximum lengths for each field, the other to do what you're currently doing (with the calculated rather than fixed lengths):
# Can set MINIMUM lengths here if desired, eg: lengths = [10, 0, 41, 7]
lengths = [0] * 4
fmtstr = None
for pass in range(2):
for i in range(len(list_l)):
if pass == 0:
# First pass sets lengths as per data.
for field in range(4):
lengths[field] = max(lengths[field], len(list_l[i][field])
else:
# Second pass prints the data.
# First, set format string if not yet set.
if fmtstr is None:
fmtstr = '|'
for item in lengths:
fmtstr += '{:^%ds}|' % (item)
# Now print item (and header stuff if first item).
if i == 0: print(h_dashes)
print(fmtstr.format(list_l[i][0].upper(), list_l[i][1].upper(), list_l[i][2].upper(), list_l[i][3].upper()))
if i == 0: print(h_dashes)
The construction of the format string is done the first time you process an item in pass two.
It does so by taking a collection like [31,41,59] and giving you the string:
|{:^31s}|{:^41s}|{:^59s}|
There's little point using all those {:^1s} format specifiers when the | is not actually a varying item - you may as well code it directly into the format string.
when I run this program, sometimes I receive an error.This error however is not possible as I am using an 8x8 grid and I limit the inputs so that they can only be numbers from 0-7, to obey the fact that list indexes start at 0.
The user must input coordinates (1-8),(A-H) and the program will check to see if those coordinates are correct, by systematically going through the CompShips list and repeatedly comparing those coordinates to ones given by the user. If the cords match, then a message will appear and a "Z" will change to an "X" on those coordinates, indicating a HIT. If the guess does not match, a "Z" will change to an "M" on those coordinates indicating a MISS.
CompShips=[[1,0],[1,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[5,4],[5,5],[5,6],[5,7],[1,7],[2,7],[3,7],[4,7],[5,7]]
FRow1=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow2=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow3=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow4=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow5=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow6=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow7=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow8=["Z","Z","Z","Z","Z","Z","Z","Z",]
def PrintFireBoard():
print(Index)
print(FRow1)
print(FRow2)
print(FRow3)
print(FRow4)
print(FRow5)
print(FRow6)
print(FRow7)
print(FRow8)
FireBoard=[FRow1,FRow2,FRow3,FRow4,FRow5,FRow6,FRow7,FRow8]
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
#As Lists start at 0
FireRow=int(FireRow)-1
if FireIndex==("A"):
FireIndex=0
elif FireIndex==("B"):
FireIndex=1
elif FireIndex==("C"):
FireIndex=2
elif FireIndex==("D"):
FireIndex=3
elif FireIndex==("E"):
FireIndex=4
elif FireIndex==("F"):
FireIndex=5
elif FireIndex==("G"):
FireIndex=6
elif FireIndex==("H"):
FireIndex=7
Guess=[FireRow,FireIndex]
#Check To See If Correct
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow)
print(FireIndex)
FireBoard[[FireRow][FireIndex]]=("H")
PrintFireBoard()
else:
print("MISS")
print(FireRow)
print(FireIndex)
FireBoard[[FireRow][FireIndex]]=("M")
PrintFireBoard()
I receive the error:
IndexError: string index out of range
Looks like these two lines
FireBoard[[FireRow][FireIndex]]=("H")
FireBoard[[FireRow][FireIndex]]=("M")
should be
FireBoard[FireRow][FireIndex]="H"
FireBoard[FireRow][FireIndex]="M"
Explanation: In your old code, FireBoard[[FireRow][FireIndex]]=("H")
[FireRow][FireIndex] means, given a list [FireRow] (which contains just one element), get the FireIndex-th element. This is not what you're trying to do.
For example [3][0] returns 3, and [3][1] gives IndexError.
Take a look at How to define a two-dimensional array in Python
Also note that ("H") is the same as the string "H". There is no need to add parentheses.
Here is a much cleaner code!
CompShips=[[1,0],[1,1],[2,2],[2,3],
[2,4],[3,0],[3,1],[3,2],
[5,4],[5,5],[5,6],[5,7],
[1,7],[2,7],[3,7],[4,7],
[5,7]]
FRow=[["Z"]*8]*8 #1 More Pythonic
def PrintFireBoard():
#print(Index)
for i in range(0,8):
print(FRow[i])
FireBoard=FRow[:] #NOTE THIS ONE!!!
mydict = {}
for i,key in enumerate(["A","B","C","D","E","F","G","H"]): #2 More Pythonic
mydict[key] = i
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
FireRow=int(FireRow)-1
FireIndex = mydict[FireIndex]
Guess=[FireRow,FireIndex]
print(Guess)
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow,FireIndex)
FireBoard[FireRow][FireIndex]="H" #3 your problem here
PrintFireBoard()
else:
print("MISS")
print(FireRow,FireIndex)
FireBoard[FireRow][FireIndex]="M"
PrintFireBoard()
1) As explained in the comments that's just a more nicer way to create a list of lists!. Remember DRY principle! Do Not Repeat yourself!
2) Instead of having all that if else to convert the 'A' to 0. You can use a dictionary lookup instead!
3) Your problem seems to be here! correct this to FireBoard[FireRow][FireIndex]="H"
PS: NOTE THIS ONE!!!: I'm not just making FireBoard as an alias to FRow! I'm copying it into a FireBoard as a new list! There's a subtle difference read about it here. I'm doing this incase you don't want your original FRow list to be modified!
The indentation in your question was off. I think that all the code from
Guess=[FireRow,FireIndex]
until the end should be preceded by 4 spaces.
I've removed print(Index) since it was not defined.
To access FireBoard use:
FireBoard[FireRow][FireIndex]
Instead of
FireBoard[[FireRow][FireIndex]]
This should be working
CompShips=[[1,0],[1,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[5,4],
[5,5],[5,6],[5,7],[1,7],[2,7],[3,7],[4,7],[5,7]]
FRow1=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow2=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow3=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow4=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow5=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow6=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow7=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow8=["Z","Z","Z","Z","Z","Z","Z","Z",]
def PrintFireBoard():
print(FRow1)
print(FRow2)
print(FRow3)
print(FRow4)
print(FRow5)
print(FRow6)
print(FRow7)
print(FRow8)
FireBoard=[FRow1,FRow2,FRow3,FRow4,FRow5,FRow6,FRow7,FRow8]
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
#As Lists start at 0
FireRow=int(FireRow)-1
if FireIndex==("A"):
FireIndex=0
elif FireIndex==("B"):
FireIndex=1
elif FireIndex==("C"):
FireIndex=2
elif FireIndex==("D"):
FireIndex=3
elif FireIndex==("E"):
FireIndex=4
elif FireIndex==("F"):
FireIndex=5
elif FireIndex==("G"):
FireIndex=6
elif FireIndex==("H"):
FireIndex=7
Guess=[FireRow,FireIndex]
#Check To See If Correct
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow)
print(FireIndex)
FireBoard[FireRow][FireIndex]=("H")
PrintFireBoard()
else:
print("MISS")
print(FireRow)
print(FireIndex)
FireBoard[FireRow][FireIndex]=("M")
PrintFireBoard()