【发布时间】:2021-09-27 23:40:55
【问题描述】:
我最近开始学习 AI,我观看了 Code Bullet 的 this 视频,展示了一个非常简单的简单游戏的遗传算法,其中点需要达到目标。我想使用 pygame 在 python 中重新创建这个游戏。由于它根本不起作用,我尝试重新设计它。 代码如下:
from DotGame.DotGameFunctions import *
import random as r
import time
pg.init()
WIN_WIDTH = 500
WIN_HEIGHT = 500
win = pg.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
pg.display.set_caption('DotGame')
SPAWN = Vector2(WIN_WIDTH / 2, WIN_HEIGHT - 40)
GOAL = Vector2(WIN_WIDTH / 2, 10)
class Dot:
def __init__(self, pos: Vector2):
self.pos = pos
self.dead = False
self.reachedGoal = False
self.is_best = False
self.fitness = 0
self.brain = Brain(1000)
self.vel = Vector2()
def move(self):
if self.brain.step < len(self.brain.directions):
self.vel = self.brain.directions[self.brain.step]
self.brain.step += 1
else:
self.dead = True
self.pos = self.pos + self.vel
def update(self):
if not self.dead and not self.reachedGoal:
self.move()
if self.pos.x < 5 or self.pos.y < 5 or self.pos.x > WIN_WIDTH - 5 or self.pos.y > WIN_HEIGHT - 5:
self.dead = True
elif dis(self.pos.x, self.pos.y, GOAL.x, GOAL.y) < 5:
self.reachedGoal = True
if self.is_best:
color = (0, 255, 0)
width = 7
else:
color = (0, 0, 0)
width = 5
if self.fitness > .005:
color = (0, 0, 255)
width = 7
pg.draw.circle(win, color, tuple(self.pos), width)
def calculate_fitness(self):
if self.reachedGoal:
self.fitness = 1 / 16 + 10000 / self.brain.step
else:
distance_to_goal = dis(self.pos.x, self.pos.y, GOAL.x, GOAL.y)
self.fitness = 1 / distance_to_goal
def make_baby(self):
baby = Dot(SPAWN)
baby.brain = self.brain.clone()
return baby
class Goal:
def __init__(self):
self.pos = GOAL
def update(self):
pg.draw.circle(win, (255, 0, 0), tuple(self.pos), 10)
class Brain:
def __init__(self, size):
self.size = size
self.step = 0
self.directions = []
self.randomize()
self.mutationRate = 1
def randomize(self):
self.directions = []
for i in range(self.size):
self.directions.append(Vector2.from_angle(r.random() * 2 * math.pi) * 4)
def clone(self):
clone = Brain(len(self.directions))
clone.directions = self.directions
return clone
def mutate(self):
for i in range(len(self.directions) - 1):
rand = r.random()
if rand < self.mutationRate:
self.directions[i] = Vector2.from_angle(r.random() * 2 * math.pi) * 4
class Population:
def __init__(self, size):
self.size = size
self.Dots = []
for i in range(size):
self.Dots.append(Dot(SPAWN))
self.minStep = 20
self.gen = 0
self.bestDot = 0
self.fit_sum = 0
def update(self):
for d in self.Dots:
d.update()
def fitness(self):
for d in self.Dots:
d.calculate_fitness()
def end_of_generation(self):
for d in self.Dots:
if not d.dead and not d.reachedGoal:
return False
return True
def natural_selection(self):
self.fitness()
new_dots = sorted(self.Dots, key=lambda x: x.fitness)
new_dots.reverse()
new_dots = new_dots[:len(new_dots)//2] * 2
print([i.fitness for i in new_dots])
self.Dots = [d.make_baby() for d in new_dots]
self.Dots[0].is_best = True
for d in range(len(self.Dots) // 2, len(self.Dots)):
self.Dots[d].brain.mutate()
test = Population(100)
goal = Goal()
run = True
while run:
win.fill('white')
goal.update()
if test.end_of_generation():
test.natural_selection()
time.sleep(1)
else:
test.update()
# for event in pg.event.get():
# if event.type == pg.QUIT:
# run = False
pg.display.update()
time.sleep(0.005)
pg.quit()
这段代码的重要部分是人口类中的 natural_selection() 函数
def natural_selection(self):
self.fitness()
new_dots = sorted(self.Dots, key=lambda x: x.fitness)
new_dots.reverse()
new_dots = new_dots[:len(new_dots)//2] * 2
print([i.fitness for i in new_dots])
self.Dots = [d.make_baby() for d in new_dots]
self.Dots[0].is_best = True
for d in range(len(self.Dots) // 2, len(self.Dots)):
self.Dots[d].brain.mutate()
它所做的(或应该做的)是计算点的适应度,按适应度从高到低对点列表进行排序,将列表切成两半并复制它,所以前半部分和后半部分是相同,然后将第一个点设置为最佳并改变后半部分。 问题在于,正如第 6 行中的打印所示,它并没有真正改变点,结果是在每一代中它只需要相同的重复列表并对其进行排序,并且适应度如下所示:
[0.003441755148372998, 0.0034291486755453414, 0.003070574887525978, 0.0030339596318951327, 0.003030855079397534,...]
[0.00481387362410465, 0.00481387362410465, 0.003468488512721536, 0.003468488512721536, 0.0032419920180191478,...]
[0.004356736047656708, 0.004356736047656708, 0.004356736047656708, 0.004356736047656708, 0.003056862712974015,...]
我检查了 brain.mutate 函数,它似乎工作得很好。
我想不出任何可能导致此问题的原因,因为一切似乎都运行良好。 我希望我已经足够清楚了。
编辑:我导入了一个名为 DotGame.DotGameFunctions 但忘记显示的文件:
import math
class Vector2:
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
def __add__(self, other):
return Vector2(self.x + other.x, self.y + other.y)
def __mul__(self, other: int):
return Vector2(self.x * other, self.y * other)
def __iadd__(self, other):
self.x += other.x
self.y += other.y
def __iter__(self):
yield self.x
yield self.y
def copy(self):
return Vector2(self.x, self.y)
@staticmethod
def from_angle(angle):
return Vector2(math.cos(angle), math.sin(angle))
def dis(x1, y1, x2, y2):
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
【问题讨论】:
-
有什么东西可以随机化这个游戏的环境吗? (即一个点可以根据迭代有不同的适应度)?
-
适应度直接根据离目标的距离计算
标签: python artificial-intelligence genetic-algorithm