diff --git a/brain.py b/brain.py index 884f5c6..5cddd03 100644 --- a/brain.py +++ b/brain.py @@ -1,17 +1,25 @@ import numpy as np +import random +def mat_mult(A,B): + return [[sum([A[i][m]*B[m][j] for m in range(len(A[0]))]) for j in range(len(B[0]))] for i in range(len(A))] class Neural_Network(object): # inspired from https://enlight.nyc/projects/neural-network/ - def __init__(self): + def __init__(self, W1=None, W2=None): #parameters self.inputSize = 3 self.outputSize = 2 self.hiddenSize = 3 + self.fitness = 0 #weights - self.W1 = np.random.randn(self.inputSize, self.hiddenSize) # weights from input to hidden layer - self.W2 = np.random.randn(self.hiddenSize, self.outputSize) # weights from hidden to output layer + if not W1 : + self.W1 = np.random.randn(self.inputSize, self.hiddenSize) # weights from input to hidden layer + if not W2 : + self.W2 = np.random.randn(self.hiddenSize, self.outputSize) # weights from hidden to output layer + # self.w1 = [[random.random() for i in range(self.hiddenSize)] for i in range(self.inputSize)] + # self.w2 = [[random.random() for i in range(self.outputSize)] for i in range(self.hiddenSize)] def predict(self, X): #forward propagation through our network @@ -19,6 +27,10 @@ class Neural_Network(object): self.z2 = self.sigmoid(self.z) # activation function self.z3 = np.dot(self.z2, self.W2) # dot product of hidden layer (z2) and second set of 3x1 weights o = self.sigmoid(self.z3) # final activation function + # self.z = mat_mult(X, self.w1) # dot product of X (input) and first set of 3x2 weights + # self.z2 = self.sigmoid(self.z) # activation function + # self.z3 = mat_mult(self.z2, self.w2) # dot product of hidden layer (z2) and second set of 3x1 weights + # o = self.sigmoid(self.z3) # final activation function return o def sigmoid(self, s): diff --git a/car.py b/car.py index 3272945..684a51c 100644 --- a/car.py +++ b/car.py @@ -2,7 +2,7 @@ import numpy as np import pygame from brain import Neural_Network -from params import GY, CAR_SIZE, VISION_LENGTH, VISION_SPAN, THROTTLE_POWER, screen +from params import GY, CAR_MAX_SPEED, CAR_SIZE, CAR_STEERING_FACTOR, VISION_LENGTH, VISION_SPAN, THROTTLE_POWER, screen from trigo import angle_to_vector, get_line_feats, segments_intersection, distance IMG = pygame.image.load("car20.png")#.convert() @@ -65,6 +65,7 @@ class Car(pygame.sprite.Sprite): old_center = self.rect.center self.rect.center = (self.speed * vec[0] / 2 + old_center[0], -self.speed * vec[1] / 2 + old_center[1]) self.update_sensors() + self.brain.fitness += int(distance(old_center, self.rect.center)) @@ -81,6 +82,7 @@ class Car(pygame.sprite.Sprite): self.probes[idx] = min(dist, self.probes[idx]) if dist < 1.2 * self.speed or self.speed < 0.01 : self.run = False + self.speed = 0 print(f'Car {id(self)} crashed') # else : @@ -113,7 +115,7 @@ class Car(pygame.sprite.Sprite): ) if self.speed : - self.heading += self.heading_change * 10 / self.speed + self.heading += self.heading_change * CAR_STEERING_FACTOR / self.speed self.heading = self.heading % 360 self.speed += self.throttle #THROTTLE_POWER @@ -123,7 +125,7 @@ class Car(pygame.sprite.Sprite): # self.speed -= self.throttle #THROTTLE_POWER self.speed = max(0, self.speed) - self.speed = min(self.speed, 7) + self.speed = min(self.speed, CAR_MAX_SPEED) super().update() diff --git a/genetics.py b/genetics.py new file mode 100644 index 0000000..90b29c5 --- /dev/null +++ b/genetics.py @@ -0,0 +1,56 @@ +import numpy as np +import random +from brain import Neural_Network + +def genetic_selection(brains): + # tot_fitness = sum ([int(b.fitness) for b in brains]) + + # does not seem very optimized... TBR + # constitute a list where every brain is represented + # proportionnally to its relative fitness + wheel = [] + for b in brains : + wheel += [b] * b.brains + + + tot_fitness = len(wheel) + half_pop = int(len(brains)/2) + + # selection of pool/2 pair of parents to reproduce + parents_pool = [] + for _ in range(half_pop): + parents_pool.append([round(random.random()*tot_fitness), round(random.random()*tot_fitness)]) + + +def cross_mutate_genes(p1_gene, p2_gene): + child = [] + p1_gene = list(p1_gene) + p2_gene = list(p1_gene) + for idx,x in enumerate(p2_gene): + if random.random() > 0.5 : + choice = p1_gene + else : + choice = p2_gene + # Mutation + if random.random() < 0.005 : + choice[random.randint(0, len(choice - 1))] = random.random() + print("Mutation !") + child.append(np.array(choice)) + return child + + +def genetic_reproduction(parents_pool): + # every pair of parents will produce a mixed child + new_pop = [] + for [p1,p2] in parents_pool: + W1_kid = cross_mutate_genes(p1.W1, p2.W1) + W2_kid = cross_mutate_genes(p1.W2, p2.W2) + c_brain1 = Neural_Network(W1=W1_kid, W2=W2_kid) + c_brain2 = Neural_Network(W1=W1_kid, W2=W2_kid) + new_pop.append(c_brain1) + new_pop.append(c_brain2) + return new_pop + + + + diff --git a/main.py b/main.py index ab10a35..7ffd982 100755 --- a/main.py +++ b/main.py @@ -43,6 +43,9 @@ while running_cars : pygame.display.flip() clock.tick(24) +for c in all_cars : + print(f"Car {id(c)} Fitness : {c.brain.fitness})") + while True : pygame.display.flip() clock.tick(24) diff --git a/params.py b/params.py index 0f87ff5..371d197 100644 --- a/params.py +++ b/params.py @@ -3,13 +3,16 @@ from pygame.locals import HWSURFACE, DOUBLEBUF FLAGS = HWSURFACE | DOUBLEBUF #| FULLSCREEN -GX = 1000 -GY = 1000 -CELL_COLOR = (80,80,80) -CAR_SIZE=20 -VISION_LENGTH = 50 -VISION_SPAN = 25 # degrees -THROTTLE_POWER = 3 +GX = 1000 +GY = 1000 +CELL_COLOR = (80,80,80) +CAR_SIZE = 20 +CAR_MAX_SPEED = 7 +CAR_STEERING_FACTOR = 10 +VISION_LENGTH = 50 +VISION_SPAN = 25 # degrees +THROTTLE_POWER = 3 + pygame.init() screen = pygame.display.set_mode((GX, GY), FLAGS) diff --git a/trigo.py b/trigo.py index 67122e4..3821fcb 100644 --- a/trigo.py +++ b/trigo.py @@ -17,7 +17,6 @@ def get_line_feats(point1, point2): return a,b - def segments_intersection(line1, line2): p1,p2 = line1 p3,p4 = line2 @@ -42,6 +41,5 @@ def segments_intersection(line1, line2): return None # intersect is outside segments - def distance(point1, point2): return math.hypot(point1[0] - point2[0], point1[1] - point2[1]) \ No newline at end of file