Home > database >  Trying to get a 2d array from class but instead getting <class '__main__.className'>
Trying to get a 2d array from class but instead getting <class '__main__.className'>

Time:06-25

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 -

  1. Classes would be defined as class State: unless you are inheriting another class
  2. Line 75 has a goal_grid which is not defined anywhere
  3. 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).

  • Related