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()
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 __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()
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}
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 }
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 }
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 }