def eval_genome(g, config): net = neat.nn.FeedForwardNetwork.create(g, config) cycles_per_net = 5 fitnesses = [0] * cycles_per_net for cycle in range(cycles_per_net): x = random.randrange(SNAKE_WIDTH + SNAKE_WIDTH / 2, WIN_WIDTH - (SNAKE_WIDTH + SNAKE_WIDTH / 2), SNAKE_WIDTH) y = random.randrange(SNAKE_WIDTH, WIN_WIDTH - (SNAKE_WIDTH), SNAKE_WIDTH) h = Head(WIN_WIDTH, SNAKE_WIDTH, x, y) b1 = Body(h.x, h.y + SNAKE_WIDTH, h, SNAKE_WIDTH) b2 = Body(b1.x, b1.y + SNAKE_WIDTH, b1, SNAKE_WIDTH) bodies = [b1, b2] a = Apple(WIN_WIDTH, SNAKE_WIDTH, h, bodies) run = True score = 0 time = 200 while (run): time -= 1 fitnesses[cycle] += 0.01 if (time <= 0): run = False break # Control Code inputs = game.determine_position(h, bodies, a) output = net.activate( (inputs )) #distance_up, distance_down, distance_left, distance_right i = output.index(max(output)) if i == 0: h.moveForward() elif i == 1: h.moveLeft() elif i == 2: h.moveRight() game.update_positions(h, bodies) if (h.check_collisions(bodies)): run = False break if (h.check_apple(a)): score += 1 fitnesses[cycle] += 10 + time * 0.1 time = 200 end = bodies[-1] bodies.append(Body(end.p_x, end.p_y, end, SNAKE_WIDTH)) a.reset(h, bodies) return [statistics.median(fitnesses), net]
def single_game(manual, distances=False, net=None, generation=None): win = pygame.display.set_mode((WIN_WIDTH, WIN_WIDTH)) win.fill((0, 0, 0)) clock = pygame.time.Clock() # generating head of snake x = random.randrange(SNAKE_WIDTH + SNAKE_WIDTH / 2, WIN_WIDTH - (SNAKE_WIDTH + SNAKE_WIDTH / 2), SNAKE_WIDTH) y = random.randrange(SNAKE_WIDTH, WIN_WIDTH - (SNAKE_WIDTH), SNAKE_WIDTH) h = Head(WIN_WIDTH, SNAKE_WIDTH, x, y) # generating body of snake b1 = Body(h.x, h.y + SNAKE_WIDTH, h, SNAKE_WIDTH) b2 = Body(b1.x, b1.y + SNAKE_WIDTH, b1, SNAKE_WIDTH) bodies = [b1, b2] # generating apple for snake to eat a = Apple(WIN_WIDTH, SNAKE_WIDTH, h, bodies) run = True score = 0 time = 200 while (run): # setting clock speed. Slower if the game is being manually run if manual: clock.tick(MANUAL_SNAKE_SPEED) else: clock.tick(SNAKE_SPEED) # Counting down time and cause the game to quit if an apple hasnt been eaten within the time frame time -= 1 if (time <= 0): run = False print('Time Out!') break # Automated Control # inputs to neural network: # distance forward, distance left, distance right, apple right, apple top inputs = determine_position(h, bodies, a) # Previous attempts # head x, head y, boundaries, apple x, apple y # Failed to train after 100 generatations of 100 population -> has no sense of direction # distance front, distance down, distance left, distance right, left distance to apple, upwards distance to apple # Failed to train after 100 generatations of 100 population -> has no sense of direction # if the user is not playing the game manually, run the game with automated controls if not (manual): # run inputs into neural network output = net.activate((inputs)) # determing direction indicated by NN i = output.index(max(output)) if i == 0: h.moveForward() elif i == 1: h.moveLeft() elif i == 2: h.moveRight() for event in pygame.event.get(): # checking if exit button is pushed on game if (event.type == pygame.QUIT): run = False break # manual control - checking for key pushes if manual: if event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: h.KeyMoveUp() break # breaking prevents multiple keys being processed at the same time elif event.key == pygame.K_DOWN: h.KeyMoveDown() break elif event.key == pygame.K_LEFT: h.KeyMoveLeft() break elif event.key == pygame.K_RIGHT: h.KeyMoveRight() break # update the position of the head and bodies update_positions(h, bodies) # checking for collisions if (h.check_collisions(bodies)): run = False print('Collision!') break # check if head ate apple if (h.check_apple(a)): score += 1 time = 200 end = bodies[-1] bodies.append(Body(end.p_x, end.p_y, end, SNAKE_WIDTH)) a.reset(h, bodies) # if (score >= 200): # run = False # break # outputing distances for debugging distance_list = [] if (distances): distance_list = [inputs[0], inputs[1], inputs[2]] # drawing game state onto screen draw_screen(win, h, bodies, a, distance_list, score, time, generation)
class Game(object): def __init__(self, window): self.snake = Snake() self.apple = Apple() self.window = window self.grid = [] self.score = 0 def move_snake(self, key): """Manual move of snake""" if key in [KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT]: self.snake.move(key) else: self.snake.move(self.snake.last_direction) if self.snake.head == self.apple.position: self.snake.eat(self.apple, self.grid) self.score += 1 if self.snake.is_collide(): self.reset() def automove(self): """Deplace position of the snake with A* algorithm""" path = find_path(tuple(self.snake.head), tuple(self.apple.position), self.grid) move = path[0] if path else False if not move: move = self.any_possible_move() if move: if not self.snake.is_eating: self.snake.position.pop(0) self.snake.is_eating = False self.snake.position.append(move) self.snake.head = self.snake.position[-1] self.snake.tail = self.snake.position[0] if self.snake.head == self.apple.position: self.snake.eat(self.apple, self.grid) self.score += 1 if self.snake.is_collide() or not move: self.reset() def any_possible_move(self): """Return any possible position""" neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0)] for i, j in neighbors: neighbor = self.snake.head[0] + i, self.snake.head[1] + j if self.grid[neighbor[0]][neighbor[1]] == 0: return [neighbor[0], neighbor[1]] return False def display(self): """display game""" self.grid[:] = [] for line in range(HEIGHT): self.grid.append([]) for column in range(WIDTH): if column == 0 or line == 0 or column == WIDTH - 1 or line == HEIGHT - 1: self.grid[line].append(1) else: self.grid[line].append(0) for line, column in self.snake.position: self.grid[line][column] = 1 self.window.addstr(line, column, 's') line, column = self.apple.position self.window.addstr(line, column, 'a') def reset(self): """Reset game""" self.apple.reset() self.snake.reset() self.score = 0
class Field: def __init__(self, ga, width, height): self.ga = ga self.width = width self.height = height self.sc = pygame.display.set_mode([width + 1, height + 1]) self.clock = pygame.time.Clock() self.snake = Snake(self.sc, self) self.apple = Apple(self.sc, self) self.death_stats = [0] * 3 pygame.init() def get_fitness(self, individual): self.ga.ind_counter += 1 self.snake.reset() self.snake.load_weights(individual) self.apple.reset() while True: self.snake.move() self.draw() if self.snake.check_apple_collision(): self.apple.reset() if self.snake.check_wall_collision(): self.death_stats[0] += 1 break if self.snake.check_self_collision(): self.death_stats[1] += 1 break if self.snake.check_energy(): self.death_stats[2] += 1 break check_events() return self.snake.get_fitness() # Проверяем не вышла ли голова змейки на границы поля def is_valid_pos(self, pos): if pos.x < 0 or pos.x > self.width - self.snake.size or pos.y < 0 or pos.y > self.height - self.snake.size: return False return True # Получаем случайную позицию на поле [доработать] def get_random_pos(self): while True: pos = Vector2(randrange(0, self.width, self.snake.size), randrange(0, self.height, self.snake.size)) if pos not in self.snake.get_segments(): return pos def get_apple_pos(self): return self.apple.get_pos() def set_death_stats(self, death_stats): self.death_stats = death_stats def get_death_stats(self): return self.death_stats def draw(self): if settings['animation']: self.sc.fill(pygame.Color('#282A36')) if settings['grid']: self.draw_grid() if settings['info']: self.draw_info() if settings['snake_footprint']: self.snake.draw_footprint() if settings['snake_vectors']: self.snake.draw_vectors() self.snake.draw() self.apple.draw() pygame.display.flip() if settings['slow_mode']: self.clock.tick(settings['fps']) def draw_grid(self): for x in range(0, self.width + self.snake.size, self.snake.size): pygame.draw.aaline(self.sc, '#44475a', [x, 0], [x, self.height]) for y in range(0, self.height + self.snake.size, self.snake.size): pygame.draw.aaline(self.sc, '#44475a', [0, y], [self.width, y]) def draw_info(self): self.draw_text(20, 20, f'HIGH SCORE: {self.snake.high_score}') self.draw_text(20, 40, f'GENERATION: {self.ga.gen_counter}') self.draw_text(20, 60, f'GENOME: {self.ga.ind_counter}/{self.ga.pop_length}') self.draw_text(20, 80, f'SCORE: {self.snake.score}') self.draw_text(20, 100, f'STEPS: {self.snake.steps}') self.draw_text(20, 120, f'ENERGY: {self.snake.get_energy()}') if sum(self.death_stats) != 0: percent = '{:.2f}'.format(self.death_stats[0] * 100 / sum(self.death_stats)) self.draw_text( 20, 140, f'BUMPED INTO A WALL: {self.death_stats[0]} | {percent}%') percent = '{:.2f}'.format(self.death_stats[1] * 100 / sum(self.death_stats)) self.draw_text( 20, 160, f'HIT ITS OWN TAIL: {self.death_stats[1]} | {percent}%') percent = '{:.2f}'.format(self.death_stats[2] * 100 / sum(self.death_stats)) self.draw_text( 20, 180, f'DIED OF HUNGER: {self.death_stats[2]} | {percent}%') self.draw_text(20, 220, f'HOTKEYS:') self.draw_text(20, 240, f'A - evolution without animation') self.draw_text(20, 260, f'S - slow/fast') self.draw_text(20, 280, f'M - smart speed') self.draw_text(20, 300, f'F - footprint') self.draw_text(20, 320, f'V - vectors') self.draw_text(20, 340, f'G - grid') self.draw_text(20, 360, f'I - info') self.draw_text( 20, 400, f'Snake: [ {self.snake.get_head().x}, {self.snake.get_head().y} ]') self.draw_text( 20, 420, f'Apple: [ {self.apple.get_pos().x}, {self.apple.get_pos().x} ]') def draw_text(self, x, y, text): font = pygame.font.Font('./fonts/TerminusTTF.ttf', 14) self.sc.blit(font.render(text, True, pygame.Color('#50FA7B')), (x, y))