예제 #1
0
    def __init__(self, grid_size=[30,30], unit_size=10, unit_gap=1, snake_size=3, n_snakes=1, n_foods=1, random_init=True):

        assert n_snakes < grid_size[0]//3
        assert n_snakes < 25
        assert snake_size < grid_size[1]//2
        assert unit_gap >= 0 and unit_gap < unit_size

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)
        self.score = 0

        self.snakes = []
        self.dead_snakes = []
        for i in range(1,n_snakes+1):
            #start_coord = [i*grid_size[0]//(n_snakes+1), snake_size+1]
            start_coord = [random.randint(0, grid_size[0]-1), random.randint(0, grid_size[1]-1)]
            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i*10, 0]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        if not random_init:
            for i in range(2,n_foods+2):
                start_coord = [i*grid_size[0]//(n_foods+3), grid_size[1]-5]
                self.grid.place_food(start_coord)
        else:
            for i in range(n_foods):
                self.grid.new_food()
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 n_snakes=1,
                 n_foods=1,
                 random_init=True,
                 start_coord=None,
                 food_pos=None):

        assert n_snakes < grid_size[0] // 3
        assert n_snakes < 25
        assert snake_size < grid_size[1] // 2
        assert unit_gap >= 0 and unit_gap < unit_size

        if food_pos is not None:
            assert food_pos[0] < grid_size[
                0]  # added 20190519 in mod19 version
            assert food_pos[1] < grid_size[
                1]  # added 20190519 in mod19 version

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)

        self.snakes = []
        self.dead_snakes = []
        for i in range(1, n_snakes + 1):

            if start_coord is None:
                start_coord = [
                    i * grid_size[0] // (n_snakes + 1), snake_size + 1
                ]

            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i * 10, 0]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        if food_pos is not None:
            self.grid.place_food(food_pos)  # added 20190519 in mod19 version
        else:
            if not random_init:
                for i in range(2, n_foods + 2):
                    start_coord = [
                        i * grid_size[0] // (n_foods + 3), grid_size[1] - 5
                    ]
                    self.grid.place_food(start_coord)
            else:
                for i in range(n_foods):
                    self.grid.new_food()
예제 #3
0
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 n_snakes=1,
                 n_foods=1,
                 random_init=True,
                 wall=False):

        assert n_snakes < grid_size[0] // 3
        assert n_snakes < 25
        assert snake_size < grid_size[1] // 2
        assert unit_gap >= 0 and unit_gap < unit_size

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)

        self.empty = []
        self.douse = []
        self.r = [0., 0., 0., 0.]

        self.snakes = []
        self.dead_snakes = []
        for i in range(1, n_snakes + 1):
            start_coord = [i * grid_size[0] // (n_snakes + 1), snake_size + 1]
            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i * 10, 0]
            color = [0, 0, 255]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        # if wall:
        #     self.grid.create_wall()
        #     self.grid.create_wall()
        #     self.grid.create_wall()

        # if not random_init:
        #     for i in range(2,n_foods+2):
        #         start_coord = [i*grid_size[0]//(n_foods+3), grid_size[1]-5]
        #         self.grid.place_food(start_coord)
        # else:
        # for i in range(n_foods):
        #     self.grid.new_food(i)

        self.grid.init_fire()
