Example #1
0
class GameBoard(object):
    def __init__(self, goal_value, agent=None, graphics=True):
        self.goal = goal_value
        self.graphics = graphics
        self.score = 2
        self._legal_moves = []
        # Initialize state space representation
        self._current_state = {
            (0, 0): 0,
            (1, 0): 0,
            (2, 0): 0,
            (3, 0): 0,
            (0, 1): 0,
            (1, 1): 0,
            (2, 1): 0,
            (3, 1): 0,
            (0, 2): 0,
            (1, 2): 0,
            (2, 2): 0,
            (3, 2): 0,
            (0, 3): 0,
            (1, 3): 0,
            (2, 3): 0,
            (3, 3): 0
        }
        # Initialize Graphics and keybindings
        if self.graphics:
            self._root = Graphics()
        self.__random_cell()  # Make first random cell
        self.print_state()
        if not agent:
            self._root.back_grid.bind('<KeyPress-Up>', self.__up)
            self._root.back_grid.bind('<KeyPress-Down>', self.__down)
            self._root.back_grid.bind('<KeyPress-Left>', self.__left)
            self._root.back_grid.bind('<KeyPress-Right>', self.__right)
        # If using AI, initiate first event
        if agent:
            self.agent = agent
            if self.graphics:
                self._root.back_grid.bind(
                    '<<Action>>', self._agent_move)  # Bind virtual events
                self._root.back_grid.bind('<<Up>>', self.__up)
                self._root.back_grid.bind('<<Down>>', self.__down)
                self._root.back_grid.bind('<<Left>>', self.__left)
                self._root.back_grid.bind('<<Right>>', self.__right)
                self._root.back_grid.after(
                    100, self._agent_move)  # Start agent move loop
            else:
                self.end = False
                while not self.end:
                    self._agent_move(None)
        # Begin main loop of Tkinter, wait for input
        if self.graphics:
            self._root.mainloop()

    def agent(self):
        if agent:
            return True
        else:
            return False

    def get_current_state(self):
        return copy.deepcopy(self._current_state)

    def get_actions(self, state=None):
        up, down, left, right = False, False, False, False
        if not state:
            state = self._current_state
        if self.is_goal_state(state):
            return []
        else:
            move_list = []
            for y in range(4):
                for x in range(4):
                    if state[x, y] != 0:
                        # Look at up possibility
                        if not up and y > 0:
                            up = state[x, y] == state[x,
                                                      y - 1] or state[x, y -
                                                                      1] == 0
                            if up:
                                move_list.append('Up')
                        # Down possibility
                        if not down and y < 3:
                            down = state[x, y] == state[x,
                                                        y + 1] or state[x, y +
                                                                        1] == 0
                            if down:
                                move_list.append('Down')
                        # Left possibility
                        if not left and x > 0:
                            left = state[x, y] == state[x - 1,
                                                        y] or state[x - 1,
                                                                    y] == 0
                            if left:
                                move_list.append('Left')
                        # Right possibility
                        if not right and x < 3:
                            right = state[x, y] == state[x + 1,
                                                         y] or state[x + 1,
                                                                     y] == 0
                            if right:
                                move_list.append('Right')
            return move_list

    def get_next_state(
        self, state, action
    ):  # Returns the state after action but before a random cell has been placed
        temp_state = copy.deepcopy(self._current_state)  # COPY current state
        self._current_state = copy.deepcopy(
            state)  # Change current state to match argument
        if action == 'Up':
            self.__up(None, False)
        elif action == 'Down':
            self.__down(None, False)
        elif action == 'Left':
            self.__left(None, False)
        elif action == 'Right':
            self.__right(None, False)
        # self._root.back_grid.event_generate('<<Fake'+action+'>>')  # Perform action w/o graphics
        out_state = self._current_state  # Save changed state
        self._current_state = temp_state  # Replace previous state as current again
        return out_state  # Output the changed state

    def get_possible_states(
            self, state
    ):  # Returns a list of possible states by filling each empty slot
        temp_state = copy.deepcopy(state)  # Copy state
        out_list = []  # List to output
        for coord, val in temp_state.iteritems():
            if not val:  # For each cell that's 0
                temp_state[coord] = 2  # Change val to 2
                out_list.append(copy.deepcopy(temp_state))  # Append a copy
                temp_state[coord] = 0
        return out_list

    def is_goal_state(self,
                      state):  # Returns whether the passed state is the goal
        for coord in state:
            if state[coord] >= self.goal:
                return True
        return False

    def is_lose_state(self, state):
        if not self.get_actions(state):
            return True
        else:
            return False

    def _agent_move(self, event=None):
        if self.is_goal_state(self._current_state):
            print "Win!"
            self.end = True
            self.print_state()
            return
        if self.is_lose_state(self._current_state):
            print "Lose :("
            self.end = True
            self.print_state()
            return
        action = self.agent.get_action(self)
        if self.graphics:
            self._root.back_grid.after(0, self._root.back_grid.event_generate,
                                       '<<' + action + '>>')
            self._root.back_grid.after(400,
                                       self._root.back_grid.event_generate,
                                       '<<Action>>')  # Get another action
        else:
            if action == 'Up':
                self.__up(None)
            elif action == 'Down':
                self.__down(None)
            elif action == 'Left':
                self.__left(None)
            elif action == 'Right':
                self.__right(None)

    def print_state(self, state=None):
        if not state:
            state = self._current_state
        for y in range(0, 4):
            for x in range(0, 4):
                print state[x, y],
            print ''

    def __up(self, event, real=True):
        if real:
            if 'Up' not in self._legal_moves:
                return
            print 'Move up'
        # Setup loop to scan in correct order, top to bottom
        for y in range(1, 4):
            for x in range(0, 4):
                if self._current_state[x, y]:
                    collide, new_y = self.__collision((x, y), 'Up')
                    if collide:
                        self._current_state[x, new_y] *= 2
                        if real:
                            self.score += self._current_state[x, new_y]
                        self._current_state[x, y] = 0
                    elif y != new_y:
                        self._current_state[x, new_y] = self._current_state[x,
                                                                            y]
                        self._current_state[x, y] = 0
                    if real and self.graphics:
                        self._root.up(x, y, x, new_y, collide,
                                      self._current_state[x, new_y])
        if real:
            self.__random_cell()
            if self.graphics:
                self.print_state()

    def __down(self, event, real=True):
        if real:
            if 'Down' not in self._legal_moves:
                return
            print 'Move down'
        # Setup loop to scan in correct order, bottom to top
        for y in reversed(range(0, 3)):
            for x in range(0, 4):
                if self._current_state[x, y]:
                    collide, new_y = self.__collision((x, y), 'Down')
                    if collide:
                        self._current_state[x, new_y] *= 2
                        if real:
                            self.score += self._current_state[x, new_y]
                        self._current_state[x, y] = 0
                    elif y != new_y:
                        self._current_state[x, new_y] = self._current_state[x,
                                                                            y]
                        self._current_state[x, y] = 0
                    if real and self.graphics:
                        self._root.down(x, y, x, new_y, collide,
                                        self._current_state[x, new_y])
        if real:
            self.__random_cell()
            if self.graphics:
                self.print_state()

    def __left(self, event, real=True):
        if real:
            if 'Left' not in self._legal_moves:
                return
            print 'Move left'
        # Setup loop to scan in correct order, left to right
        for x in range(1, 4):
            for y in range(0, 4):
                if self._current_state[x, y]:
                    collide, new_x = self.__collision((x, y), 'Left')
                    if collide:
                        self._current_state[new_x, y] *= 2
                        if real:
                            self.score += self._current_state[new_x, y]
                        self._current_state[x, y] = 0
                    elif x != new_x:
                        self._current_state[new_x, y] = self._current_state[x,
                                                                            y]
                        self._current_state[x, y] = 0
                    if real and self.graphics:
                        self._root.left(x, y, new_x, y, collide,
                                        self._current_state[new_x, y])
        if real:
            self.__random_cell()
            if self.graphics:
                self.print_state()

    def __right(self, event, real=True):
        if real:
            if 'Right' not in self._legal_moves:
                print self._legal_moves
                return
            print 'Move right'
        # Setup loop to scan in correct order, right to left
        for x in reversed(range(0, 3)):
            for y in range(0, 4):
                if self._current_state[x, y]:
                    collide, new_x = self.__collision((x, y), 'Right')
                    if collide:
                        self._current_state[new_x, y] *= 2
                        if real:
                            self.score += self._current_state[new_x, y]
                        self._current_state[x, y] = 0
                    elif x != new_x:
                        self._current_state[new_x, y] = self._current_state[x,
                                                                            y]
                        self._current_state[x, y] = 0
                    if real and self.graphics:
                        self._root.right(x, y, new_x, y, collide,
                                         self._current_state[new_x, y])
        if real:
            self.__random_cell()
            if self.graphics:
                self.print_state()

    def __collision(self, (x, y), move):
        if move == 'Up':
            for i in reversed(range(0, y)):
                if self._current_state[x, i] == self._current_state[x, y]:
                    return True, i
                elif self._current_state[x, i]:
                    return False, i + 1
            return False, 0
        elif move == 'Down':
            for i in range(y + 1, 4):
                if self._current_state[x, i] == self._current_state[x, y]:
                    return True, i
                elif self._current_state[x, i]:
                    return False, i - 1
            return False, 3
        elif move == 'Left':
            for i in reversed(range(0, x)):
                if self._current_state[i, y] == self._current_state[x, y]:
                    return True, i
                elif self._current_state[i, y]:
                    return False, i + 1
            return False, 0
        elif move == 'Right':
            for i in range(x + 1, 4):
                if self._current_state[i, y] == self._current_state[x, y]:
                    return True, i
                elif self._current_state[i, y]:
                    return False, i - 1
            return False, 3