python 中 "smart dots" 的遗传算法不起作用

Genetic algorithm for "smart dots" in python doesn't work

在过去的几天里,我一直在尝试实现所谓的“智能点”游戏。我第一次看到它是在 Code Bullet youtube 频道:https://www.youtube.com/watch?v=BOZfhUcNiqk。不幸的是,它是用 Processing 语言编码的,而我几乎不知道的唯一语言是 Python。我完成了游戏的 python 版本,但出现了一些错误。

问题在于,在第二代中,被选为最佳点的点几乎立即停止移动。我认为这与我不擅长 OOP 和复制 Brain class 错误有关。步骤(我用于移动)在主循环的第一次或第二次循环中从零(在开始时设置)跳到最大值(200)。但问题还不止于此。在下一代,当我尝试将 brain step 设置为零时,它会中断:

AttributeError: 'NoneType' object has no attribute 'brain'

我尝试手动设置新大脑,但我仍然遇到同样的错误。如果有人已经做了这个或者有时间可以帮助我解决这个错误甚至项目,我将不胜感激。

我知道代码中有很多未使用的东西,但这只是我尝试修复它的结果 :(

注释掉的代码是我用过的一些旧代码。

main2.py(主循环): </p> <pre><code>import pygame import klase2 pygame.init() def main(): win = pygame.display.set_mode((klase2.WIN_W, klase2.WIN_H)) clock = pygame.time.Clock() population = klase2.Population() dots = population.return_dots(1000) goal = klase2.Goal() run = True while run: clock.tick(30) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False win.fill((255, 255, 255)) goal.draw_goal(win) for dot in dots: dot.draw_dot(win) dot.update_dot() if population.all_dots_dead(): # natural selection population.natural_selection() # mutation dots = population.mutate_dots() population.gen += 1 print(population.gen) pygame.display.update() main()

kase2(处理所有功能和 classes): </p> <pre><code>import pygame import numpy as np from pygame import gfxdraw import math import random pygame.init() WIN_W = 500 WIN_H = 500 class Brain: def __init__(self, size): self.step = 0 self.size = size self.directionsx = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.size / 2))) self.directionsy = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.size / 2))) def clone(self): self.size = self.size self.directionsx = self.directionsx self.directionsy = self.directionsy self.step = 0 class Goal: def __init__(self): self.x = WIN_W / 2 self.y = 10 self.color = (255, 20, 20) self.r = 5 def draw_goal(self, win): pygame.gfxdraw.aacircle(win, int(self.x), int(self.y), self.r, self.color) pygame.gfxdraw.filled_circle(win, int(self.x), int(self.y), self.r, self.color) class Dot: goal = Goal() def __init__(self): self.tick = 0 self.goal = Goal() self.brain = Brain(400) self.velx = 0 self.vely = 0 self.accx = 0 self.accy = 0 self.x = WIN_W / 2 self.y = WIN_H - 10 self.r = 3 self.color = (0, 0, 0) self.alive = True self.velLimit = 5 self.fitness = 0 def draw_dot(self, win): pygame.gfxdraw.aacircle(win, int(self.x), int(self.y), self.r, self.color) pygame.gfxdraw.filled_circle(win, int(self.x), int(self.y), self.r, self.color) def move_dot(self): if self.brain.size / 2 > self.brain.step: self.accx = self.brain.directionsx[self.brain.step] self.accy = self.brain.directionsy[self.brain.step] self.brain.step += 1 else: self.alive = False self.velx += self.accx self.vely += self.accy if self.velx > self.velLimit: self.velx = self.velLimit elif self.velx < -self.velLimit: self.velx = -self.velLimit if self.vely > self.velLimit: self.vely = self.velLimit elif self.vely < -self.velLimit: self.vely = -self.velLimit self.x += self.velx self.y += self.vely def update_dot(self): if not self.reached_goal(): self.tick += 1 if self.alive: self.move_dot() if self.x < 0 + self.r or self.x > WIN_W - self.r or self.y < 0 + self.r or self.y > WIN_H - self.r or self.reached_goal(): self.alive = False def distance_to_goal(self): a = abs(self.x - self.goal.x) b = abs(self.y - self.goal.y) return math.sqrt(a**2 + b**2) def reached_goal(self): if self.distance_to_goal() <= self.r + self.goal.r: return True return False def fitness_dot(self): if self.reached_goal(): self.fitness = 1 / (self.brain.step) else: self.fitness = 1 / (self.distance_to_goal()**2) return self.fitness class Population: def __init__(self): self.dots = [] self.newDots = [] self.gen = 0 self.mutateChance = 800 self.size = 0 self.fitness_sum = 0 def return_dots(self, size): self.size = size for _ in range(size): self.dots.append(Dot()) return self.dots def all_dots_dead(self): for i in range(len(self.dots)): if self.dots[i].alive: return False return True def sort_dots(self): self.dots = sorted(self.dots, key=lambda dot: dot.fitness, reverse=True) def sum_fitness(self): for dot in self.dots: self.fitness_sum += dot.fitness_dot() return self.fitness_sum def get_parent(self): rand = random.uniform(0, self.fitness_sum) running_sum = 0 for dot in self.dots: running_sum += dot.fitness if running_sum >= rand: return dot def natural_selection(self): for dot in self.dots: dot.fitness_dot() self.sort_dots() self.newDots.append(self.dots[0]) self.sum_fitness() for i in range(1, len(self.dots)): parent = self.get_parent() self.newDots.append(Dot()) self.newDots[i].brain = parent.brain self.newDots[i].brain.step = 0 self.dots = self.newDots def mutate_dots(self): for i in range(1, len(self.dots)): rand = random.randint(0, 1000) if rand > self.mutateChance: self.dots[i].brain.directionsx = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.dots[i].brain.size / 2))) self.dots[i].brain.directionsy = np.array(np.random.uniform(low=-2.5, high=2.5, size=int(self.dots[i].brain.size / 2))) return self.dots # def natural_selection(self): # self.selectedDots = [] # for dot in self.dots: # dot.fitness_dot() # self.sort_dots() # for i in range(0, int(len(self.dots) / 3)): # self.selectedDots.append(self.dots[i]) # self.selectedDots[i].tick = 0 # self.selectedDots[i].velx = 0 # self.selectedDots[i].vely = 0 # self.selectedDots[i].accx = 0 # self.selectedDots[i].accy = 0 # self.selectedDots[i].x = WIN_W / 2 # self.selectedDots[i].y = WIN_H - 10 # self.selectedDots[i].alive = True # self.selectedDots[i].fitness = 0 # self.selectedDots[i].brain.step = 0 # self.selectedDots[i].goal = Goal() # # def new_dots(self): # for i in range(len(self.selectedDots), len(self.dots)): # self.selectedDots.append(Dot()) # self.dots = self.selectedDots # # def mutate_dots(self): # for i, dot in enumerate(self.dots): # isMutating = random.randint(0, 1000) # if self.mutateChance > isMutating and i > int(len(self.dots) / 3) and i < (2 * int((len(self.dots) / 3))): # for j in range(len(dot.brain.directionsx)): # isMutatingDir = random.randint(0, 1000) # if isMutatingDir >= 800: # dot.brain.directionsx[j] = np.random.uniform(low=-2.5, high=2.5, size=1) # for j in range(len(dot.brain.directionsy)): # isMutatingDir = random.randint(0, 1000) # if isMutatingDir >= 800: # dot.brain.directionsy[j] = np.random.uniform(low=-2.5, high=2.5, size=1) # return self.dots ''' def natural_selection(self): self.selectedDots = [] for dot in self.dots: dot.fitness_dot() self.sort_dots() self.selectedDots = self.dots[0:int(0.3 * len(self.dots))] def new_dots(self): for i in range(len(self.dots) - int(0.3 * len(self.dots))): self.selectedDots.append(self.dots[i]) self.dots = [] def mutate_dots(self): for i, selectedDot in enumerate(self.selectedDots): self.tick = 0 self.x = WIN_W / 2 self.y = WIN_H - 10 self.r = 3 self.alive = True self.velLimit = 5 self.fitness = 0 self.dots = self.selectedDots return self.dots ''' ''' def mutate_dots(self): for i, selectedDot in enumerate(self.selectedDots): selectedDot.alive = True if i >= 1: isMutating = random.randint(0, 1000) if isMutating <= self.mutateChance: for j in range(len(selectedDot.brain.directionsx)): isMutatingDir = random.randint(0, 1000) if isMutatingDir >= 800: selectedDot.brain.directionsx[j] = np.random.uniform(low=-2.5, high=2.5, size=1) for j in range(len(selectedDot.brain.directionsy)): isMutatingDir = random.randint(0, 1000) if isMutatingDir >= 800: selectedDot.brain.directionsy[j] = np.random.uniform(low=-2.5, high=2.5, size=1) elif isMutating <= 800: selectedDot.brain.directionsx = np.array(np.random.uniform(low=-2.5, high=2.5, size=200)) selectedDot.brain.directionsy = np.array(np.random.uniform(low=-2.5, high=2.5, size=200)) self.newDots.append(selectedDot) return self.newDots '''

None类型错误是由get_parent方法引起的。它搜索子点,但如果搜索失败则没有 return 值(与 return None 效果相同)。此代码将克服该错误

def get_parent(self):
    rand = random.uniform(0, self.fitness_sum)
    running_sum = 0
    for dot in self.dots:
        running_sum += dot.fitness
        if running_sum >= rand:
            return dot
    return self.dots[0]  # search failed, return 1st dot