Skip to content

Instantly share code, notes, and snippets.

@ahmedfgad
Created October 3, 2021 21:07
Show Gist options
  • Select an option

  • Save ahmedfgad/513366eb30c4b518b04e4ac7e8420a6c to your computer and use it in GitHub Desktop.

Select an option

Save ahmedfgad/513366eb30c4b518b04e4ac7e8420a6c to your computer and use it in GitHub Desktop.
Play the Flappy Bird Game using PyGAD
import random # For generating random numbers
import sys # We will use sys.exit to exit the program
import pygame
from pygame.locals import * # Basic pygame imports
import pygad
import threading
class PygadThread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
ga_instance = pygad.GA(num_generations=200000,
sol_per_pop=200,
num_parents_mating=100,
num_genes=1,
fitness_func=fitness_func,
init_range_low=100.0,
init_range_high=200.0,
random_mutation_min_val=40.0,
random_mutation_max_val=350.0,
mutation_by_replacement=True,
on_generation=on_generation,
suppress_warnings=True,
delay_after_gen=0.01)
ga_instance.run()
playery = 0
pipeVelX = -4
playerVelY = -9
playerMaxVelY = 10
playerMinVelY = -8
playerAccY = 1
playerFlapAccv = -8 # velocity while flapping
playerFlapped = False # It is true only when the bird is flapping
# Global Variables for the game
FPS = 32
SCREENWIDTH = int(289*1.4) #
SCREENHEIGHT = 511
SCREEN = pygame.display.set_mode((SCREENWIDTH, SCREENHEIGHT))
pygame.display.set_icon(pygame.image.load('gallery/sprites/pygad.png').convert_alpha())
GROUNDY = SCREENHEIGHT * 0.8
GAME_SPRITES = {}
GAME_SOUNDS = {}
PLAYER = 'gallery/sprites/bird.png'
BACKGROUND = 'gallery/sprites/background.png'
PIPE = 'gallery/sprites/pipe.png'
last_gen_best_solution = 0
def on_generation(ga_inst):
global last_gen_best_solution, playery, playerVelY, playerFlapAccv, playerFlapped, playerMaxVelY, playerAccY, playerHeight, GROUNDY
best_sol, best_sol_fit, _ = ga_inst.best_solution()
if playery > best_sol:
# Go up
playerVelY = playerFlapAccv
playerFlapped = True
# playery = playery - 10
# GAME_SOUNDS['wing'].play()
elif playery < best_sol:
# Go down
playerFlapped = False
# playery = playery + 15
# GAME_SOUNDS['wing'].play()
if playerVelY <playerMaxVelY and not playerFlapped:
playerVelY += playerAccY
if playerFlapped:
playerFlapped = False
playerHeight = GAME_SPRITES['player'].get_height()
playery = playery + min(playerVelY, GROUNDY - playery - playerHeight)
last_gen_best_solution = best_sol
def closest_pipe(playerx, pipes):
global GAME_SPRITES
closest_pipe_idx = -1
for pipeIDX in range(len(pipes)-1):
pipe0X = abs(playerx - pipes[pipeIDX]['x'])
pipe1X = abs(playerx - pipes[pipeIDX + 1]['x'])
# The second condition (pipes[pipeIDX]['x'] + 25 > playerx) is added to make sure the pipe in use is not the passed one but the coming one.
# 25 is a margin between the pipe and the character.
if pipe0X < pipe1X and pipes[pipeIDX]['x'] + 40 > playerx:
closest_pipe_idx = pipeIDX
else:
closest_pipe_idx = pipeIDX + 1
return closest_pipe_idx
def fitness_func(solution, solution_idx):
global SCREEN, playery, pipeHeight, playerx, upperPipes, lowerPipes, GAME_SPRITES, playerVelY, playerFlapAccv, playerFlapped, playerMaxVelY, playerAccY, playerHeight, GROUNDY, nearest_upper_pipe, nearest_lower_pipe
if solution < 0:
return -8888
if solution > GROUNDY - 25:
return -9999
fitness_ground = abs(solution - GROUNDY)
if fitness_ground < 50:
fitness_ground = (-1.0/fitness_ground) * 999999
nearest_upper_pipe = upperPipes[closest_pipe(playerx, upperPipes)]
pipeHeight = GAME_SPRITES['pipe'][0].get_height()
fitness_upper = abs(solution - (pipeHeight + nearest_upper_pipe['y']) - 25)
if(solution < pipeHeight + nearest_upper_pipe['y'] + 70): # and abs(playerx - nearest_upper_pipe['x']) < GAME_SPRITES['pipe'][0].get_width() + 30):
fitness_upper = (-1.0/fitness_upper) * 999999
else:
fitness_upper = fitness_upper
nearest_lower_pipe = lowerPipes[closest_pipe(playerx, lowerPipes)]
fitness_lower = abs(solution + GAME_SPRITES['player'].get_height() - nearest_lower_pipe['y'] - 25)
if (solution + GAME_SPRITES['player'].get_height() > nearest_lower_pipe['y'] - 50): # and abs(playerx - nearest_lower_pipe['x']) < GAME_SPRITES['pipe'][0].get_width() + 30:
fitness_lower = (-1.0/fitness_lower) * 999999
else:
fitness_lower = fitness_lower
fitness = (fitness_ground + fitness_upper + fitness_lower)/3
return fitness
def welcomeScreen():
global SCREEN
"""
Shows welcome images on the screen
"""
playerx = int(SCREENWIDTH/5)
playery = int((SCREENHEIGHT - GAME_SPRITES['player'].get_height())/2)
messagex = int((SCREENWIDTH - GAME_SPRITES['message'].get_width())/2)
messagey = int(SCREENHEIGHT * 0.13)
basex = 0
while True:
for event in pygame.event.get():
# if user clicks on cross button, close the game
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# If the user presses space or up key, start the game for them
elif event.type == KEYDOWN and (event.key == K_SPACE or event.key == K_UP):
return
else:
SCREEN.blit(GAME_SPRITES['background'], (0, 0))
SCREEN.blit(GAME_SPRITES['player'], (playerx, playery))
SCREEN.blit(GAME_SPRITES['message'], (messagex,messagey ))
SCREEN.blit(GAME_SPRITES['base'], (basex, GROUNDY))
pygame.display.update()
FPSCLOCK.tick(FPS)
nearest_upper_pipe = {'x': 0, 'y': 0}
nearest_lower_pipe = {'x': 0, 'y': 0}
def mainGame():
global SCREEN, playerx, playery, upperPipes, lowerPipes, playerVelY, playerFlapAccv, playerFlapped, playerMaxVelY, playerAccY, playerHeight, GROUNDY, nearest_upper_pipe, nearest_lower_pipe
score = 0
playerx = int(SCREENWIDTH/5)
playery = int(SCREENWIDTH/2)
basex = 0
# Create 2 pipes for blitting on the screen
newPipe1 = getRandomPipe()
newPipe2 = getRandomPipe()
# my List of upper pipes
upperPipes = [
{'x': SCREENWIDTH + 200, 'y': newPipe1[0]['y']},
{'x': SCREENWIDTH + 200 + (SCREENWIDTH/2), 'y': newPipe2[0]['y']},
]
# my List of lower pipes
lowerPipes = [
{'x': SCREENWIDTH + 200, 'y': newPipe1[1]['y']},
{'x': SCREENWIDTH + 200+ (SCREENWIDTH/2), 'y': newPipe2[1]['y']},
]
pipeVelX = -10
pygad_thread = PygadThread()
pygad_thread.start()
while True:
# playery = playery + 25
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
crashTest = isCollide(playerx, playery, upperPipes, lowerPipes) # This function will return true if the player is crashed
if crashTest:
return
#check for score
playerMidPos = playerx + GAME_SPRITES['player'].get_width()/2
for pipe in upperPipes:
pipeMidPos = pipe['x'] + GAME_SPRITES['pipe'][0].get_width()/2
if pipeMidPos <= playerMidPos < pipeMidPos + abs(pipeVelX):
score += 1
# print(f"Your score is {score}")
GAME_SOUNDS['point'].play()
# move pipes to the left
for upperPipe , lowerPipe in zip(upperPipes, lowerPipes):
upperPipe['x'] += pipeVelX
lowerPipe['x'] += pipeVelX
# Add a new pipe when the first is about to cross the leftmost part of the screen
if 0 < upperPipes[0]['x'] < abs(pipeVelX) + 1:
newpipe = getRandomPipe()
upperPipes.append(newpipe[0])
lowerPipes.append(newpipe[1])
# if the pipe is out of the screen, remove it
if upperPipes[0]['x'] < -GAME_SPRITES['pipe'][0].get_width():
upperPipes.pop(0)
lowerPipes.pop(0)
# Lets blit our sprites now
SCREEN.blit(GAME_SPRITES['background'], (0, 0))
for upperPipe, lowerPipe in zip(upperPipes, lowerPipes):
SCREEN.blit(GAME_SPRITES['pipe'][0], (upperPipe['x'], upperPipe['y']))
SCREEN.blit(GAME_SPRITES['pipe'][1], (lowerPipe['x'], lowerPipe['y']))
SCREEN.blit(GAME_SPRITES['base'], (basex, GROUNDY))
SCREEN.blit(GAME_SPRITES['player'], (playerx, playery))
myDigits = [int(x) for x in list(str(score))]
width = 0
for digit in myDigits:
width += GAME_SPRITES['numbers'][digit].get_width()
Xoffset = (SCREENWIDTH - width)/2
for digit in myDigits:
SCREEN.blit(GAME_SPRITES['numbers'][digit], (Xoffset, SCREENHEIGHT*0.12))
Xoffset += GAME_SPRITES['numbers'][digit].get_width()
pygame.display.update()
FPSCLOCK.tick(FPS)
def isCollide(playerx, playery, upperPipes, lowerPipes):
# print(playery, fitness_func(playery, 0))
if playery > GROUNDY - 25 or playery < 0:
print("Ground", playery, fitness_func(playery, 0))
# If the player hit the upper part of the screen.
GAME_SOUNDS['hit'].play()
return True
for pipe in upperPipes:
pipeHeight = GAME_SPRITES['pipe'][0].get_height()
if(playery < pipeHeight + pipe['y'] and abs(playerx - pipe['x']) < GAME_SPRITES['pipe'][0].get_width()):
print("Upper", playery, fitness_func(playery, 0))
GAME_SOUNDS['hit'].play()
return True
for pipe in lowerPipes:
if (playery + GAME_SPRITES['player'].get_height() > pipe['y']) and abs(playerx - pipe['x']) < GAME_SPRITES['pipe'][0].get_width():
print("Lower", playery, fitness_func(playery, 0))
GAME_SOUNDS['hit'].play()
return True
return False
def getRandomPipe():
global pipeHeight
"""
Generate positions of two pipes(one bottom straight and one top rotated ) for blitting on the screen
"""
pipeHeight = GAME_SPRITES['pipe'][0].get_height()
offset = SCREENHEIGHT/3
y2 = offset + random.randrange(0, int(SCREENHEIGHT - GAME_SPRITES['base'].get_height() - 1.2 *offset))
pipeX = SCREENWIDTH + 10
y1 = pipeHeight - y2 + offset
pipe = [
{'x': pipeX, 'y': -y1}, # upper Pipe
{'x': pipeX, 'y': y2} # lower Pipe
]
return pipe
if __name__ == "__main__":
# This will be the main point from where our game will start
pygame.init() # Initialize all pygame's modules
FPSCLOCK = pygame.time.Clock()
pygame.display.set_caption('Unbeaten PyGAD Plays Flappy Bird')
GAME_SPRITES['numbers'] = (
pygame.image.load('gallery/sprites/0.png').convert_alpha(),
pygame.image.load('gallery/sprites/1.png').convert_alpha(),
pygame.image.load('gallery/sprites/2.png').convert_alpha(),
pygame.image.load('gallery/sprites/3.png').convert_alpha(),
pygame.image.load('gallery/sprites/4.png').convert_alpha(),
pygame.image.load('gallery/sprites/5.png').convert_alpha(),
pygame.image.load('gallery/sprites/6.png').convert_alpha(),
pygame.image.load('gallery/sprites/7.png').convert_alpha(),
pygame.image.load('gallery/sprites/8.png').convert_alpha(),
pygame.image.load('gallery/sprites/9.png').convert_alpha(),
)
GAME_SPRITES['message'] = pygame.transform.scale(pygame.image.load('gallery/sprites/pygad.png').convert_alpha(), [400, 300])
GAME_SPRITES['base'] = pygame.image.load('gallery/sprites/base.png').convert_alpha()
GAME_SPRITES['pipe'] = (pygame.transform.rotate(pygame.image.load(PIPE).convert_alpha(), 180), pygame.image.load(PIPE).convert_alpha())
# Game sounds
GAME_SOUNDS['die'] = pygame.mixer.Sound('gallery/audio/die.wav')
GAME_SOUNDS['hit'] = pygame.mixer.Sound('gallery/audio/hit.wav')
GAME_SOUNDS['point'] = pygame.mixer.Sound('gallery/audio/point.wav')
GAME_SOUNDS['swoosh'] = pygame.mixer.Sound('gallery/audio/swoosh.wav')
GAME_SOUNDS['wing'] = pygame.mixer.Sound('gallery/audio/wing.wav')
GAME_SPRITES['background'] = pygame.image.load(BACKGROUND).convert()
GAME_SPRITES['player'] = pygame.image.load(PLAYER).convert_alpha()
while True:
welcomeScreen() # Shows welcome screen to the user until he presses a button
mainGame() # This is the main game function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment