class Env(): def __init__(self, max_width, max_height, init_width, init_height, display_width, display_height, agent_vision): self.max_w = max_width self.max_h = max_height self.w = init_width self.h = init_height self.disp_w, self.disp_h = display_width, display_height self.ACTION_SPACE = 4 self.agent_vision = agent_vision self.STATE_SPACE = (agent_vision * 2)**2 + 2 # agent_vision*2) #-------Increase the size of environment------# def change_size(self, w_change, h_change): self.w = min(self.w + w_change, self.max_w - self.agent_vision) self.h = min(self.h + h_change, self.max_h - self.agent_vision) return self.w, self.h #---------helpful function to get boundaries----------# def get_boundaries(self): odd_w, odd_h = self.w % 2, self.h % 2 mid_width, mid_height = int(self.max_w / 2), int(self.max_h / 2) x1, x2 = mid_width - int(self.w / 2), mid_height - 1 + int( self.w / 2) + odd_w y1, y2 = mid_width - int(self.h / 2), mid_height - 1 + int( self.h / 2) + odd_h return x1, x2, y1, y2 #-------------Returns random positions within the boundary--------------# def get_randoms(self, length=1): x1, x2, y1, y2 = self.get_boundaries() #logic to keep the snake AI inside the boundary if length > 1: max_w, max_h = self.max_w - length, self.max_h - length if x1 < length - 1: x1 = length - 1 if x2 > max_w: x2 = max_w if y1 < length - 1: y1 = length - 1 if y2 > max_h: y2 = max_h a = random.randint(x1, x2) b = random.randint(y1, y2) return a, b #-----Decide the playing region------# def draw_boundary(self, env): x1, x2, y1, y2 = self.get_boundaries() env[0:y1, :] = CONSTANTS['boundary'] env[y2 + 1:, :] = CONSTANTS['boundary'] env[y1:y2 + 1, 0:x1] = CONSTANTS['boundary'] env[y1:y2 + 1, x2 + 1:] = CONSTANTS['boundary'] return env #-------Reset the environment-------# def reset(self): # reset env self.env = np.zeros((self.max_h, self.max_w)) # background self.env = self.draw_boundary(self.env) # Add player 1 head_x1, head_y1 = self.get_randoms() self.p1 = Snake(head_x1, head_y1, self.w, self.h) self.env = self.p1.draw(self.env, player=1) # Add player 2 head_x2, head_y2 = self.get_randoms() self.p2 = Snake(head_x2, head_y2, self.w, self.h) self.env = self.p2.draw(self.env, player=2) # Add food self.food_x, self.food_y = self.get_randoms() self.env[self.food_y, self.food_x] = CONSTANTS['food'] # Get player's states (v for vision) self.v1 = self.p1.look(self.env, self.agent_vision) self.v2 = self.p2.look(self.env, self.agent_vision) return np.hstack((self.v1.ravel(), (head_x1 - self.food_x, head_y1 - self.food_y))), self.v1, \ np.hstack((self.v2.ravel(), (head_x2 - self.food_x, head_y2 - self.food_y))), self.v2 #-----Render the environment---------# def render(self, actions, states, stats, train=True, episode=-1, epsilon=-1, gamma=-1): disp_matrix = self.get_display_matrix(self.env) state1, state2 = self.get_display_matrix( states[0]), self.get_display_matrix(states[1]) disp_env = add_states(disp_matrix, state1, actions[0], left=True) disp_env = add_states(disp_env, state2, actions[1]) # sprinkle some info to the disp train_params = None if train: train_params = { 'Episode': episode, 'Epsilon': epsilon, 'Gamma': gamma } p1_params = {'Player': 1, 'stats': stats[0]} p2_params = {'Player': 2, 'stats': stats[1]} img = add_info(self.disp_w, self.disp_h, disp_env, p1_params, p2_params, train_params=train_params) #img2 = Image.fromarray(self.env) #img2 = np.array(img2.resize(self.max_w, self.max_h)) cv2.imshow("Sliterh_io", img) #cv2.imshow("AI v/s AI", self.env) #time.sleep(1) if cv2.waitKey(1) & 0xFF == ord('q'): #when Q is pressed print('Stop Execution') cv2.destroyAllWindows() quit() #------------Adds color to matrix------------------------------# @staticmethod def get_display_matrix(matrix): disp_matrix = np.ones( (*matrix.shape, 3), dtype=np.uint8) * 255 # background disp_matrix[np.where(matrix == CONSTANTS['boundary'])] = BOUNDARY_COLOR disp_matrix[np.where(matrix == CONSTANTS['p1'])] = PLAYER_COLORS[1] disp_matrix[np.where(matrix == CONSTANTS['p2'])] = PLAYER_COLORS[2] disp_matrix[np.where( matrix == CONSTANTS['p1_head'])] = PLAYER_HEAD_COLOR disp_matrix[np.where( matrix == CONSTANTS['p2_head'])] = PLAYER_HEAD_COLOR disp_matrix[np.where(matrix == CONSTANTS['food'])] = FOOD_COLOR return disp_matrix #----------Agent takes an action and environment changes--------# def step(self, action, player): info = 0 # 1 - food eaten # 2 - Hit opponent # 3 - Died on Boundary p = self.p1 if player == 1 else self.p2 opp_player = 2 if player == 1 else 1 done, reward = False, -1 # positive reward x1, x2, y1, y2 = self.get_boundaries() x, y = ACTION_RESULT[action] head_x, head_y = p.head_pos() new_x, new_y = head_x + x, head_y + y # new head positions # check outside boundary if new_x < x1 or new_x > x2 or new_y < y1 or new_y > y2: done = True reward = 0 info = 3 """ # check collision with other agents if self.env[new_y, new_x] == CONSTANTS[f'p{opp_player}'] and not done: done = True reward = -10 info = 2 """ # check if ate food if self.env[new_y, new_x] == CONSTANTS['food']: p.grow() self.env[new_y, new_x] = CONSTANTS['background'] self.food_x, self.food_y = self.get_randoms() self.env[self.food_y, self.food_x] = CONSTANTS['food'] reward = 50 info = 1 if not done: # p.grow() pass else: # Add something to info # player is dead, displaye a cross or red patch pass p.update(x, y) self.env[np.where(self.env == CONSTANTS[f'p{player}_head'])] = 0 self.env[np.where(self.env == CONSTANTS[f'p{player}'])] = 0 self.env = self.draw_boundary(self.env) # redraw the boundaries self.env = p.draw(self.env, player=player) # redraw the player #print(self.env) vision = p.look(self.env, self.agent_vision ) # Check if next state is required in case of done return np.hstack((vision.ravel(), (new_x - self.food_x, new_y - self.food_y))), reward, done, vision, info
class Game: def __init__(self): pygame.init() self.CLOCK = Clock() self.ROWS_COLS = 15 self.GRID_SIZE = 50 self.SCREEN_SIZE = self.GRID_SIZE * self.ROWS_COLS self.DISPLAY = display.set_mode((self.SCREEN_SIZE, self.SCREEN_SIZE)) self.BACKGROUND_COLOR = (0, 0, 0) self.GRID_COLOR = (100, 100, 100) self.SNAKE_COLOR = (0, 255, 0) self.apple_generator = AppleGenerator(self.ROWS_COLS, self.GRID_SIZE) self.apple = self.apple_generator.generate_apple(self.snake) mid = (self.ROWS_COLS // 2) * self.GRID_SIZE self.start_x_y = (mid, mid) self.snake = Snake(self.start_x_y, self.GRID_SIZE, self.SCREEN_SIZE, self.SNAKE_COLOR, self.spawn_apple) self.exit_clicked = None self.playing = None def start(self): self.playing = True self.exit_clicked = False self.reset() while self.playing: self.CLOCK.tick(10) delay(50) if not self.exit_clicked: for event in pygame.event.get(): if event.type == pygame.QUIT: # noinspection PyAttributeOutsideInit self.exit_clicked = True self.playing = False # Doesn't close immediately. Will still run a few cycles. pygame.quit() if not self.exit_clicked: self.snake.move() self.playing = self.playing and not self.snake.has_collided if self.playing: self.draw_game() elif not self.exit_clicked: print('Score: ', len(self.snake.body) + 1) self.message_box('You lost!', 'Play again?') self.reset() # noinspection PyAttributeOutsideInit self.playing = True def spawn_apple(self): self.apple = self.apple_generator.generate_apple(self.snake) self.snake.set_apple(self.apple) def reset(self): self.snake = Snake(self.start_x_y, self.GRID_SIZE, self.SCREEN_SIZE, self.SNAKE_COLOR, self.spawn_apple) self.spawn_apple() def draw_game(self): self.DISPLAY.fill(self.BACKGROUND_COLOR) self.draw_grid() self.snake.draw(self.DISPLAY) self.apple.draw(self.DISPLAY) display.update() def draw_grid(self): x, y = 0, 0 for i in range(self.ROWS_COLS): x += self.GRID_SIZE y += self.GRID_SIZE draw.line(self.DISPLAY, self.GRID_COLOR, (x, 0), (x, self.SCREEN_SIZE)) draw.line(self.DISPLAY, self.GRID_COLOR, (0, y), (self.SCREEN_SIZE, y)) @staticmethod def message_box(subject, content): root = tk.Tk() root.attributes("-topmost", True) root.withdraw() messagebox.showinfo(subject, content) # noinspection PyBroadException try: root.destroy() except: pass