import pygame
import random
import os
import time
import neat
import visualize
import pickle
[Link]() # init font
WIN_WIDTH = 600
WIN_HEIGHT = 800
FLOOR = 730
STAT_FONT = [Link]("comicsans", 50)
END_FONT = [Link]("comicsans", 70)
DRAW_LINES = False
WIN = [Link].set_mode((WIN_WIDTH, WIN_HEIGHT))
[Link].set_caption("Flappy Bird")
pipe_img = [Link].scale2x([Link]([Link]("imgs","[Link]")).convert_alpha())
bg_img = [Link]([Link]([Link]("imgs","[Link]")).convert_alpha(), (600, 900))
bird_images = [[Link].scale2x([Link]([Link]("imgs","bird" + str(x) + ".png"))) for x in
range(1,4)]
base_img = [Link].scale2x([Link]([Link]("imgs","[Link]")).convert_alpha())
gen = 0
class Bird:
"""
Bird class representing the flappy bird
"""
MAX_ROTATION = 25
IMGS = bird_images
ROT_VEL = 20
ANIMATION_TIME = 5
def _init_(self, x, y):
"""
Initialize the object
:param x: starting x pos (int)
:param y: starting y pos (int)
:return: None
"""
self.x = x
self.y = y
[Link] = 0 # degrees to tilt
self.tick_count = 0
[Link] = 0
[Link] = self.y
self.img_count = 0
[Link] = [Link][0]
def jump(self):
"""
make the bird jump
:return: None
"""
[Link] = -10.5
self.tick_count = 0
[Link] = self.y
def move(self):
"""
make the bird move
:return: None
"""
self.tick_count += 1
# for downward acceleration
displacement = [Link]*(self.tick_count) + 0.5*(3)(self.tick_count)*2 # calculate displacement
# terminal velocity
if displacement >= 16:
displacement = (displacement/abs(displacement)) * 16
if displacement < 0:
displacement -= 2
self.y = self.y + displacement
if displacement < 0 or self.y < [Link] + 50: # tilt up
if [Link] < self.MAX_ROTATION:
[Link] = self.MAX_ROTATION
else: # tilt down
if [Link] > -90:
[Link] -= self.ROT_VEL
def draw(self, win):
"""
draw the bird
:param win: pygame window or surface
:return: None
"""
self.img_count += 1
# For animation of bird, loop through three images
if self.img_count <= self.ANIMATION_TIME:
[Link] = [Link][0]
elif self.img_count <= self.ANIMATION_TIME*2:
[Link] = [Link][1]
elif self.img_count <= self.ANIMATION_TIME*3:
[Link] = [Link][2]
elif self.img_count <= self.ANIMATION_TIME*4:
[Link] = [Link][1]
elif self.img_count == self.ANIMATION_TIME*4 + 1:
[Link] = [Link][0]
self.img_count = 0
# so when bird is nose diving it isn't flapping
if [Link] <= -80:
[Link] = [Link][1]
self.img_count = self.ANIMATION_TIME*2
# tilt the bird
blitRotateCenter(win, [Link], (self.x, self.y), [Link])
def get_mask(self):
"""
gets the mask for the current image of the bird
:return: None
"""
return [Link].from_surface([Link])
class Pipe():
"""
represents a pipe object
"""
GAP = 200
VEL = 5
def _init_(self, x):
"""
initialize pipe object
:param x: int
:param y: int
:return" None
"""
self.x = x
[Link] = 0
# where the top and bottom of the pipe is
[Link] = 0
[Link] = 0
self.PIPE_TOP = [Link](pipe_img, False, True)
self.PIPE_BOTTOM = pipe_img
[Link] = False
self.set_height()
def set_height(self):
"""
set the height of the pipe, from the top of the screen
:return: None
"""
[Link] = [Link](50, 450)
[Link] = [Link] - self.PIPE_TOP.get_height()
[Link] = [Link] + [Link]
def move(self):
"""
move pipe based on vel
:return: None
"""
self.x -= [Link]
def draw(self, win):
"""
draw both the top and bottom of the pipe
:param win: pygame window/surface
:return: None
"""
# draw top
[Link](self.PIPE_TOP, (self.x, [Link]))
# draw bottom
[Link](self.PIPE_BOTTOM, (self.x, [Link]))
def collide(self, bird, win):
"""
returns if a point is colliding with the pipe
:param bird: Bird object
:return: Bool
"""
bird_mask = bird.get_mask()
top_mask = [Link].from_surface(self.PIPE_TOP)
bottom_mask = [Link].from_surface(self.PIPE_BOTTOM)
top_offset = (self.x - bird.x, [Link] - round(bird.y))
bottom_offset = (self.x - bird.x, [Link] - round(bird.y))
b_point = bird_mask.overlap(bottom_mask, bottom_offset)
t_point = bird_mask.overlap(top_mask,top_offset)
if b_point or t_point:
return True
return False
class Base:
"""
Represnts the moving floor of the game
"""
VEL = 5
WIDTH = base_img.get_width()
IMG = base_img
def _init_(self, y):
"""
Initialize the object
:param y: int
:return: None
"""
self.y = y
self.x1 = 0
self.x2 = [Link]
def move(self):
"""
move floor so it looks like its scrolling
:return: None
"""
self.x1 -= [Link]
self.x2 -= [Link]
if self.x1 + [Link] < 0:
self.x1 = self.x2 + [Link]
if self.x2 + [Link] < 0:
self.x2 = self.x1 + [Link]
def draw(self, win):
"""
Draw the floor. This is two images that move together.
:param win: the pygame surface/window
:return: None
"""
[Link]([Link], (self.x1, self.y))
[Link]([Link], (self.x2, self.y))
def blitRotateCenter(surf, image, topleft, angle):
"""
Rotate a surface and blit it to the window
:param surf: the surface to blit to
:param image: the image surface to rotate
:param topLeft: the top left position of the image
:param angle: a float value for angle
:return: None
"""
rotated_image = [Link](image, angle)
new_rect = rotated_image.get_rect(center = image.get_rect(topleft = topleft).center)
[Link](rotated_image, new_rect.topleft)
def draw_window(win, birds, pipes, base, score, gen, pipe_ind):
"""
draws the windows for the main game loop
:param win: pygame window surface
:param bird: a Bird object
:param pipes: List of pipes
:param score: score of the game (int)
:param gen: current generation
:param pipe_ind: index of closest pipe
:return: None
"""
if gen == 0:
gen = 1
[Link](bg_img, (0,0))
for pipe in pipes:
[Link](win)
[Link](win)
for bird in birds:
# draw lines from bird to pipe
if DRAW_LINES:
try:
[Link](win, (255,0,0), (bird.x+[Link].get_width()/2, bird.y + [Link].get_height()/2),
(pipes[pipe_ind].x + pipes[pipe_ind].PIPE_TOP.get_width()/2, pipes[pipe_ind].height), 5)
[Link](win, (255,0,0), (bird.x+[Link].get_width()/2, bird.y + [Link].get_height()/2),
(pipes[pipe_ind].x + pipes[pipe_ind].PIPE_BOTTOM.get_width()/2, pipes[pipe_ind].bottom), 5)
except:
pass
# draw bird
[Link](win)
# score
score_label = STAT_FONT.render("Score: " + str(score),1,(255,255,255))
[Link](score_label, (WIN_WIDTH - score_label.get_width() - 15, 10))
# generations
score_label = STAT_FONT.render("Gens: " + str(gen-1),1,(255,255,255))
[Link](score_label, (10, 10))
# alive
score_label = STAT_FONT.render("Alive: " + str(len(birds)),1,(255,255,255))
[Link](score_label, (10, 50))
[Link]()
def eval_genomes(genomes, config):
"""
runs the simulation of the current population of
birds and sets their fitness based on the distance they
reach in the game.
"""
global WIN, gen
win = WIN
gen += 1
# start by creating lists holding the genome itself, the
# neural network associated with the genome and the
# bird object that uses that network to play
nets = []
birds = []
ge = []
for genome_id, genome in genomes:
[Link] = 0 # start with fitness level of 0
net = [Link](genome, config)
[Link](net)
[Link](Bird(230,350))
[Link](genome)
base = Base(FLOOR)
pipes = [Pipe(700)]
score = 0
clock = [Link]()
run = True
while run and len(birds) > 0:
[Link](30)
for event in [Link]():
if [Link] == [Link]:
run = False
[Link]()
quit()
break
pipe_ind = 0
if len(birds) > 0:
if len(pipes) > 1 and birds[0].x > pipes[0].x + pipes[0].PIPE_TOP.get_width(): # determine whether to use the
first or second
pipe_ind = 1 # pipe on the screen for neural network input
for x, bird in enumerate(birds): # give each bird a fitness of 0.1 for each frame it stays alive
ge[x].fitness += 0.1
[Link]()
# send bird location, top pipe location and bottom pipe location and determine from network whether to jump
or not
output = nets[[Link](bird)].activate((bird.y, abs(bird.y - pipes[pipe_ind].height), abs(bird.y -
pipes[pipe_ind].bottom)))
if output[0] > 0.5: # we use a tanh activation function so result will be between -1 and 1. if over 0.5 jump
[Link]()
[Link]()
rem = []
add_pipe = False
for pipe in pipes:
[Link]()
# check for collision
for bird in birds:
if [Link](bird, win):
ge[[Link](bird)].fitness -= 1
[Link]([Link](bird))
[Link]([Link](bird))
[Link]([Link](bird))
if pipe.x + pipe.PIPE_TOP.get_width() < 0:
[Link](pipe)
if not [Link] and pipe.x < bird.x:
[Link] = True
add_pipe = True
if add_pipe:
score += 1
# can add this line to give more reward for passing through a pipe (not required)
for genome in ge:
[Link] += 5
[Link](Pipe(WIN_WIDTH))
for r in rem:
[Link](r)
for bird in birds:
if bird.y + [Link].get_height() - 10 >= FLOOR or bird.y < -50:
[Link]([Link](bird))
[Link]([Link](bird))
[Link]([Link](bird))
draw_window(WIN, birds, pipes, base, score, gen, pipe_ind)
# break if score gets large enough
'''if score > 20:
[Link](nets[0],open("[Link]", "wb"))
break'''
def run(config_file):
"""
runs the NEAT algorithm to train a neural network to play flappy bird.
:param config_file: location of config file
:return: None
"""
config = [Link]([Link], [Link],
[Link], [Link],
config_file)
# Create the population, which is the top-level object for a NEAT run.
p = [Link](config)
# Add a stdout reporter to show progress in the terminal.
p.add_reporter([Link](True))
stats = [Link]()
p.add_reporter(stats)
#p.add_reporter([Link](5))
# Run for up to 50 generations.
winner = [Link](eval_genomes, 50)
# show final stats
print('\nBest genome:\n{!s}'.format(winner))
if _name_ == '_main_':
# Determine path to configuration file. This path manipulation is
# here so that the script will run successfully regardless of the
# current working directory.
local_dir = [Link](_file_)
config_path = [Link](local_dir, '[Link]')
run(config_path)