예제 #4
0
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 random_init=True):

        assert snake_size < grid_size[1] // 2
        assert 0 <= unit_gap < unit_size

        self.grid = Grid(grid_size, unit_size, unit_gap)

        start_coord = [grid_size[0] // 2, snake_size + 1]
        self.snake = Snake(start_coord, snake_size)
        self.dead_snake = None
        color = [self.grid.HEAD_COLOR[0], 0, 0]
        self.snake.head_color = color
        self.grid.draw_snake(self.snake, color)

        if not random_init:
            start_coord = [grid_size[0] // 2, grid_size[1] - 5]
            self.grid.place_food(start_coord)
        else:
            self.grid.new_food()
예제 #5
0
class Controller():
    """
    This class combines the Snake, Food, and Grid classes to handle the game logic.
    """

    def __init__(self, grid_size=[30,30], unit_size=10, unit_gap=1, snake_size=3, n_snakes=1, n_foods=1, random_init=True):

        assert n_snakes < grid_size[0]//3
        assert n_snakes < 25
        assert snake_size < grid_size[1]//2
        assert unit_gap >= 0 and unit_gap < unit_size

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)
        self.score = 0

        self.snakes = []
        self.dead_snakes = []
        for i in range(1,n_snakes+1):
            #start_coord = [i*grid_size[0]//(n_snakes+1), snake_size+1]
            start_coord = [random.randint(0, grid_size[0]-1), random.randint(0, grid_size[1]-1)]
            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i*10, 0]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        if not random_init:
            for i in range(2,n_foods+2):
                start_coord = [i*grid_size[0]//(n_foods+3), grid_size[1]-5]
                self.grid.place_food(start_coord)
        else:
            for i in range(n_foods):
                self.grid.new_food()

    def move_snake(self, direction, snake_idx):
        """
        Moves the specified snake according to the game's rules dependent on the direction.
        Does not draw head and does not check for reward scenarios. See move_result for these
        functionalities.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return

        # Cover old head position with body
        self.grid.cover(snake.head, self.grid.BODY_COLOR)
        # Erase tail without popping so as to redraw if food eaten
        self.grid.erase(snake.body[0])
        # Find and set next head position conditioned on direction
        snake.action(direction)

    def move_result(self, direction, snake_idx=0):
        """
        Checks for food and death collisions after moving snake. Draws head of snake if
        no death scenarios.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return 0

        # Check for death of snake
        if self.grid.check_death(snake.head):
            self.dead_snakes[snake_idx] = self.snakes[snake_idx]
            self.snakes[snake_idx] = None
            self.grid.cover(snake.head, snake.head_color) # Avoid miscount of grid.open_space
            self.grid.connect(snake.body.popleft(), snake.body[0], self.grid.SPACE_COLOR)
            reward = -1
        # Check for reward
        elif self.grid.food_space(snake.head):
            self.score += 1
            self.grid.draw(snake.body[0], self.grid.BODY_COLOR) # Redraw tail
            self.grid.connect(snake.body[0], snake.body[1], self.grid.BODY_COLOR)
            self.grid.cover(snake.head, snake.head_color) # Avoid miscount of grid.open_space
            reward = 1
            self.grid.foodLocations.remove(tuple(snake.head))
            self.grid.new_food()
        else:
            reward = -.1
            food = self.grid.foodLocations[0]
            # distToFood = abs(snake.head[0] - food[0]) + abs(snake.head[1] - food[1])
            # reward = -.2*distToFood
            empty_coord = snake.body.popleft()
            self.grid.connect(empty_coord, snake.body[0], self.grid.SPACE_COLOR)
            self.grid.draw(snake.head, snake.head_color)

        self.grid.connect(snake.body[-1], snake.head, self.grid.BODY_COLOR)

        return reward

    def kill_snake(self, snake_idx):
        """
        Deletes snake from game and subtracts from the snake_count 
        """
        
        assert self.dead_snakes[snake_idx] is not None
        self.grid.erase(self.dead_snakes[snake_idx].head)
        self.grid.erase_snake_body(self.dead_snakes[snake_idx])
        self.dead_snakes[snake_idx] = None
        self.snakes_remaining -= 1

    def generateObservationTuple(self, direction):
        #Observation = [danger up, danger left, danger right, snake up, snake down, snake left,
        # snake right, (food_up, food_down)]
        observation = []
        if self.snakes and self.snakes[0]:
            directionMap = {0:[0, 3, 1], 1:[1, 0, 2], 2:[2, 1, 3], 3:[3, 2, 0]}
            for relativeDirec in directionMap[self.snakes[0].direction]:
                    observation.append(int(self.grid.check_death(self.snakes[0].step(self.snakes[0].head, relativeDirec))))

            for direction in range(4):
                if self.snakes[0].direction == direction:
                    observation.append(1)
                else:
                    observation.append(0)

            snake_to_fruit = np.sign([self.snakes[0].head[0] - self.grid.foodLocations[0][0], self.snakes[0].head[1] - self.grid.foodLocations[0][1]])
            if snake_to_fruit[0] == 1:
                observation = observation + [0, 1]
            elif snake_to_fruit[0] == -1:
                observation = observation + [1, 0]
            else:
                observation = observation + [0, 0]
            if snake_to_fruit[1] == 1:
                observation = observation + [0, 1]
            elif snake_to_fruit[1] == -1:
                observation = observation + [1, 0]
            else:
                observation = observation + [0, 0]
            #observation.append(tuple(snake_to_fruit))
        else:
            #observation = [0] * 7 + [(0, 0)]
            observation = [0] * 11
        
        # if self.snakes and self.snakes[0]:
        #     head = self.snakes[0].head
        #     last_tail = self.snakes[0].body[-1]
        #     food = self.grid.foodLocations[0]
        #     return ((last_tail[0] - head[0], last_tail[1] - head[1]), (food[0] - head[0], food[1] - head[1]))
        # else:
        #     return ((0, 0), (0, 0))
        return observation

    def step(self, directions):
        """
        Takes an action for each snake in the specified direction and collects their rewards
        and dones.

        directions - tuple, list, or ndarray of directions corresponding to each snake.
        """

        # Ensure no more play until reset
        if self.snakes_remaining < 1 or self.grid.open_space < 1:
            if type(directions) == type(int()) or len(directions) is 1:
                return self.grid.grid.copy(), 0, True, {"snakes_remaining":self.snakes_remaining}
            else:
                return self.grid.grid.copy(), [0]*len(directions), True, {"snakes_remaining":self.snakes_remaining}

        rewards = []

        if type(directions) == type(int()):
            directions = [directions]

        for i, direction in enumerate(directions):
            if self.snakes[i] is None and self.dead_snakes[i] is not None:
                self.kill_snake(i)
            self.move_snake(direction,i)
            rewards.append(self.move_result(direction, i))

        done = self.snakes_remaining < 1 or self.grid.open_space < 1
        # #Observation = [danger left, danger right, danger straight, snake up, snake down, snake left,
        # # snake right, (food_up, food_down)]
        # observation = []
        # if self.snakes and self.snakes[0]:
        #     for direction in range(4):
        #         if np.abs(self.snakes[0].direction-direction) != 2:
        #             observation.append(int(self.grid.off_grid(self.snakes[0].step(self.snakes[0].head, direction))))
        #         if self.snakes[0].direction == direction:
        #             observation.append(1)
        #         else:
        #             observation.append(0)
        #     snake_to_fruit = np.sign([self.snakes[0].head[0] - self.grid.foodLocations[0][0], self.snakes[0].head[1] - self.grid.foodLocations[0][1]])
        #     observation.append(tuple(snake_to_fruit))
        # else:
        #     observation = [0] * 7 + [(0, 0)]
        #observation = [tuple(self.snakes[0].head) if self.snakes and self.snakes[0] else None, self.grid.foodLocations[0]]
        observation = self.generateObservationTuple(direction)
        if len(rewards) is 1:
            #used to be self.grid.grid.copy()
            return tuple(observation), rewards[0], done, {"snakes_remaining":self.snakes_remaining}
        else:
            return tuple(observation), rewards, done, {"snakes_remaining":self.snakes_remaining}
예제 #6
0
class Controller:
    """
    This class combines the Snake, Food, and Grid classes to handle the game logic.
    """
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 random_init=True):

        assert snake_size < grid_size[1] // 2
        assert 0 <= unit_gap < unit_size

        self.grid = Grid(grid_size, unit_size, unit_gap)

        start_coord = [grid_size[0] // 2, snake_size + 1]
        self.snake = Snake(start_coord, snake_size)
        self.dead_snake = None
        color = [self.grid.HEAD_COLOR[0], 0, 0]
        self.snake.head_color = color
        self.grid.draw_snake(self.snake, color)

        if not random_init:
            start_coord = [grid_size[0] // 2, grid_size[1] - 5]
            self.grid.place_food(start_coord)
        else:
            self.grid.new_food()

    def move_snake(self, direction):
        """
        Moves the snake according to the game's rules dependent on the direction.
        Does not draw head and does not check for reward scenarios. See move_result for these
        functionalities.
        """

        snake = self.snake
        if snake is None:
            return

        # Cover old head position with body
        self.grid.cover(snake.head, self.grid.BODY_COLOR)
        # Erase tail without popping so as to redraw if food eaten
        self.grid.erase(snake.body[0])
        # Find and set next head position conditioned on direction
        snake.action(direction)

    def move_result(self):
        """
        Checks for food and death collisions after moving snake. Draws head of snake if
        no death scenarios.
        """

        snake = self.snake
        if snake is None:
            return 0

        # Check for death of snake
        if self.grid.check_death(snake.head):
            self.dead_snake = self.snake
            self.snake = None
            self.grid.cover(
                snake.head,
                snake.head_color)  # Avoid miscount of grid.open_space
            self.grid.connect(snake.body.popleft(), snake.body[0],
                              self.grid.SPACE_COLOR)
            reward = -1
        # Check for reward
        elif self.grid.food_space(snake.head):
            self.grid.draw(snake.body[0], self.grid.BODY_COLOR)  # Redraw tail
            self.grid.connect(snake.body[0], snake.body[1],
                              self.grid.BODY_COLOR)
            self.grid.cover(
                snake.head,
                snake.head_color)  # Avoid miscount of grid.open_space
            reward = 1
            self.grid.new_food()
        else:
            reward = 0
            empty_coord = snake.body.popleft()
            self.grid.connect(empty_coord, snake.body[0],
                              self.grid.SPACE_COLOR)
            self.grid.draw(snake.head, snake.head_color)

        self.grid.connect(snake.body[-1], snake.head, self.grid.BODY_COLOR)
        return reward

    def kill_snake(self):
        """
        Deletes snake from game
        """
        assert self.dead_snake is not None
        self.grid.erase(self.dead_snake.head)
        self.grid.erase_snake_body(self.dead_snake)
        self.dead_snake = None

    # def get_state(self):
    #     """
    #     Creates a state from game parameters
    #     """
    #     snake = self.snake
    #     if snake is None:
    #         return None
    #     head = snake.head
    #     body = snake.body
    #     food = self.grid.food_coord
    #     return head, body, food

    def step(self, direction):
        """
        Takes an action for snake in the specified direction and collects its reward.

        direction - integer direction for the snake.
        """

        # Ensure no more play until reset
        if (self.snake is None
                and self.dead_snake is not None) or self.grid.open_space < 1:
            return self.grid.grid.copy(), 0, True, None

        if self.snake is None and self.dead_snake is not None:
            self.kill_snake()
        self.move_snake(direction)
        reward = (self.move_result())

        done = reward == -1 or self.grid.open_space < 1
        return self.grid.grid.copy(), reward, done, None
class Controller():
    """
    This class combines the Snake, Food, and Grid classes to handle the game logic.
    """
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 n_snakes=1,
                 n_foods=1,
                 random_init=True,
                 start_coord=None,
                 food_pos=None):

        assert n_snakes < grid_size[0] // 3
        assert n_snakes < 25
        assert snake_size < grid_size[1] // 2
        assert unit_gap >= 0 and unit_gap < unit_size

        if food_pos is not None:
            assert food_pos[0] < grid_size[
                0]  # added 20190519 in mod19 version
            assert food_pos[1] < grid_size[
                1]  # added 20190519 in mod19 version

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)

        self.snakes = []
        self.dead_snakes = []
        for i in range(1, n_snakes + 1):

            if start_coord is None:
                start_coord = [
                    i * grid_size[0] // (n_snakes + 1), snake_size + 1
                ]

            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i * 10, 0]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        if food_pos is not None:
            self.grid.place_food(food_pos)  # added 20190519 in mod19 version
        else:
            if not random_init:
                for i in range(2, n_foods + 2):
                    start_coord = [
                        i * grid_size[0] // (n_foods + 3), grid_size[1] - 5
                    ]
                    self.grid.place_food(start_coord)
            else:
                for i in range(n_foods):
                    self.grid.new_food()

    def move_snake(self, direction, snake_idx):
        """
        Moves the specified snake according to the game's rules dependent on the direction.
        Does not draw head and does not check for reward scenarios. See move_result for these
        functionalities.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return

        # Cover old head position with body
        self.grid.cover(snake.head, self.grid.BODY_COLOR)
        # Erase tail without popping so as to redraw if food eaten
        self.grid.erase(snake.body[0])
        # Find and set next head position conditioned on direction
        snake.action(direction)

    def move_result(self, direction, snake_idx=0):
        """
        Checks for food and death collisions after moving snake. Draws head of snake if
        no death scenarios.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return 0

        # Check for death of snake
        if self.grid.check_death(snake.head):
            self.dead_snakes[snake_idx] = self.snakes[snake_idx]
            self.snakes[snake_idx] = None
            self.grid.cover(
                snake.head,
                snake.head_color)  # Avoid miscount of grid.open_space
            self.grid.connect(snake.body.popleft(), snake.body[0],
                              self.grid.SPACE_COLOR)
            reward = -1
        # Check for reward
        elif self.grid.food_space(snake.head):
            self.grid.draw(snake.body[0], self.grid.BODY_COLOR)  # Redraw tail
            self.grid.connect(snake.body[0], snake.body[1],
                              self.grid.BODY_COLOR)
            self.grid.cover(
                snake.head,
                snake.head_color)  # Avoid miscount of grid.open_space
            reward = 1
            self.grid.new_food()
        else:
            reward = 0
            empty_coord = snake.body.popleft()
            self.grid.connect(empty_coord, snake.body[0],
                              self.grid.SPACE_COLOR)
            self.grid.draw(snake.head, snake.head_color)

        self.grid.connect(snake.body[-1], snake.head, self.grid.BODY_COLOR)

        return reward

    def kill_snake(self, snake_idx):
        """
        Deletes snake from game and subtracts from the snake_count 
        """

        assert self.dead_snakes[snake_idx] is not None
        self.grid.erase(self.dead_snakes[snake_idx].head)
        self.grid.erase_snake_body(self.dead_snakes[snake_idx])
        self.dead_snakes[snake_idx] = None
        self.snakes_remaining -= 1

    def step(self, directions):
        """
        Takes an action for each snake in the specified direction and collects their rewards
        and dones.

        directions - tuple, list, or ndarray of directions corresponding to each snake.
        """

        # Ensure no more play until reset
        if self.snakes_remaining < 1 or self.grid.open_space < 1:
            if type(directions) == type(int()) or len(directions) is 1:
                return self.grid.grid.copy(), 0, True, {
                    "snakes_remaining": self.snakes_remaining
                }
            else:
                return self.grid.grid.copy(), [0] * len(directions), True, {
                    "snakes_remaining": self.snakes_remaining
                }

        rewards = []

        if type(directions) == type(int()):
            directions = [directions]

        for i, direction in enumerate(directions):
            if self.snakes[i] is None and self.dead_snakes[i] is not None:
                self.kill_snake(i)
            self.move_snake(direction, i)
            rewards.append(self.move_result(direction, i))

        done = self.snakes_remaining < 1 or self.grid.open_space < 1
        if len(rewards) is 1:
            return self.grid.grid.copy(), rewards[0], done, {
                "snakes_remaining": self.snakes_remaining
            }
        else:
            return self.grid.grid.copy(), rewards, done, {
                "snakes_remaining": self.snakes_remaining
            }
