-3

I created an AI in python/pygame but even after spending hours of debugging, I could not find why the individuals(dots) are not getting mutated. After few generations, all the individuals just overlap each other and follow the same exact path. But after mutation they should move a little bit differently.

Here is what a population size of 10 looks like after every 2-3 generations..

Image 1 Image 2 Image 3

As you can see, just after few generations they just overlap and all the individuals in the population move together, following exact same path! We need mutations!!!

I would be really grateful to you if you could find any mistake. Thank!

I saw the code from: https://www.youtube.com/watch?v=BOZfhUcNiqk&t and tried to make it in python. Here's my code

import pygame, random
import numpy as np

pygame.init()
width = 800
height = 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("The Dots")

FPS = 30
clock = pygame.time.Clock()
gameExit = False

grey = [30, 30, 30]
white = [255, 255, 255]
black = [0, 0, 0]
red = [255, 0, 0]

goal = [400, 10]

class Dot():
    def __init__(self):
        self.x = int(width/2)
        self.y = int(height - 150)
        self.r = 3
        self.c = black
        self.xVel = self.yVel = 0
        self.xAcc = 0
        self.yAcc = 0
        self.dead = False
        self.steps = 0
        self.reached = False
        self.brain = Brain(200)

    def show(self):
        pygame.draw.circle(screen, self.c, [int(self.x), int(self.y)], self.r)

    def update(self):
        if (self.x >= width or self.x <= 0 or self.y >= height or self.y <= 0):
            self.dead = True
        elif (np.sqrt((self.x-goal[0])**2 + (self.y-goal[1])**2) < 5):
            self.reached = True
        if not self.dead and not self.reached:
            if len(self.brain.directions) > self.steps:
                self.xAcc = self.brain.directions[self.steps][0]
                self.yAcc = self.brain.directions[self.steps][1]
                self.steps += 1

                self.xVel += self.xAcc
                self.yVel += self.yAcc
                if self.xVel > 5:
                    self.xVel = 5
                if self.yVel > 5:
                    self.yVel = 5
                self.x += self.xVel
                self.y += self.yVel
            else: self.dead = True

    def calculateFitness(self):
        distToGoal = np.sqrt((self.x-goal[0])**2 + (self.y-goal[1])**2)
        self.fitness = 1/(distToGoal**2)
        return self.fitness

    def getChild(self):
        child = Dot()
        child.brain = self.brain
        return child

class Brain():
    def __init__(self, size):
        self.size = size
        self.directions = []
        self.randomize()

    def randomize(self):
        self.directions.append((np.random.normal(size=(self.size, 2))).tolist())
        self.directions = self.directions[0]

    def mutate(self):
        for i in self.directions:
            rand = random.random()
            if rand < 1:
                i = np.random.normal(size=(1, 2)).tolist()[0]

class Population():
    def __init__(self, size):
        self.size = size
        self.dots = []
        self.fitnessSum = 0

        for i in range(self.size):
            self.dots.append(Dot())

    def show(self):
        for i in self.dots:
            i.show()

    def update(self):
        for i in self.dots:
            i.update()

    def calculateFitness(self):
        for i in self.dots:
            i.calculateFitness()

    def allDead(self):
        for i in self.dots:
            if not i.dead and not i.reached:
                return False
        return True

    def calculateFitnessSum(self):
        self.fitnessSum = 0
        for i in self.dots:
            self.fitnessSum += i.fitness

    def SelectParent(self):
        rand = random.uniform(0, self.fitnessSum)
        runningSum = 0
        for i in self.dots:
            runningSum += i.fitness
            if runningSum > rand:
                return i

    def naturalSelection(self):
        newDots = []
        self.calculateFitnessSum()
        for i in self.dots:
            parent = self.SelectParent()
            newDots.append(parent.getChild())

        self.dots = newDots

    def mutate(self):
        for i in self.dots:
            i.brain.mutate()

test = Population(100)

while not gameExit:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            gameExit = True
    screen.fill(white)

    if test.allDead():
        #Genetic Algorithm
        test.calculateFitness()
        test.naturalSelection()
        test.mutate()

    else:
        test.update()
        test.show()

    pygame.draw.circle(screen, red, goal, 4)
    clock.tick(FPS)
    pygame.display.update()
pygame.quit()

Thanks for any help!

Sparsh
  • 1
  • 3
  • What do you mean they're not getting mutated? could you be more specific? – Stanley Sep 03 '18 at 08:52
  • Hey I just added few pictures and clarified my point. I would suggest you to test the code yourself to understand it better. Thanks! – Sparsh Sep 03 '18 at 10:59
  • Welcome to StackOverflow. Please read and follow the posting guidelines in the help documentation, as suggested when you created this account. [Minimal, complete, verifiable example](http://stackoverflow.com/help/mcve) applies here. We cannot effectively help you until you post your MCVE code and accurately describe the problem. See this lovely [debug](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) blog for help. – Prune Sep 04 '18 at 16:12

1 Answers1

0

I didn't go through the whole code, but over here

def mutate(self):
    for i in self.directions:
        rand = random.random()
        if rand < 1:
            i = np.random.normal(size=(1, 2)).tolist()[0]

you are trying to assign a new value to i (which is an iterater), so it won't change anything, which explains why you'r having trouble with the mutations.

You should have something like this:

def mutate(self):
    for i in range(len(self.directions)):
        rand = random.random()
        if rand < 1:
            self.directions[i] = np.random.normal(size=(1, 2)).tolist()[0]

or you can use list comprehensions https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions