├── pipe-red.png ├── pipeDown.png ├── redbird.png ├── background.png ├── pipe-green.png ├── README.md ├── config ├── flappyBird2.py ├── testBird.py └── flappyBird3.py /pipe-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/neuroevolution-for-flappy-birds/HEAD/pipe-red.png -------------------------------------------------------------------------------- /pipeDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/neuroevolution-for-flappy-birds/HEAD/pipeDown.png -------------------------------------------------------------------------------- /redbird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/neuroevolution-for-flappy-birds/HEAD/redbird.png -------------------------------------------------------------------------------- /background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/neuroevolution-for-flappy-birds/HEAD/background.png -------------------------------------------------------------------------------- /pipe-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llSourcell/neuroevolution-for-flappy-birds/HEAD/pipe-green.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NEAT_FlappyBird 2 | 3 | 4 | # Coding Challenge - Due Date, Thursday, Feb 15 at 12 PM PST 5 | 6 | Create your own NEAT bot that plays a game of your choice. It could be a simulated environment really. Bonus points for creativity and good documentation. Post your github links in the comment section of the video. Good luck! 7 | 8 | ## Overview 9 | 10 | This is the code for [this](https://youtu.be/hVv68aHYSs4) video on Youtube by Siraj Raval. 11 | 12 | Implementation of the NEAT algorithm for learning an agent to play Flappy Bird. 13 | 14 | The game was designed using the pygame library while NEAT was implemented using the Python-NEAT package. 15 | 16 | flappyBird2.py : Basic python game. Allows user to play Flappy Bird 17 | 18 | flappyBird3.py : Implements NEAT algorithm to train game playing bot ( Neural Network ) 19 | 20 | testBird.py : Runs 10 runs of a given trained model to evaluate its performance 21 | 22 | 23 | ## Credits 24 | 25 | Credits for this code go to [rsk237](https://github.com/rsk2327/NEAT_FlappyBird). I've merely created a wrapper to get people started. 26 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | # NEAT configuration for the bit-sequence memory experiment. 2 | 3 | # The `NEAT` section specifies parameters particular to the NEAT algorithm 4 | # or the experiment itself. This is the only required section. 5 | [NEAT] 6 | fitness_criterion = max 7 | fitness_threshold = 600.0 8 | pop_size = 100 9 | reset_on_extinction = 0 10 | 11 | [DefaultGenome] 12 | num_inputs = 4 13 | num_hidden = 1 14 | num_outputs = 1 15 | initial_connection = partial_direct 0.5 16 | feed_forward = True 17 | compatibility_disjoint_coefficient = 1.0 18 | compatibility_weight_coefficient = 0.6 19 | conn_add_prob = 0.2 20 | conn_delete_prob = 0.2 21 | node_add_prob = 0.2 22 | node_delete_prob = 0.2 23 | activation_default = sigmoid 24 | activation_options = sigmoid 25 | activation_mutate_rate = 0.0 26 | aggregation_default = sum 27 | aggregation_options = sum 28 | aggregation_mutate_rate = 0.0 29 | bias_init_mean = 0.0 30 | bias_init_stdev = 1.0 31 | bias_replace_rate = 0.1 32 | bias_mutate_rate = 0.7 33 | bias_mutate_power = 0.5 34 | bias_max_value = 30.0 35 | bias_min_value = -30.0 36 | response_init_mean = 1.0 37 | response_init_stdev = 0.0 38 | response_replace_rate = 0.0 39 | response_mutate_rate = 0.0 40 | response_mutate_power = 0.0 41 | response_max_value = 30.0 42 | response_min_value = -30.0 43 | 44 | weight_max_value = 30 45 | weight_min_value = -30 46 | weight_init_mean = 0.0 47 | weight_init_stdev = 1.0 48 | weight_mutate_rate = 0.8 49 | weight_replace_rate = 0.1 50 | weight_mutate_power = 0.5 51 | enabled_default = True 52 | enabled_mutate_rate = 0.01 53 | 54 | [DefaultSpeciesSet] 55 | compatibility_threshold = 3.0 56 | 57 | [DefaultStagnation] 58 | species_fitness_func = max 59 | max_stagnation = 20 60 | 61 | [DefaultReproduction] 62 | elitism = 2 63 | survival_threshold = 0.2 64 | 65 | -------------------------------------------------------------------------------- /flappyBird2.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | from numpy.random import randint,choice 3 | import sys 4 | 5 | 6 | import pygame 7 | from pygame.locals import * 8 | 9 | FPS = 30 10 | SCREENWIDTH = 288 11 | SCREENHEIGHT = 512 12 | # amount by which base can maximum shift to left 13 | PIPEGAPSIZE = 160 # gap between upper and lower part of pipe 14 | BASEY = SCREENHEIGHT * 0.79 15 | SCORE = 0 16 | 17 | BACKGROUND = pygame.image.load('/home/roshan/Documents/FlappyBird/background.png') 18 | 19 | 20 | class Bird(pygame.sprite.Sprite): 21 | 22 | def __init__(self,displayScreen): 23 | 24 | pygame.sprite.Sprite.__init__(self) 25 | 26 | self.image = pygame.image.load('/home/roshan/Documents/FlappyBird/redbird.png') 27 | 28 | self.x = int(SCREENWIDTH * 0.2) 29 | self.y = SCREENHEIGHT*0.5 30 | 31 | self.rect = self.image.get_rect() 32 | self.height = self.rect.height 33 | self.screen = displayScreen 34 | 35 | self.playerVelY = -9 36 | self.playerMaxVelY = 10 37 | self.playerMinVelY = -8 38 | self.playerAccY = 1 39 | self.playerFlapAcc = -9 40 | self.playerFlapped = False 41 | 42 | self.display(self.x, self.y) 43 | 44 | def display(self,x,y): 45 | 46 | self.screen.blit(self.image, (x,y)) 47 | self.rect.x, self.rect.y = x,y 48 | 49 | 50 | def move(self,input): 51 | 52 | if input != None: 53 | self.playerVelY = self.playerFlapAcc 54 | self.playerFlapped = True 55 | 56 | if self.playerVelY < self.playerMaxVelY and not self.playerFlapped: 57 | self.playerVelY += self.playerAccY 58 | if self.playerFlapped: 59 | self.playerFlapped = False 60 | 61 | self.y += min(self.playerVelY, SCREENHEIGHT - self.y - self.height) 62 | self.y = max(self.y,0) 63 | self.display(self.x,self.y) 64 | 65 | 66 | class PipeBlock(pygame.sprite.Sprite): 67 | 68 | def __init__(self,image,upper): 69 | 70 | pygame.sprite.Sprite.__init__(self) 71 | 72 | if upper == False: 73 | self.image = pygame.image.load(image) 74 | else: 75 | self.image = pygame.transform.rotate(pygame.image.load(image),180) 76 | 77 | self.rect = self.image.get_rect() 78 | 79 | 80 | 81 | class Pipe(pygame.sprite.Sprite): 82 | 83 | 84 | def __init__(self,screen,x): 85 | 86 | pygame.sprite.Sprite.__init__(self) 87 | 88 | self.screen = screen 89 | self.lowerBlock = PipeBlock('/home/roshan/Documents/FlappyBird/pipe-red.png',False) 90 | self.upperBlock = PipeBlock('/home/roshan/Documents/FlappyBird/pipe-red.png',True) 91 | 92 | 93 | self.pipeWidth = self.upperBlock.rect.width 94 | self.x = x 95 | 96 | 97 | heights = self.getHeight() 98 | self.upperY, self.lowerY = heights[0], heights[1] 99 | 100 | self.behindBird = 0 101 | self.display() 102 | 103 | 104 | def getHeight(self): 105 | 106 | # randVal = randint(1,10) 107 | randVal = choice([1,2,3,4,5,6,7,8,9], p =[0.04,0.04*2,0.04*3,0.04*4,0.04*5,0.04*4,0.04*3,0.04*2,0.04] ) 108 | 109 | midYPos = 106 + 30*randVal 110 | 111 | upperPos = midYPos - (PIPEGAPSIZE/2) 112 | lowerPos = midYPos + (PIPEGAPSIZE/2) 113 | 114 | # print(upperPos) 115 | # print(lowerPos) 116 | # print('-------') 117 | return([upperPos,lowerPos]) 118 | 119 | def display(self): 120 | 121 | self.screen.blit(self.lowerBlock.image, (self.x, self.lowerY)) 122 | self.screen.blit(self.upperBlock.image, (self.x, self.upperY - self.upperBlock.rect.height)) 123 | self.upperBlock.rect.x, self.upperBlock.rect.y = self.x, (self.upperY - self.upperBlock.rect.height) 124 | self.lowerBlock.rect.x, self.lowerBlock.rect.y = self.x, self.lowerY 125 | 126 | def move(self): 127 | 128 | self.x -= 3 129 | 130 | if self.x <= 0: 131 | self.x = SCREENWIDTH 132 | heights = self.getHeight() 133 | self.upperY, self.lowerY = heights[0], heights[1] 134 | self.behindBird = 0 135 | 136 | self.display() 137 | return([self.x+(self.pipeWidth/2), self.upperY, self.lowerY]) 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | def game(): 149 | 150 | 151 | 152 | pygame.init() 153 | 154 | FPSCLOCK = pygame.time.Clock() 155 | DISPLAY = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT)) 156 | pygame.display.set_caption('Flappy Bird') 157 | 158 | global SCORE 159 | 160 | bird = Bird(DISPLAY) 161 | pipe1 = Pipe(DISPLAY, SCREENWIDTH+100) 162 | pipe2 = Pipe(DISPLAY, SCREENWIDTH+100+(SCREENWIDTH/2)) 163 | 164 | pipeGroup = pygame.sprite.Group() 165 | pipeGroup.add(pipe1.upperBlock) 166 | pipeGroup.add(pipe2.upperBlock) 167 | pipeGroup.add(pipe1.lowerBlock) 168 | pipeGroup.add(pipe2.lowerBlock) 169 | 170 | # birdGroup = pygame.sprite.Group() 171 | # birdGroup.add(bird1) 172 | 173 | 174 | moved = False 175 | pause =0 176 | 177 | while True: 178 | 179 | DISPLAY.blit(BACKGROUND,(0,0)) 180 | 181 | # if (pipe1.x < pipe2.x and pipe1.behindBird==0) or (pipe2.x < pipe1.x and pipe2.behindBird==1): 182 | # input = (bird.y,pipe1.x, pipe1.upperY, pipe1.lowerY) 183 | # centerY = (pipe1.upperY + pipe1.lowerY)/2 184 | # elif (pipe1.x < pipe2.x and pipe1.behindBird==1) or (pipe2.x < pipe1.x and pipe2.behindBird==0): 185 | # input = (bird.y,pipe2.x, pipe2.upperY, pipe2.lowerY) 186 | # centerY = (pipe2.upperY + pipe2.lowerY)/2 187 | 188 | # print(input) 189 | 190 | t = pygame.sprite.spritecollideany(bird,pipeGroup) 191 | 192 | if t!=None or (bird.y== 512 - bird.height) or (bird.y == 0): 193 | print("GAME OVER") 194 | print("FINAL SCORE IS %d"%SCORE) 195 | return(SCORE) 196 | 197 | 198 | 199 | for event in pygame.event.get(): 200 | if event.type == QUIT or (event.type == KEYDOWN and (event.key == K_ESCAPE )): 201 | pygame.quit() 202 | sys.exit() 203 | if event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_RETURN): 204 | bird.move("UP") 205 | moved = True 206 | if event.type == KEYDOWN and event.key == K_m : 207 | pause=1 208 | 209 | 210 | if moved == False: 211 | bird.move(None) 212 | else: 213 | moved = False 214 | 215 | 216 | pipe1Pos = pipe1.move() 217 | if pipe1Pos[0] <= int(SCREENWIDTH * 0.2) - int(bird.rect.width/2): 218 | if pipe1.behindBird == 0: 219 | pipe1.behindBird = 1 220 | SCORE += 1 221 | print("SCORE IS %d"%SCORE) 222 | 223 | pipe2Pos = pipe2.move() 224 | if pipe2Pos[0] <= int(SCREENWIDTH * 0.2) - int(bird.rect.width/2): 225 | if pipe2.behindBird == 0: 226 | pipe2.behindBird = 1 227 | SCORE += 1 228 | print("SCORE IS %d"%SCORE) 229 | 230 | 231 | 232 | if pause==0: 233 | pygame.display.update() 234 | 235 | FPSCLOCK.tick(FPS) 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | game() 244 | 245 | -------------------------------------------------------------------------------- /testBird.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | from numpy.random import randint,choice 3 | import sys 4 | import neat 5 | import cPickle as pickle 6 | import os 7 | 8 | 9 | import pygame 10 | from pygame.locals import * 11 | 12 | FPS = 200 13 | SCREENWIDTH = 288 14 | SCREENHEIGHT = 512 15 | # amount by which base can maximum shift to left 16 | PIPEGAPSIZE = 180 # gap between upper and lower part of pipe 17 | BASEY = SCREENHEIGHT * 0.79 18 | SCORE = 0 19 | 20 | BACKGROUND = pygame.image.load('/home/roshan/Documents/FlappyBird/background.png') 21 | 22 | GENERATION = 0 23 | MAX_FITNESS = 0 24 | BEST_GENOME = 0 25 | 26 | class Bird(pygame.sprite.Sprite): 27 | 28 | def __init__(self,displayScreen): 29 | 30 | pygame.sprite.Sprite.__init__(self) 31 | 32 | self.image = pygame.image.load('/home/roshan/Documents/FlappyBird/redbird.png') 33 | 34 | self.x = int(SCREENWIDTH * 0.2) 35 | self.y = SCREENHEIGHT*0.5 36 | 37 | self.rect = self.image.get_rect() 38 | self.height = self.rect.height 39 | self.screen = displayScreen 40 | 41 | self.playerVelY = -9 42 | self.playerMaxVelY = 10 43 | self.playerMinVelY = -8 44 | self.playerAccY = 1 45 | self.playerFlapAcc = -9 46 | self.playerFlapped = False 47 | 48 | self.display(self.x, self.y) 49 | 50 | def display(self,x,y): 51 | 52 | self.screen.blit(self.image, (x,y)) 53 | self.rect.x, self.rect.y = x,y 54 | 55 | 56 | def move(self,input): 57 | 58 | if input != None: 59 | self.playerVelY = self.playerFlapAcc 60 | self.playerFlapped = True 61 | 62 | if self.playerVelY < self.playerMaxVelY and not self.playerFlapped: 63 | self.playerVelY += self.playerAccY 64 | if self.playerFlapped: 65 | self.playerFlapped = False 66 | 67 | self.y += min(self.playerVelY, SCREENHEIGHT - self.y - self.height) 68 | self.y = max(self.y,0) 69 | self.display(self.x,self.y) 70 | 71 | 72 | class PipeBlock(pygame.sprite.Sprite): 73 | 74 | def __init__(self,image,upper): 75 | 76 | pygame.sprite.Sprite.__init__(self) 77 | 78 | if upper == False: 79 | self.image = pygame.image.load(image) 80 | else: 81 | self.image = pygame.transform.rotate(pygame.image.load(image),180) 82 | 83 | self.rect = self.image.get_rect() 84 | 85 | 86 | 87 | class Pipe(pygame.sprite.Sprite): 88 | 89 | 90 | def __init__(self,screen,x): 91 | 92 | pygame.sprite.Sprite.__init__(self) 93 | 94 | self.screen = screen 95 | self.lowerBlock = PipeBlock('/home/roshan/Documents/FlappyBird/pipe-red.png',False) 96 | self.upperBlock = PipeBlock('/home/roshan/Documents/FlappyBird/pipe-red.png',True) 97 | 98 | 99 | self.pipeWidth = self.upperBlock.rect.width 100 | self.x = x 101 | 102 | 103 | heights = self.getHeight() 104 | self.upperY, self.lowerY = heights[0], heights[1] 105 | 106 | self.behindBird = 0 107 | self.display() 108 | 109 | 110 | def getHeight(self): 111 | 112 | # randVal = randint(1,10) 113 | randVal = choice([1,2,3,4,5,6,7,8,9], p =[0.04,0.04*2,0.04*3,0.04*4,0.04*5,0.04*4,0.04*3,0.04*2,0.04] ) 114 | 115 | midYPos = 106 + 30*randVal 116 | 117 | upperPos = midYPos - (PIPEGAPSIZE/2) 118 | lowerPos = midYPos + (PIPEGAPSIZE/2) 119 | 120 | # print(upperPos) 121 | # print(lowerPos) 122 | # print('-------') 123 | return([upperPos,lowerPos]) 124 | 125 | def display(self): 126 | 127 | self.screen.blit(self.lowerBlock.image, (self.x, self.lowerY)) 128 | self.screen.blit(self.upperBlock.image, (self.x, self.upperY - self.upperBlock.rect.height)) 129 | self.upperBlock.rect.x, self.upperBlock.rect.y = self.x, (self.upperY - self.upperBlock.rect.height) 130 | self.lowerBlock.rect.x, self.lowerBlock.rect.y = self.x, self.lowerY 131 | 132 | def move(self): 133 | 134 | self.x -= 3 135 | 136 | if self.x <= 0: 137 | self.x = SCREENWIDTH 138 | heights = self.getHeight() 139 | self.upperY, self.lowerY = heights[0], heights[1] 140 | self.behindBird = 0 141 | 142 | self.display() 143 | return([self.x+(self.pipeWidth/2), self.upperY, self.lowerY]) 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | def game(genome, config): 155 | 156 | net = neat.nn.FeedForwardNetwork.create(genome, config) 157 | 158 | pygame.init() 159 | 160 | FPSCLOCK = pygame.time.Clock() 161 | DISPLAY = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT)) 162 | pygame.display.set_caption('Flappy Bird') 163 | 164 | global SCORE 165 | 166 | bird = Bird(DISPLAY) 167 | pipe1 = Pipe(DISPLAY, SCREENWIDTH+100) 168 | pipe2 = Pipe(DISPLAY, SCREENWIDTH+100+(SCREENWIDTH/2)) 169 | 170 | pipeGroup = pygame.sprite.Group() 171 | pipeGroup.add(pipe1.upperBlock) 172 | pipeGroup.add(pipe2.upperBlock) 173 | pipeGroup.add(pipe1.lowerBlock) 174 | pipeGroup.add(pipe2.lowerBlock) 175 | 176 | # birdGroup = pygame.sprite.Group() 177 | # birdGroup.add(bird1) 178 | 179 | 180 | moved = False 181 | 182 | time = 0 183 | 184 | while True: 185 | 186 | DISPLAY.blit(BACKGROUND,(0,0)) 187 | 188 | if (pipe1.x < pipe2.x and pipe1.behindBird==0) or (pipe2.x < pipe1.x and pipe2.behindBird==1): 189 | input = (bird.y,pipe1.x, pipe1.upperY, pipe1.lowerY) 190 | centerY = (pipe1.upperY + pipe1.lowerY)/2 191 | elif (pipe1.x < pipe2.x and pipe1.behindBird==1) or (pipe2.x < pipe1.x and pipe2.behindBird==0): 192 | input = (bird.y,pipe2.x, pipe2.upperY, pipe2.lowerY) 193 | centerY = (pipe2.upperY + pipe2.lowerY)/2 194 | 195 | # print(input) 196 | vertDist = (((bird.y - centerY)**2)*100)/(512*512) 197 | time += 1 198 | 199 | fitness = SCORE - vertDist + (time/10.0) 200 | 201 | t = pygame.sprite.spritecollideany(bird,pipeGroup) 202 | 203 | if t!=None or (bird.y== 512 - bird.height) or (bird.y == 0): 204 | return(fitness) 205 | 206 | output = net.activate(input) 207 | 208 | # for event in pygame.event.get(): 209 | # if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): 210 | # pygame.quit() 211 | # sys.exit() 212 | 213 | if output[0]>=0.5: 214 | bird.move("UP") 215 | moved = True 216 | 217 | 218 | if moved == False: 219 | bird.move(None) 220 | else: 221 | moved = False 222 | 223 | 224 | pipe1Pos = pipe1.move() 225 | if pipe1Pos[0] <= int(SCREENWIDTH * 0.2): 226 | if pipe1.behindBird == 0: 227 | pipe1.behindBird = 1 228 | SCORE += 10 229 | # print("SCORE IS %d"%(SCORE+vertDist)) 230 | 231 | pipe2Pos = pipe2.move() 232 | if pipe2Pos[0] <= int(SCREENWIDTH * 0.2): 233 | if pipe2.behindBird == 0: 234 | pipe2.behindBird = 1 235 | SCORE += 10 236 | # print("SCORE IS %d"%(SCORE+vertDist)) 237 | 238 | 239 | 240 | 241 | pygame.display.update() 242 | FPSCLOCK.tick(FPS) 243 | 244 | 245 | 246 | def eval_genomes(genomes, config): 247 | i = 0 248 | global SCORE 249 | global GENERATION, MAX_FITNESS, BEST_GENOME 250 | 251 | GENERATION += 1 252 | for genome_id, genome in genomes: 253 | 254 | genome.fitness = game(genome, config) 255 | print("Gen : %d Genome # : %d Fitness : %f Max Fitness : %f"%(GENERATION,i,genome.fitness, MAX_FITNESS)) 256 | if genome.fitness >= MAX_FITNESS: 257 | MAX_FITNESS = genome.fitness 258 | BEST_GENOME = genome 259 | SCORE = 0 260 | i+=1 261 | 262 | config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, 263 | neat.DefaultSpeciesSet, neat.DefaultStagnation, 264 | 'config') 265 | 266 | 267 | genomeFile = '/home/roshan/Documents/FlappyBird/bestGenomes/2_732.p' 268 | genome = pickle.load(open(genomeFile,'rb')) 269 | 270 | fitnessScores = [] 271 | 272 | for i in range(10): 273 | fitness = game(genome, config) 274 | SCORE = 0 275 | print('Fitness is %f'% fitness) 276 | fitnessScores.append(fitness) 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | -------------------------------------------------------------------------------- /flappyBird3.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | from numpy.random import randint,choice 3 | import sys 4 | import neat 5 | import cPickle as pickle 6 | import os 7 | 8 | 9 | import pygame 10 | from pygame.locals import * 11 | 12 | FPS = 200 13 | SCREENWIDTH = 288 14 | SCREENHEIGHT = 512 15 | # amount by which base can maximum shift to left 16 | PIPEGAPSIZE = 160 # gap between upper and lower part of pipe 17 | BASEY = SCREENHEIGHT * 0.79 18 | SCORE = 0 19 | 20 | BACKGROUND = pygame.image.load('/home/roshan/Documents/FlappyBird/background.png') 21 | 22 | GENERATION = 0 23 | MAX_FITNESS = 0 24 | BEST_GENOME = 0 25 | 26 | class Bird(pygame.sprite.Sprite): 27 | 28 | def __init__(self,displayScreen): 29 | 30 | pygame.sprite.Sprite.__init__(self) 31 | 32 | self.image = pygame.image.load('/home/roshan/Documents/FlappyBird/redbird.png') 33 | 34 | self.x = int(SCREENWIDTH * 0.2) 35 | self.y = SCREENHEIGHT*0.5 36 | 37 | self.rect = self.image.get_rect() 38 | self.height = self.rect.height 39 | self.screen = displayScreen 40 | 41 | self.playerVelY = -9 42 | self.playerMaxVelY = 10 43 | self.playerMinVelY = -8 44 | self.playerAccY = 1 45 | self.playerFlapAcc = -9 46 | self.playerFlapped = False 47 | 48 | self.display(self.x, self.y) 49 | 50 | def display(self,x,y): 51 | 52 | self.screen.blit(self.image, (x,y)) 53 | self.rect.x, self.rect.y = x,y 54 | 55 | 56 | def move(self,input): 57 | 58 | if input != None: 59 | self.playerVelY = self.playerFlapAcc 60 | self.playerFlapped = True 61 | 62 | if self.playerVelY < self.playerMaxVelY and not self.playerFlapped: 63 | self.playerVelY += self.playerAccY 64 | if self.playerFlapped: 65 | self.playerFlapped = False 66 | 67 | self.y += min(self.playerVelY, SCREENHEIGHT - self.y - self.height) 68 | self.y = max(self.y,0) 69 | self.display(self.x,self.y) 70 | 71 | 72 | class PipeBlock(pygame.sprite.Sprite): 73 | 74 | def __init__(self,image,upper): 75 | 76 | pygame.sprite.Sprite.__init__(self) 77 | 78 | if upper == False: 79 | self.image = pygame.image.load(image) 80 | else: 81 | self.image = pygame.transform.rotate(pygame.image.load(image),180) 82 | 83 | self.rect = self.image.get_rect() 84 | 85 | 86 | 87 | class Pipe(pygame.sprite.Sprite): 88 | 89 | 90 | def __init__(self,screen,x): 91 | 92 | pygame.sprite.Sprite.__init__(self) 93 | 94 | self.screen = screen 95 | self.lowerBlock = PipeBlock('/home/roshan/Documents/FlappyBird/pipe-red.png',False) 96 | self.upperBlock = PipeBlock('/home/roshan/Documents/FlappyBird/pipe-red.png',True) 97 | 98 | 99 | self.pipeWidth = self.upperBlock.rect.width 100 | self.x = x 101 | 102 | 103 | heights = self.getHeight() 104 | self.upperY, self.lowerY = heights[0], heights[1] 105 | 106 | self.behindBird = 0 107 | self.display() 108 | 109 | 110 | def getHeight(self): 111 | 112 | # randVal = randint(1,10) 113 | randVal = choice([1,2,3,4,5,6,7,8,9], p =[0.04,0.04*2,0.04*3,0.04*4,0.04*5,0.04*4,0.04*3,0.04*2,0.04] ) 114 | 115 | midYPos = 106 + 30*randVal 116 | 117 | upperPos = midYPos - (PIPEGAPSIZE/2) 118 | lowerPos = midYPos + (PIPEGAPSIZE/2) 119 | 120 | # print(upperPos) 121 | # print(lowerPos) 122 | # print('-------') 123 | return([upperPos,lowerPos]) 124 | 125 | def display(self): 126 | 127 | self.screen.blit(self.lowerBlock.image, (self.x, self.lowerY)) 128 | self.screen.blit(self.upperBlock.image, (self.x, self.upperY - self.upperBlock.rect.height)) 129 | self.upperBlock.rect.x, self.upperBlock.rect.y = self.x, (self.upperY - self.upperBlock.rect.height) 130 | self.lowerBlock.rect.x, self.lowerBlock.rect.y = self.x, self.lowerY 131 | 132 | def move(self): 133 | 134 | self.x -= 3 135 | 136 | if self.x <= 0: 137 | self.x = SCREENWIDTH 138 | heights = self.getHeight() 139 | self.upperY, self.lowerY = heights[0], heights[1] 140 | self.behindBird = 0 141 | 142 | self.display() 143 | return([self.x+(self.pipeWidth/2), self.upperY, self.lowerY]) 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | def game(genome, config): 155 | 156 | net = neat.nn.FeedForwardNetwork.create(genome, config) 157 | 158 | pygame.init() 159 | 160 | FPSCLOCK = pygame.time.Clock() 161 | DISPLAY = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT)) 162 | pygame.display.set_caption('Flappy Bird') 163 | 164 | global SCORE 165 | 166 | bird = Bird(DISPLAY) 167 | pipe1 = Pipe(DISPLAY, SCREENWIDTH+100) 168 | pipe2 = Pipe(DISPLAY, SCREENWIDTH+100+(SCREENWIDTH/2)) 169 | 170 | pipeGroup = pygame.sprite.Group() 171 | pipeGroup.add(pipe1.upperBlock) 172 | pipeGroup.add(pipe2.upperBlock) 173 | pipeGroup.add(pipe1.lowerBlock) 174 | pipeGroup.add(pipe2.lowerBlock) 175 | 176 | # birdGroup = pygame.sprite.Group() 177 | # birdGroup.add(bird1) 178 | 179 | 180 | moved = False 181 | 182 | time = 0 183 | 184 | while True: 185 | 186 | DISPLAY.blit(BACKGROUND,(0,0)) 187 | 188 | if (pipe1.x < pipe2.x and pipe1.behindBird==0) or (pipe2.x < pipe1.x and pipe2.behindBird==1): 189 | input = (bird.y,pipe1.x, pipe1.upperY, pipe1.lowerY) 190 | centerY = (pipe1.upperY + pipe1.lowerY)/2 191 | elif (pipe1.x < pipe2.x and pipe1.behindBird==1) or (pipe2.x < pipe1.x and pipe2.behindBird==0): 192 | input = (bird.y,pipe2.x, pipe2.upperY, pipe2.lowerY) 193 | centerY = (pipe2.upperY + pipe2.lowerY)/2 194 | 195 | # print(input) 196 | vertDist = (((bird.y - centerY)**2)*100)/(512*512) 197 | time += 1 198 | 199 | fitness = SCORE - vertDist + (time/10.0) 200 | 201 | t = pygame.sprite.spritecollideany(bird,pipeGroup) 202 | 203 | if t!=None or (bird.y== 512 - bird.height) or (bird.y == 0): 204 | # print("GAME OVER") 205 | # print("FINAL SCORE IS %d"%fitness) 206 | return(fitness) 207 | 208 | output = net.activate(input) 209 | 210 | # for event in pygame.event.get(): 211 | # if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): 212 | # pygame.quit() 213 | # sys.exit() 214 | 215 | if output[0]>=0.5: 216 | bird.move("UP") 217 | moved = True 218 | 219 | 220 | if moved == False: 221 | bird.move(None) 222 | else: 223 | moved = False 224 | 225 | 226 | pipe1Pos = pipe1.move() 227 | if pipe1Pos[0] <= int(SCREENWIDTH * 0.2) - int(bird.rect.width/2): 228 | if pipe1.behindBird == 0: 229 | pipe1.behindBird = 1 230 | SCORE += 10 231 | print("SCORE IS %d"%(SCORE+vertDist)) 232 | 233 | pipe2Pos = pipe2.move() 234 | if pipe2Pos[0] <= int(SCREENWIDTH * 0.2) - int(bird.rect.width/2): 235 | if pipe2.behindBird == 0: 236 | pipe2.behindBird = 1 237 | SCORE += 10 238 | print("SCORE IS %d"%(SCORE+vertDist)) 239 | 240 | 241 | 242 | 243 | pygame.display.update() 244 | FPSCLOCK.tick(FPS) 245 | 246 | 247 | 248 | def eval_genomes(genomes, config): 249 | i = 0 250 | global SCORE 251 | global GENERATION, MAX_FITNESS, BEST_GENOME 252 | 253 | GENERATION += 1 254 | for genome_id, genome in genomes: 255 | 256 | genome.fitness = game(genome, config) 257 | print("Gen : %d Genome # : %d Fitness : %f Max Fitness : %f"%(GENERATION,i,genome.fitness, MAX_FITNESS)) 258 | if genome.fitness >= MAX_FITNESS: 259 | MAX_FITNESS = genome.fitness 260 | BEST_GENOME = genome 261 | SCORE = 0 262 | i+=1 263 | 264 | config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, 265 | neat.DefaultSpeciesSet, neat.DefaultStagnation, 266 | 'config') 267 | 268 | pop = neat.Population(config) 269 | stats = neat.StatisticsReporter() 270 | pop.add_reporter(stats) 271 | 272 | winner = pop.run(eval_genomes, 30) 273 | 274 | print(winner) 275 | 276 | outputDir = '/home/roshan/Documents/FlappyBird/bestGenomes' 277 | os.chdir(outputDir) 278 | serialNo = len(os.listdir(outputDir))+1 279 | outputFile = open(str(serialNo)+'_'+str(int(MAX_FITNESS))+'.p','wb' ) 280 | 281 | pickle.dump(winner, outputFile) 282 | 283 | --------------------------------------------------------------------------------