예제 #8
0
class Controller():
    """
    This class combines the Snake, Food, and Grid classes to handle the game logic.
    """
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 n_snakes=1,
                 n_foods=1,
                 random_init=True):

        assert n_snakes < grid_size[0] // 3
        assert n_snakes < 25
        assert snake_size < grid_size[1] // 2
        assert unit_gap >= 0 and unit_gap < unit_size

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)

        self.snakes = []
        self.dead_snakes = []
        for i in range(1, n_snakes + 1):
            start_coord = [i * grid_size[0] // (n_snakes + 1), snake_size + 1]
            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i * 10, 0]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        if not random_init:
            for i in range(2, n_foods + 2):
                start_coord = [
                    i * grid_size[0] // (n_foods + 3), grid_size[1] - 5
                ]
                self.grid.place_food(start_coord)
        else:
            for i in range(n_foods):
                self.grid.new_food()

    def move_snake(self, direction, snake_idx):
        """
        Moves the specified snake according to the game's rules dependent on the direction.
        Does not draw head and does not check for reward scenarios. See move_result for these
        functionalities.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return

        # Cover old head position with body
        self.grid.cover(snake.head, self.grid.BODY_COLOR)
        # Erase tail without popping so as to redraw if food eaten
        self.grid.erase(snake.body[0])
        # Find and set next head position conditioned on direction
        snake.action(direction)

    def move_result(self, direction, snake_idx=0):
        """
        Checks for food and death collisions after moving snake. Draws head of snake if
        no death scenarios.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return 0

        # Check for death of snake
        if self.grid.check_death(snake.head):
            self.dead_snakes[snake_idx] = self.snakes[snake_idx]
            self.snakes[snake_idx] = None
            self.grid.cover(
                snake.head,
                snake.head_color)  # Avoid miscount of grid.open_space
            self.grid.connect(snake.body.popleft(), snake.body[0],
                              self.grid.SPACE_COLOR)
            reward = -10.0
        # Check for reward
        elif self.grid.food_space(snake.head):
            self.grid.draw(snake.body[0], self.grid.BODY_COLOR)  # Redraw tail
            self.grid.connect(snake.body[0], snake.body[1],
                              self.grid.BODY_COLOR)
            self.grid.cover(
                snake.head,
                snake.head_color)  # Avoid miscount of grid.open_space
            reward = 10.0
            #self.grid.new_food()
        else:
            reward = 1 / np.linalg.norm(self.grid.food_coord - snake.head)
            empty_coord = snake.body.popleft()
            self.grid.connect(empty_coord, snake.body[0],
                              self.grid.SPACE_COLOR)
            self.grid.draw(snake.head, snake.head_color)

        self.grid.connect(snake.body[-1], snake.head, self.grid.BODY_COLOR)

        return reward

    def kill_snake(self, snake_idx):
        """
        Deletes snake from game and subtracts from the snake_count 
        """

        assert self.dead_snakes[snake_idx] is not None
        self.grid.erase(self.dead_snakes[snake_idx].head)
        self.grid.erase_snake_body(self.dead_snakes[snake_idx])
        self.dead_snakes[snake_idx] = None
        self.snakes_remaining -= 1

    def get_snake_info(self):
        snake_info = []

        for snake in range(len(self.snakes)):
            agent = self.snakes[snake]
            if agent is not None:
                H = agent.head
                surr_coord = (
                    (H[0] + 1, H[1]), (H[0] - 1, H[1]), (H[0], H[1] + 1),
                    (H[0], H[1] - 1))  #right left down up
                surr_info = []

                for i in range(4):
                    if self.grid.check_death(surr_coord[i]):
                        surr_info.append(np.array(surr_coord[i] + (1, )))
                    elif self.grid.food_space(surr_coord[i]):
                        surr_info.append(np.array(surr_coord[i] + (2, )))
                    else:
                        surr_info.append(np.array(surr_coord[i] + (0, )))

                # Return the euclidean distance
                food_dist = np.linalg.norm(self.grid.food_coord - agent.head)

                food_diff = [
                    self.grid.food_coord[0] - H[0],
                    self.grid.food_coord[1] - H[1]
                ]
                food_diff.append(food_dist)

                surr_info.append(food_diff)
                snake_info.append(surr_info)
            else:
                neg_1s = [-1, -1, -1]
                surr_info = []
                surr_info.append(neg_1s)
                surr_info.append(neg_1s)
                surr_info.append(neg_1s)
                surr_info.append(neg_1s)
                surr_info.append([0, 0, -1])
                snake_info.append(surr_info)

        return snake_info

    def step(self, directions):
        """
        Takes an action for each snake in the specified direction and collects their rewards
        and dones.

        directions - tuple, list, or ndarray of directions corresponding to each snake.
        """

        # Fawad's Code #

        # Return a list of the spaces surrounding the head of the snake [SPACE = 0, BODY/HEAD/WALL = 1, FOOD = 2].
        # A wall will be considered a Body
        snake_info = self.get_snake_info()

        ###############################

        # Ensure no more play until reset
        if self.snakes_remaining < 1 or self.grid.open_space < 1:
            if type(directions) == type(int()) or len(directions) is 1:
                return snake_info, 0, True, {
                    "snakes_remaining": self.snakes_remaining
                }
            else:
                return snake_info, [0] * len(directions), True, {
                    "snakes_remaining": self.snakes_remaining
                }

        rewards = []

        if type(directions) == type(int()):
            directions = [directions]

        for i, direction in enumerate(directions):
            if self.snakes[i] is None and self.dead_snakes[i] is not None:
                self.kill_snake(i)
            self.move_snake(direction, i)
            rewards.append(self.move_result(direction, i))

        done = self.snakes_remaining < 1 or self.grid.open_space < 1
        if len(rewards) is 1:
            return snake_info, rewards[0], done, {
                "snakes_remaining": self.snakes_remaining
            }
        else:
            return snake_info, rewards, done, {
                "snakes_remaining": self.snakes_remaining
            }
