Search code examples
pythonlistpython-class

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


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


Solution

  • 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!