queue = []
goal_grid = {
1: [0, 0],
2: [0, 1],
3: [0, 2],
4: [1, 0],
5: [1, 1],
6: [1, 2],
7: [2, 0],
8: [2, 2]
}
sol = 0
visited = []
class State:
def __init__(self, state, h=0, parent=None):
self.state = state
self.h = h
self.parent = parent
def getState(self):
return self.state
def getParent(self):
return self.parent
def setH(self, h):
self.h = h
def find_h(state):
grid = state.getState()
h = 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 0:
continue
correct_positon = goal_grid[grid[i][j]]
h = h abs(correct_positon[0]-i) \
abs(correct_positon[1] - j)
return h
def generateChildren(state):
i, j = blank_i, blank_j
# move up
if 0 <= i-1 < 3 and 0 <= j < 3:
new_state = State(state, 0, state)
grid = new_state.state
grid[i-1][j], grid[i][j] = grid[i][j], grid[i-1][j]
new_state.h = find_h(new_state)
queue.append(new_state)
# move down
if 0 <= i 1 < 3 and 0 <= j < 3:
grid = state.getState()
grid[i 1][j], grid[i][j] = grid[i][j], grid[i 1][j]
h = find_h(state)
new_state = State(grid, h, state)
queue.append(new_state)
# move left
if 0 <= i < 3 and 0 <= j-1 < 3:
new_state = State(state, 0, state)
grid = new_state.state
grid[i][j -
1], grid[i][j] = grid[i][j], grid[i][j-1]
new_state.h = find_h(new_state)
queue.append(new_state)
# move right
if 0 <= i-1 < 3 and 0 <= j < 3:
new_state = State(state, 0, state)
grid = new_state.state
grid[i][j
1], grid[i][j] = grid[i][j], grid[i][j 1]
new_state.h = find_h(new_state)
queue.append(new_state)
def goalstate(state, goal):
return state.state == goal
def traceback(state):
while state.getParent():
for items in visited:
if state.getParent() == items.getState():
print(state.state)
state = items
def inVisited(state):
for items in visited:
if items.getState() == state.getState():
return True
return False
def driver():
goal = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
start_grid = [[3, 7, 6], [8, 4, 2], [0, 1, 5]]
start = State(start_grid)
h = find_h(start)
start.setH(h)
global sol
queue.append(start)
while queue:
queue.sort(key=lambda x: x.h)
current_state = State(queue.pop(0))
if goalstate(current_state, goal):
sol = 1
traceback(current_state)
else:
if (inVisited(current_state)):
continue
generateChildren(current_state)
blank_i = int(input("Enter the i of blank: "))
blank_j = int(input("Enter the j of blank: "))
driver()
Here state is a 2d array. So I get the state from the class instance using getState() and save it in some variable. But the type of that variable becomes <class 'main.State'> and I cannot perform any list operations on it. It shows this error. TypeError: 'State' object is not subscriptable. Why is it not returning a list as it was provided before and how to fix this issue?
queue is a global list that is used to keep track of visited states
Another thing is that inside the find_h() function, where the heuristic value is calculated, the grid obtained by doing state.getState() does not give any error and after checking its type is shown as <class 'list'>
This is the whole o/p I get once the code is executed and input is provided i=2,j=0
Traceback (most recent call last):
line 122, in driver()
line 115, in driver generateChildren(current_state)
in generateChildren grid[i-1][j], grid[i][j] = grid[i][j], grid[i-1][j]
TypeError: 'State' object is not subscriptable
CodePudding user response:
You probably forgot to call the method and called the object instead. Did you get 'state' like this:
state_object = State(input_state)
output_state = state_object.getState()
Edit: A few things -
- Classes would be defined as
class State:
unless you are inheriting another class - Line 75 has a
goal_grid
which is not defined anywhere - Are you just calling
driver()
for your code? All i see are functions, what is the first thing you call?
CodePudding user response:
The problem you are facing is fairly simple to counter.
def driver():
goal = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
start_grid = [[3, 7, 6], [8, 4, 2], [0, 1, 5]]
start = State(start_grid)
h = find_h(start)
start.setH(h)
global sol
queue.append(start)
while queue:
queue.sort(key=lambda x: x.h)
current_state = State(queue.pop(0))
if goalstate(current_state, goal):
sol = 1
traceback(current_state)
else:
if (inVisited(current_state)):
continue
generateChildren(current_state)
In the above function, you are creating an object of State
.i.e : current_state
by using the constructor parameter state
as queue.pop(0)
which is, in turn, an object of State class since queue
appends start
which is an object itself.
Hence, current_state
is an object of State
, that contains self.state
, which, again, is an object of State
. So, in simpler terms: current_state.state
is not a list
, but an object of State
class.
A simple solution would be to use: current_state = State(queue.pop(0).getState())
.
Similarly, use: generateChildren(current_state.getState())
as the same problem occurs for generateChildren
function.
Also, as you asked, why does find_h
function works fine, is because in: start = State(start_grid)
, start_grid
is a list and not an object of State
.
Hope you understood the problem as well as the solution approach. Feel free to ask, if any doubts!
CodePudding user response:
I'm pretty sure you're creating your State
objects incorrectly in a few different places. When you call State(something)
, that something is saved in self.state
in the __init__
method. Several parts of your code expect it to be a grid, but you're often calling State(existing_state)
, so it's a State
object instead.
Probably you want to copy the grid state of the previous object, and use that copy as the new object's grid state. If you want to be able to initialize the state with either an existing object, or with a new grid, I'd do something like this:
class State:
def __init__(self, state):
if isinstance(state, State):
state = copy.deepcopy(state.state)
self.state = state
self.h = 0
None of your code uses the parent
attribute for anything, so I omitted it. If you really do need it, you could automatically set it to the state you were passed in, in the one-argument case. Also, it's generally not necessary to write getter and setter methods in your classes. Just have your users directly access your attributes (and use property
objects if you later decide you need a method call to be involved).