예제 #9
0
class Controller():
    """
    This class combines the Snake, Food, and Grid classes to handle the game logic.
    """
    def __init__(self,
                 grid_size=[30, 30],
                 unit_size=10,
                 unit_gap=1,
                 snake_size=3,
                 n_snakes=1,
                 n_foods=1,
                 random_init=True,
                 wall=False):

        assert n_snakes < grid_size[0] // 3
        assert n_snakes < 25
        assert snake_size < grid_size[1] // 2
        assert unit_gap >= 0 and unit_gap < unit_size

        self.snakes_remaining = n_snakes
        self.grid = Grid(grid_size, unit_size, unit_gap)

        self.empty = []
        self.douse = []
        self.r = [0., 0., 0., 0.]

        self.snakes = []
        self.dead_snakes = []
        for i in range(1, n_snakes + 1):
            start_coord = [i * grid_size[0] // (n_snakes + 1), snake_size + 1]
            self.snakes.append(Snake(start_coord, snake_size))
            color = [self.grid.HEAD_COLOR[0], i * 10, 0]
            color = [0, 0, 255]
            self.snakes[-1].head_color = color
            self.grid.draw_snake(self.snakes[-1], color)
            self.dead_snakes.append(None)

        # if wall:
        #     self.grid.create_wall()
        #     self.grid.create_wall()
        #     self.grid.create_wall()

        # if not random_init:
        #     for i in range(2,n_foods+2):
        #         start_coord = [i*grid_size[0]//(n_foods+3), grid_size[1]-5]
        #         self.grid.place_food(start_coord)
        # else:
        # for i in range(n_foods):
        #     self.grid.new_food(i)

        self.grid.init_fire()

    def move_snake(self, direction, snake_idx):
        """
        Moves the specified snake according to the game's rules dependent on the direction.
        Does not draw head and does not check for reward scenarios. See move_result for these
        functionalities.
        """

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return

        if direction >= 8:
            co = snake.step(snake.head, direction % 4)
            if self.grid.off_grid(co):
                return
            if np.array_equal(self.grid.color_of(co),
                              self.grid.FOOD_COLORS[0]):
                self.grid.draw(co, self.grid.WALL_COLOR)
                self.douse.append(co)
                self.grid.aag.remove(co)
                # self.r[snake_idx] += 1

        elif direction >= 4:
            co = snake.step(snake.head, direction % 4)
            if self.grid.off_grid(co):
                return
            self.grid.draw(co, self.grid.WHITE)
            self.empty.append(co)
            # self.r[snake_idx] += 0.3

        else:
            # x1 = snake.head[0]
            # y1 = snake.head[1]
            # x2 = self.grid.gm[0]
            # y2 = self.grid.gm[1]
            #
            # if np.random.rand()<0.5:
            #     if (x1 < x2):
            #         direction = 1
            #     else:
            #         direction = 3
            # else:
            #     if (y1 < y2):
            #         direction = 2
            #     else:
            #         direction = 0

            co = snake.step(snake.head, direction % 4)
            if self.grid.off_grid(co) or np.array_equal(
                    self.grid.color_of(co), self.grid.FOOD_COLORS[0]):
                return

            # if euclidean(snake.head, self.grid.gm) > euclidean(co, self.grid.gm):
            #     self.r[snake_idx] += 0.1
            # else:
            # self.r[snake_idx] -= 0.1

            # Cover old head position with body
            # if not snake.head in self.empty:
            self.grid.cover(snake.head, self.grid.BODY_COLOR)
            # else:
            #     self.grid.draw(snake.head, self.grid.WHITE)
            # Erase tail without popping so as to redraw if food eaten
            self.grid.erase(snake.body[0])
            # Find and set next head position conditioned on direction
            snake.action(direction)

    def move_result(self, direction, snake_idx=0):
        """
        Checks for food and death collisions after moving snake. Draws head of snake if
        no death scenarios.
        """

        if direction >= 4:
            return

        snake = self.snakes[snake_idx]
        if type(snake) == type(None):
            return 0

        co = snake.step(snake.head, direction)
        if self.grid.off_grid(co) or np.array_equal(self.grid.color_of(co),
                                                    self.grid.FOOD_COLORS[0]):
            return

        # Check for death of snake
        # if False and self.grid.check_death(snake.head):
        #     self.dead_snakes[snake_idx] = self.snakes[snake_idx]
        #     self.snakes[snake_idx] = None
        #     self.grid.cover(snake.head, snake.head_color)  # Avoid miscount of grid.open_space
        #     if direction < 4:
        #         if len(snake.body)>1:
        #             try:
        #                 self.grid.connect(snake.body.popleft(), snake.body[0], self.grid.SPACE_COLOR)
        #             finally:
        #                 k=0
        # self.r[snake_idx] += -0.9
        # else:
        #     food_item = self.grid.food_space(snake.head)
        #
        #     # Check for wall
        #     if food_item == -2:
        #         self.dead_snakes[snake_idx] = self.snakes[snake_idx]
        #         self.snakes[snake_idx] = None
        #         self.grid.cover(snake.head, snake.head_color)  # Avoid miscount of grid.open_space
        #         self.grid.connect(snake.body.popleft(), snake.body[0], self.grid.SPACE_COLOR)
        #         reward = -1
        #
        #     # Check for reward
        #     elif food_item != -1:
        #         self.grid.draw(snake.body[0], self.grid.BODY_COLOR)  # Redraw tail
        #         self.grid.connect(snake.body[0], snake.body[1], self.grid.BODY_COLOR)
        #         self.grid.cover(snake.head, snake.head_color)  # Avoid miscount of grid.open_space
        #         reward = self.grid.FOOD_REWARDS[food_item]
        #         self.grid.new_food(food_item)

        # else:
        reward = 0
        empty_coord = snake.body.popleft()
        self.grid.connect(empty_coord, snake.body[0], self.grid.SPACE_COLOR)
        self.grid.draw(snake.head, snake.head_color)

        self.grid.connect(snake.body[-1], snake.head, self.grid.BODY_COLOR)

        # return reward

    def kill_snake(self, snake_idx):
        """
        Deletes snake from game and subtracts from the snake_count
        """
        return
        assert self.dead_snakes[snake_idx] is not None
        self.grid.erase(self.dead_snakes[snake_idx].head)
        self.grid.erase_snake_body(self.dead_snakes[snake_idx])
        self.dead_snakes[snake_idx] = None
        self.snakes_remaining -= 1

    def step(self, directions):
        """
        Takes an action for each snake in the specified direction and collects their rewards
        and dones.

        directions - tuple, list, or ndarray of directions corresponding to each snake.
        """

        self.r = [0., 0., 0., 0.]

        # Ensure no more play until reset
        if self.snakes_remaining < 1 or self.grid.open_space < 1:
            if len(directions) is 1:
                return self.grid.grid.copy(), 0, True, {
                    "snakes_remaining": self.snakes_remaining
                }
            else:
                return self.grid.grid.copy(), [0] * len(directions), True, {
                    "snakes_remaining": self.snakes_remaining
                }

        # remove when multiple agents

        # directions = int(directions)

        if type(directions) == type(int()):
            directions = [directions]

        for i, direction in enumerate(directions):

            if self.snakes[i] is None and self.dead_snakes[i] is not None:
                self.kill_snake(i)
            self.move_snake(direction, i)
            self.move_result(direction, i)

            snake = self.snakes[i]
            if snake is not None:
                for co in self.empty:
                    if not np.array_equal(snake.head, co):
                        self.grid.draw(co, self.grid.WHITE)

                for co in self.douse:
                    if not np.array_equal(snake.head, co):
                        self.grid.draw(co, self.grid.WALL_COLOR)

                self.grid.draw(snake.head, self.grid.HEAD_COLOR)

        self.grid.new_food(0)

        done = self.snakes_remaining < 1  # or self.grid.open_space < 1
        self.r[0] += -0.2
        # self.r[1] += -0.2
        if len(self.r) is 1:
            return self.grid.grid.copy(), self.r[0], done, {
                "snakes_remaining": self.snakes_remaining
            }
        else:
            return self.grid.grid.copy(), self.r, done, {
                "snakes_remaining": self.snakes_remaining
            }