PyGame

On this page I display some minigames i created in Python using the PyGame library. Although it has not much to do with bioinformatics, I found it a useful way to learn the basics of coding.

Pollinator

This game is a parody of Flappy Bird, a game that was all the rage in 2014. You play with space bar and have to jump through the pipes. The games goes on infinitely and you have to goal to get the highest score. The main learning point of this game was the character selection.

Expand to see the code
import pygame
import random
import math

pygame.init()

# Screen
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Pollinator")

# Colors
WHITE = (255, 255, 255)

# Flag
game_over = False

# Constants
gravity = 0.5
bee_velocity = 0
bee_position = [100, SCREEN_HEIGHT // 2]
pipe_velocity = 3
airplane_size = 30
airballoon_size = 30
lives = 3
heart_size = 50
cone_size = 30
flower_size = 40
flower_speed = 1
flowerx_speed = -0.1
PIPE_WIDTH = 60
PIPE_HEIGHT = random.randint(100, 300)
PIPE_GAP = 150
flappy_speed = 1
flappy_amplitude = 10
flappy_frequency = 0.01
flappy_start_time = pygame.time.get_ticks()
font = pygame.font.SysFont(None, 40)

# Pipe list
pipes = []

# Load sounds
pygame.mixer.music.load(r'C:\Users\Private\Desktop\Python\Pollen\Beehive.mp3')
pygame.mixer.music.play(-1)
pygame.mixer.music.set_volume(0.5)

chickens_theme = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Pollen\chickens_theme.wav')

hurt_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Pollen\yoshi_hurt.wav')
click_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Pollen\click_sound.wav')
click_sound.set_volume(0.3) 
coin_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Pollen\mariocoin.wav')
coin_sound.set_volume(0.6) 
wind_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Pollen\wind_gust.wav')

# Load images
bee_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\bee.png')
bee_image = pygame.transform.scale(bee_image, (40, 40))
bee_rect = bee_image.get_rect()

pinus_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pinus.png')
pinus_image = pygame.transform.scale(pinus_image, (35, 35))
pinus_rect = pinus_image.get_rect()

colibri_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\colibri.png')
colibri_image = pygame.transform.scale(colibri_image, (40, 40))
colibri_rect = colibri_image.get_rect()

bee_cry_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\bee_cry.png')
bee_cry_image = pygame.transform.scale(bee_cry_image, (70, 70))

pinus_cry_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pinus_cry.png')
pinus_cry_image = pygame.transform.scale(pinus_cry_image, (70, 70))

colibri_cry_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\colibri_cry.png')
colibri_cry_image = pygame.transform.scale(colibri_cry_image, (70, 70))

airplane_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\airplane.png')
airplane_image = pygame.transform.scale(airplane_image, (airplane_size, airplane_size))

airballoon_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\airballoon.png')
airballoon_image = pygame.transform.scale(airballoon_image, (airballoon_size, airballoon_size))

flappy_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\flappy.png')
flappy_image = pygame.transform.scale(flappy_image, (40, 20))
flappy_rect = flappy_image.get_rect()

heart_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\heart.png')
heart_image = pygame.transform.scale(heart_image, (heart_size, heart_size))

flower_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\flower.png')
flower_image = pygame.transform.scale(flower_image, (flower_size, flower_size))

cone_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\cone.png')
cone_image = pygame.transform.scale(cone_image, (cone_size, cone_size))

background_start_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\background_start.png')
background_start_image = pygame.transform.scale(background_start_image, (SCREEN_WIDTH, SCREEN_HEIGHT))

background_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\background.png')
background_image = pygame.transform.scale(background_image, (SCREEN_WIDTH, SCREEN_HEIGHT))

redpipedown = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_down\1_pipe_down.png')
orangepipedown = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_down\2_pipe_down.png')
yellowpipedown = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_down\3_pipe_down.png')
greenpipedown = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_down\4_pipe_down.png')
bluepipedown = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_down\5_pipe_down.png')
purplepipedown = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_down\6_pipe_down.png')

redpipeup = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_up\1_pipe_up.png')
orangepipeup = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_up\2_pipe_up.png')
yellowpipeup = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_up\3_pipe_up.png')
greenpipeup = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_up\4_pipe_up.png')
bluepipeup = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_up\5_pipe_up.png')
purplepipeup = pygame.image.load(r'C:\Users\Private\Desktop\Python\Pollen\pipe_up\6_pipe_up.png')

clock = pygame.time.Clock()

score = 0

class Bee:
    def __init__(self):
        self.x = bee_position[0]
        self.y = bee_position[1]
        self.velocity = bee_velocity
        self.rect = bee_image.get_rect()
        self.rect.topleft = (self.x, self.y)

    def move(self):
        self.velocity += gravity
        self.y += self.velocity
        self.rect.y = self.y

    def flap(self):
        self.velocity = -8

    def draw(self, screen):
        screen.blit(bee_image, self.rect.topleft)

class Pinus:
    def __init__(self):
        self.x = bee_position[0]
        self.y = bee_position[1]
        self.velocity = bee_velocity
        self.rect = pinus_image.get_rect()
        self.rect.topleft = (self.x, self.y)

    def move(self):
        self.velocity += gravity
        self.y += self.velocity
        self.rect.y = self.y

    def flap(self):
        self.velocity = -8

    def draw(self, screen):
        screen.blit(pinus_image, self.rect.topleft)

class Colibri:
    def __init__(self):
        self.x = bee_position[0]
        self.y = bee_position[1]
        self.velocity = bee_velocity
        self.rect = colibri_image.get_rect()
        self.rect.topleft = (self.x, self.y)

    def move(self):
        self.velocity += gravity
        self.y += self.velocity
        self.rect.y = self.y

    def flap(self):
        self.velocity = -8

    def draw(self, screen):
        screen.blit(colibri_image, self.rect.topleft)

class Pipe:
    def __init__(self, x, color_index):
        self.x = x
        self.height = random.randint(100, 300)
        self.top = self.height
        self.bottom = self.height + PIPE_GAP
        self.width = PIPE_WIDTH
        
        # List of pipes (top and bottom) for each color
        self.color_images = [
            (redpipedown, redpipeup),
            (orangepipedown, orangepipeup),
            (yellowpipedown, yellowpipeup),
            (greenpipedown, greenpipeup),
            (bluepipedown, bluepipeup),
            (purplepipedown, purplepipeup),
        ]
        
        self.color_index = color_index
        self.top_image, self.bottom_image = self.color_images[self.color_index]

    def update(self):
        self.x -= 3

    def draw(self, screen):
        scaled_top = pygame.transform.scale(self.top_image, (self.width, self.top))
        scaled_bottom = pygame.transform.scale(self.bottom_image, (self.width, SCREEN_HEIGHT - self.bottom))

        screen.blit(scaled_top, (self.x, 0))
        screen.blit(scaled_bottom, (self.x, self.bottom))

# List of pipes with alternating color index
def reset_pipes():
    color_cycle = 0  # Start with color index 0
    pipes = [
        Pipe(SCREEN_WIDTH + 200, color_index=color_cycle),
        Pipe(SCREEN_WIDTH + 400, color_index=(color_cycle + 1) % 6),
        Pipe(SCREEN_WIDTH + 600, color_index=(color_cycle + 2) % 6),
    ]
    return pipes

# Airplane
airplane_positions = [[random.randint(450, 500), random.randint(100, SCREEN_HEIGHT - 400)] for _ in range(1)]

def draw_airplane():
    for pos in airplane_positions:
        screen.blit(airplane_image, (pos[0], pos[1]))

# Airballoon
airballoon_positions = [[random.randint(600, 650), random.randint(200, 500)] for _ in range(1)]

def draw_airballoon():
    for pos in airballoon_positions:
        screen.blit(airballoon_image, (pos[0], pos[1]))

# Flappy
flappy_rect.x = random.randint(-1000, -50)

# Flowers
flower_positions = [[random.randint(0, SCREEN_WIDTH - flower_size), random.randint(-200, SCREEN_HEIGHT - 400)] for _ in range(1)]

class StartScreen:

    def __init__(self, screen):
        self.screen = screen
        self.font = pygame.font.SysFont(None, 64)
        self.small_font = pygame.font.SysFont(None, 36)
        self.smaller_font = pygame.font.SysFont(None, 25)
        self.text_color = (255, 20, 147)
        self.selected_character = "bee"

    def display(self):
        screen.blit(background_start_image, (0, 0))
        rescaled_pinus = pygame.transform.scale(pinus_image, (20, 20))
        screen.blit(rescaled_pinus, (60, 480))
        rescaled_bee = pygame.transform.scale(bee_image, (20, 20))
        screen.blit(rescaled_bee, (177, 520))
        rescaled_colibri = pygame.transform.scale(colibri_image, (25, 25))
        screen.blit(rescaled_colibri, (285, 455))

        # Start game text
        start_text = self.font.render("Pollinator!", True, (255, 50, 100))
        self.screen.blit(start_text, (SCREEN_WIDTH // 2 - start_text.get_width() // 2, SCREEN_HEIGHT // 5))

        # Instructions
        move_text = self.small_font.render("Press Spacebar to Start", True, (255, 105, 180))
        self.screen.blit(move_text, (SCREEN_WIDTH // 2 - move_text.get_width() // 2, SCREEN_HEIGHT // 3))

        # Instructions
        instruction_text = self.small_font.render("Pick Your Character", True, self.text_color)
        self.screen.blit(instruction_text, (SCREEN_WIDTH // 2 - instruction_text.get_width() // 2, SCREEN_HEIGHT // 1.5))

        # Selected character
        if self.selected_character == "bee":
            screen.blit(bee_image, (165, 505))
            bee_text = self.smaller_font.render("Bee", True, self.text_color)
            self.screen.blit(bee_text, (168, 490))
        elif self.selected_character == "pinus":
            screen.blit(pinus_image, (55, 475))
            pinus_text = self.smaller_font.render("Pine pollen", True, self.text_color)
            self.screen.blit(pinus_text, (73, 455))
        elif self.selected_character == "colibri":
            screen.blit(colibri_image, (280, 447))
            colibri_text = self.smaller_font.render("Colibri", True, self.text_color)
            self.screen.blit(colibri_text, (270, 485))

        pygame.display.flip()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        
        if keys[pygame.K_LEFT]:
            self.selected_character = "pinus"
            coin_sound.play()
        elif keys[pygame.K_UP]:
            self.selected_character = "bee"
            coin_sound.play()
        elif keys[pygame.K_RIGHT]:
            self.selected_character = "colibri"
            coin_sound.play()

        if keys[pygame.K_SPACE]:
            return True
        return False

class GameOverScreen:
    def __init__(self, screen, score, selected_character):
        self.screen = screen
        self.score = score
        self.selected_character = selected_character
        self.font = pygame.font.SysFont(None, 64)
        self.small_font = pygame.font.SysFont(None, 24)
        
        # Oscillation parameters
        self.time = 0
        self.amplitude = 10 
        
    def display(self):
        self.screen.fill((0, 0, 0))
        game_over_text = self.font.render("GAME OVER", True, (255, 105, 180))
        self.screen.blit(game_over_text, (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2, SCREEN_HEIGHT // 4))

        # Display the score at the game over screen
        score_text = self.small_font.render(f"Score: {self.score}", True, (255, 182, 193))
        self.screen.blit(score_text, (SCREEN_WIDTH // 2 - score_text.get_width() // 2, SCREEN_HEIGHT // 2.5))
        
        restart_text = self.small_font.render("Press R to Restart or Q to Quit", True, (255, 182, 193))
        self.screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, SCREEN_HEIGHT // 1.45))
 
        if self.selected_character == "bee":
            self.screen.blit(bee_cry_image, (170, 300))
        elif self.selected_character == "pinus":
            self.screen.blit(pinus_cry_image, (170, 300))
        elif self.selected_character == "colibri":
            self.screen.blit(colibri_cry_image, (170, 300))

        self.time += 0.015  # speed of oscillation
        
        # Vertical displacement for the pipes
        displacement = self.amplitude * math.sin(self.time)
        displacement2 = self.amplitude * math.sin(self.time + math.pi)

        # Draw the pipes with oscillation
        rredpipedown = pygame.transform.scale(redpipedown, (67, 200))
        self.screen.blit(rredpipedown, (0, -150 + displacement))
        
        rorangepipedown = pygame.transform.scale(orangepipedown, (67, 200))
        self.screen.blit(rorangepipedown, (67, -150 + displacement2))
        
        ryellowpipedown = pygame.transform.scale(yellowpipedown, (67, 200))
        self.screen.blit(ryellowpipedown, (134, -150 + displacement))
        
        rgreenpipedown = pygame.transform.scale(greenpipedown, (67, 200))
        self.screen.blit(rgreenpipedown, (201, -150 + displacement2))
        
        rbluepipedown = pygame.transform.scale(bluepipedown, (66, 200))
        self.screen.blit(rbluepipedown, (268, -150 + displacement))
        
        rpurplepipedown = pygame.transform.scale(purplepipedown, (66, 200))
        self.screen.blit(rpurplepipedown, (334, -150 + displacement2))

        rredpipeup = pygame.transform.scale(redpipeup, (66, 200))
        self.screen.blit(rredpipeup, (334, 550 + displacement2))
        
        rorangepipeup = pygame.transform.scale(orangepipeup, (66, 200))
        self.screen.blit(rorangepipeup, (268, 550 + displacement))
        
        ryellowpipeup = pygame.transform.scale(yellowpipeup, (67, 200))
        self.screen.blit(ryellowpipeup, (201, 550 + displacement2))
        
        rgreenpipeup = pygame.transform.scale(greenpipeup, (67, 200))
        self.screen.blit(rgreenpipeup, (134, 550 + displacement))
        
        rbluepipeup = pygame.transform.scale(bluepipeup, (67, 200))
        self.screen.blit(rbluepipeup, (67, 550 + displacement2))
        
        rpurplepipeup = pygame.transform.scale(purplepipeup, (67, 200))
        self.screen.blit(rpurplepipeup, (0, 550 + displacement))                   
        
        pygame.display.flip()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_q]:
            pygame.quit() 
        if keys[pygame.K_r]: 
            return True
        return False

class MainFlower:
    def __init__(self, selected_character):
        self.selected_character = selected_character
        self.flower_size = 40
        self.position = [300, 300]
        self.flower_speed = 1
        self.flowerx_speed = 0.3
        self.update_flower_image()

    def update_flower_image(self):
        """Update the flower image based on the selected character."""
        if self.selected_character == "bee":
            self.flower_image = flower_image
        elif self.selected_character == "pinus":
            self.flower_image = cone_image
        elif self.selected_character == "colibri":
            self.flower_image = flower_image

    def update_position(self):
        """Update the flower's position based on the speed."""
        self.position[0] += self.flowerx_speed
        self.position[1] += self.flower_speed

        # Check if the flower goes too far up or down
        if self.position[1] > 380:
            self.flower_speed = -1
        elif self.position[1] < 220:
            self.flower_speed = 1

        # Check if the flower goes too far left or right
        if self.position[0] > 300:
            self.flowerx_speed = -0.3
        elif self.position[0] < 140:
            wind_sound.play()
            self.flowerx_speed = 0.8

    def draw(self, screen):
        """Draw the flower at its position."""
        screen.blit(self.flower_image, (self.position[0], self.position[1]))

def game_loop():
    global pipes, lives, last_hit_time
    cooldown_time = 1000
    last_hit_time = 0
    start_screen = StartScreen(screen)

    if start_screen.selected_character == "bee":
        player = Bee()
    elif start_screen.selected_character == "pinus":
        player = Pinus()
    elif start_screen.selected_character == "colibri":
        player = Colibri()

    score = 0
    color_cycle = 0
    running = True
    game_over_screen = None
    game_over = False
    pipes = reset_pipes()
    game_started = False
    chickens_theme_sound_played = False

    while running:

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                click_sound.play()
                player.flap()

        if not game_started:
            start_screen.display()

            if start_screen.handle_input():
                # Character selection
                if start_screen.selected_character == "bee":
                    player = Bee()
                elif start_screen.selected_character == "pinus":
                    player = Pinus()
                elif start_screen.selected_character == "colibri":
                    player = Colibri()

                # Spawn MainFlower
                main_flower = MainFlower(start_screen.selected_character)

                game_started = True

        elif game_over:
            if game_over_screen is None:
                game_over_screen = GameOverScreen(screen, score, start_screen.selected_character)

                # Play the game over sound
                if not chickens_theme_sound_played:
                    chickens_theme.play()
                    chickens_theme_sound_played = True
                pygame.mixer.music.stop()

            game_over_screen.display()

            if game_over_screen.handle_input():
                score = 0
                game_over_screen = None
                game_over = False
                game_started = False
                lives = 3
                pipes = reset_pipes()

                # Resume the background music after leaving the game over screen
                pygame.mixer.music.play(-1, 0.0)

                # Reset the sound play flag
                chickens_theme.stop()
                chickens_theme_sound_played = False
                continue

        else:
            screen.blit(background_image, (0, 0))

            draw_airplane()
            draw_airballoon()

            for i, pos in enumerate(airplane_positions):
                pos[0] -= 1

                if pos[0] < -500:
                    pos[0] = SCREEN_WIDTH + 50
                    pos[1] = random.randint(100, SCREEN_HEIGHT - 400)

            for i, pos in enumerate(airballoon_positions):
                pos[0] -= 0.5

                if pos[0] < -100:
                    pos[0] = SCREEN_WIDTH
                    pos[1] = random.randint(100, 400)

            screen.blit(flappy_image, flappy_rect)

            time = pygame.time.get_ticks() - flappy_start_time
            flappy_rect.x += flappy_speed
            flappy_rect.y = 100 + flappy_amplitude * math.sin(flappy_frequency * time)

            player.move()
            player.draw(screen)

            for pipe in pipes:
                pipe.update()
                pipe.draw(screen)

                if pipe.x + pipe.width < 0:
                    # Reset pipes position
                    pipe.x = SCREEN_WIDTH + 100
                    pipe.y = random.randint(100, SCREEN_HEIGHT - 400)
                    
                    color_cycle += 1
                    pipe.color_index = color_cycle % 6  # Cycle through all six colors
                    pipe.top_image, pipe.bottom_image = pipe.color_images[pipe.color_index]
                    
                    # Reset the score flag
                    pipe.scored = False

                if pygame.time.get_ticks() - last_hit_time > cooldown_time:
                    if pipe.x < player.x + 40 and pipe.x + pipe.width > player.x:
                        if player.y < pipe.top or player.y + 40 > pipe.bottom:
                            lives -= 1
                            hurt_sound.play()
                            last_hit_time = pygame.time.get_ticks()

                if pipe.x + pipe.width < player.x and not hasattr(pipe, "scored"):
                    score += 1
                    pipe.scored = True

            # Create and update MainFlower
            main_flower.update_position()
            main_flower.draw(screen)

            if lives <= 0:
                game_over = True

            score_text = font.render(f"Score: {score}", True, (255, 20, 147))
            screen.blit(score_text, (20, 20))

            if lives == 3:
                screen.blit(heart_image, (260, 10))
                screen.blit(heart_image, (300, 10))
                screen.blit(heart_image, (340, 10))
            
            if lives == 2:
                screen.blit(heart_image, (300, 10))
                screen.blit(heart_image, (340, 10))

            if lives == 1:
                screen.blit(heart_image, (340, 10))

            pygame.display.update()
            clock.tick(60)

game_loop()
pygame.quit()

Cyclopod

You play as a little cyclopod. Eat, grow and try not to get eaten yourself. You win if you eat the musquito larva.

Although not really related to bioinformatics, pygame is for me a somewhat more motivating way to play around with the python basics. The sprites and audio are from Super Mario World and I also attempted to draw some characters myself.

Because I am improving the game over time I created a project page on GitHub.

Expand to see the code
import pygame
import random
import sys

pygame.init()

# Constants
SCREEN_WIDTH = 500
SCREEN_HEIGHT = 750
FOOD_SIZE = 10
CELL_SIZE = 20
OBSTACLE_SIZE = 35
FOOD_COUNT = 6
OBSTACLE_COUNT = 10
OBSTACLE_SPEED = 5
FOOD_SPEED = 3
OBSTACLE2_SIZE = 60
OBSTACLE2_COUNT = 1
OBSTACLE2_SPEED = 8
OBSTACLE3_SIZE = 90
OBSTACLE3_COUNT = 1
OBSTACLE3_SPEED = 4
OBSTACLE4_SIZE = 120
OBSTACLE4_COUNT = 1
OBSTACLE4_SPEED = 2

# Colors
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
DARKGREEN = (0, 100, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
LIGHTBLUE = (173, 216, 230)
PURPLE = (128, 0, 128)

# Screen creation
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Cyclopod")

# Load sounds
pygame.mixer.music.load(r'C:\Users\Private\Desktop\Python\Zooplankton\underwatertheme.mp3')
pygame.mixer.music.play(-1,0,10000)
pygame.mixer.music.set_volume(0.8)

coin_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\mariocoin.wav')
mushroom10_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\mushroom.wav')
mushroom25_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\mushroom.wav')
finish_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\finish.wav')
gameover_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\gameover.wav')
menu_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\SuperMarioLand_menu.mp3')
yoshi_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Zooplankton\yoshi.wav')

# Flag for mushroom sound
mushroom10_played = False
mushroom25_played = False

# Load images
player_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\copepod_v1.png')
player_image = pygame.transform.scale(player_image, (CELL_SIZE, CELL_SIZE))

bigger_player_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\copepod_v1.png')
bigger_player_image = pygame.transform.scale(bigger_player_image, (30, 30))

even_bigger_player_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\copepod_v1.png')
even_bigger_player_image = pygame.transform.scale(bigger_player_image, (120, 120)) 

rotifer_v2_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\rotifer_v2.png')
rotifer_v2_image = pygame.transform.scale(rotifer_v2_image, (OBSTACLE_SIZE, OBSTACLE_SIZE))

larvae_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\larvae.png')
larvae_image = pygame.transform.scale(larvae_image,(100, 140))

fish_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\cheapcheap_clear.png')
fish_image = pygame.transform.scale(fish_image,(110, 110))

fishmirror_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\cheapcheap_clear_mirror2.png')
fishmirror_image = pygame.transform.scale(fishmirror_image,(130, 130))

background_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Zooplankton\mariowater.gif')
background_image = pygame.transform.scale(background_image, (SCREEN_WIDTH, SCREEN_HEIGHT))

# Player properties
player_pos = [SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2]
player_score = 0

# Food and obstacles
food_positions = [[random.randint(0, SCREEN_WIDTH - FOOD_SIZE), random.randint(0, SCREEN_HEIGHT - FOOD_SIZE)] for _ in range(FOOD_COUNT)]
obstacle_positions = [[random.randint(0, SCREEN_WIDTH - OBSTACLE_SIZE), random.randint(0, SCREEN_HEIGHT - OBSTACLE_SIZE)] for _ in range(OBSTACLE_COUNT)]
obstacle2_positions = [[random.randint(0, SCREEN_WIDTH - OBSTACLE2_SIZE), random.randint(0, SCREEN_HEIGHT - OBSTACLE2_SIZE)] for _ in range(OBSTACLE2_COUNT)]
obstacle3_positions = [[random.randint(500, 700), random.randint(0, SCREEN_HEIGHT - OBSTACLE3_SIZE)] for _ in range(OBSTACLE3_COUNT)]
obstacle4_positions = [[random.randint(-400, -100), random.randint(0, SCREEN_HEIGHT - OBSTACLE4_SIZE)] for _ in range(OBSTACLE3_COUNT)]

# Clock
clock = pygame.time.Clock()

# Timer
start_time = pygame.time.get_ticks()
font = pygame.font.SysFont(None, 36)
TIMER_EVENT = pygame.USEREVENT + 1

# Flags for game screens
game_over = False
you_won = False
end_time = None

# Time
elapsed_time = pygame.time.get_ticks() - start_time
seconds = elapsed_time // 1000

def draw_player():
    screen.blit(player_image, (player_pos[0], player_pos[1]))

    # draw the player's hitbox (optional for debugging)
    # player_rect = pygame.Rect(player_pos[0], player_pos[1], 100, 100) # Turn on/off for visbile hitbox
    # pygame.draw.rect(screen, (0, 255, 0), player_rect, 2) # Turn on/off for visbile hitbox

def draw_food():
    for pos in food_positions:
        pygame.draw.circle(screen, GREEN, (pos[0] + FOOD_SIZE // 2, pos[1] + FOOD_SIZE // 2), FOOD_SIZE // 2)

def draw_obstacles():
    for pos in obstacle_positions:
        # obstacle_rect = pygame.Rect(pos[0], pos[1], OBSTACLE_SIZE, 30) # Turn on/off for visbile hitbox
        screen.blit(rotifer_v2_image, (pos[0], pos[1]))
        # pygame.draw.rect(screen, (255, 0, 0), obstacle_rect, 2)  # Turn on/off for visbile hitbox

def draw_obstacles2():
    for pos in obstacle2_positions:
        # obstacle2_rect = pygame.Rect(pos[0] +30, pos[1], 40, 120) # Turn on/off for visbile hitbox
        screen.blit(larvae_image, (pos[0], pos[1]))
        # pygame.draw.rect(screen, (255, 0, 0), obstacle2_rect, 2)  # Turn on/off for visbile hitbox

def draw_obstacles3():
    for pos in obstacle3_positions:
        screen.blit(fish_image, (pos[0], pos[1]))

def draw_obstacles4():
    for pos in obstacle4_positions:
        screen.blit(fishmirror_image, (pos[0], pos[1]))

def check_collision_with_obstacles():
    if player_score < 10:
        player_rect = pygame.Rect(player_pos[0], player_pos[1], CELL_SIZE, CELL_SIZE)
        for obs in obstacle_positions:
            obstacle_rect = pygame.Rect(obs[0], obs[1], OBSTACLE_SIZE, 30)
            if player_rect.colliderect(obstacle_rect):
                return obs  
        return None
    if player_score >= 10:
        player_rect = pygame.Rect(player_pos[0], player_pos[1], 50, 50)
        for obs in obstacle_positions:
            obstacle_rect = pygame.Rect(obs[0], obs[1], OBSTACLE_SIZE, 30)
            if player_rect.colliderect(obstacle_rect):
                return obs
        return None
    if player_score >= 25:
        player_rect = pygame.Rect(player_pos[0], player_pos[1], 100, 100)
        for obs in obstacle_positions:
            obstacle_rect = pygame.Rect(obs[0], obs[1], OBSTACLE_SIZE, 30)
            if player_rect.colliderect(obstacle_rect):
                return obs
        return None

def check_collision_with_obstacles2():
    if player_score <10:
        player_rect = pygame.Rect(player_pos[0], player_pos[1], CELL_SIZE, CELL_SIZE)
        for obs in obstacle2_positions:
            obstacle2_rect = pygame.Rect(obs[0] +30, obs[1], 40, 120)
            if player_rect.colliderect(obstacle2_rect):
                return obs
        return None
    if player_score >= 10:
        player_rect = pygame.Rect(player_pos[0], player_pos[1], 50, 50)
        for obs in obstacle2_positions:
            obstacle2_rect = pygame.Rect(obs[0] +30, obs[1], 40, 120)
            if player_rect.colliderect(obstacle2_rect):
                return obs
        return None
    if player_score >= 25:
        player_rect = pygame.Rect(player_pos[0], player_pos[1], 100, 100)
        for obs in obstacle2_positions:
            obstacle2_rect = pygame.Rect(obs[0] +30, obs[1], 40, 120)
            if player_rect.colliderect(obstacle2_rect):
                return obs
        return None


def check_collision_with_obstacles3():
    player_rect = pygame.Rect(player_pos[0], player_pos[1], CELL_SIZE, CELL_SIZE)
    for obs in obstacle3_positions:
        obstacle3_rect = pygame.Rect(obs[0], obs[1], OBSTACLE3_SIZE, OBSTACLE3_SIZE)
        if player_rect.colliderect(obstacle3_rect):
            return obs
    return None

def check_collision_with_obstacles4():
    player_rect = pygame.Rect(player_pos[0], player_pos[1], CELL_SIZE, CELL_SIZE)
    for obs in obstacle4_positions:
        obstacle4_rect = pygame.Rect(obs[0], obs[1], OBSTACLE4_SIZE, OBSTACLE4_SIZE)
        if player_rect.colliderect(obstacle4_rect):
            return obs
    return None

class GameOverScreen:
    def __init__(self, screen, score):
        self.screen = screen
        self.score = score
        self.font = pygame.font.SysFont(None, 64)
        self.small_font = pygame.font.SysFont(None, 24)
        self.text_color = (255, 255, 255)

    def display(self):
        self.screen.fill((0, 0, 0))
        
        # Game Over text
        game_over_text = self.font.render("GAME OVER", True, (139, 0, 0))
        self.screen.blit(game_over_text, (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2, SCREEN_HEIGHT // 3))

        # Instructions to restart or quit
        restart_text = self.small_font.render("Press R to Restart or Q to Quit", True, self.text_color)
        self.screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, SCREEN_HEIGHT // 1.5))

        # Screen update
        pygame.display.flip()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_q]:
            pygame.quit()
            sys.exit()
        if keys[pygame.K_r]:
            gameover_sound.stop()  
            return True
        return False

class YouWonScreen:
    def __init__(self, screen, score):
        self.screen = screen
        self.score = score
        self.end_time = end_time
        self.start_time = start_time
        self.font = pygame.font.SysFont(None, 64)
        self.small_font = pygame.font.SysFont(None, 24)
        self.text_color = (255, 223, 0)

    def display(self):
        self.screen.fill((0, 0, 0))

        # Game Over text
        you_won_text = self.font.render("YOU WON", True, self.text_color)
        self.screen.blit(you_won_text, (SCREEN_WIDTH // 2 - you_won_text.get_width() // 2, SCREEN_HEIGHT // 3))

        # Display final time
        elapsed_time = (self.end_time - self.start_time) // 1000
        final_time_text = self.small_font.render(f"Your Time: {elapsed_time}s", True, (255, 255, 255))
        self.screen.blit(final_time_text, (SCREEN_WIDTH // 2 - final_time_text.get_width() // 2, SCREEN_HEIGHT // 2))

        # Instructions to restart or quit
        restart_text = self.small_font.render("Press Q to Quit", True, (255, 255, 255))
        self.screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, SCREEN_HEIGHT // 1.5))

        # Update the screen
        pygame.display.flip()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_q]:
            pygame.quit()
            sys.exit()
        if keys[pygame.K_r]:
            return True
        return False

class StartScreen:
    def __init__(self, screen):
        self.screen = screen
        self.font = pygame.font.SysFont(None, 64)
        self.small_font = pygame.font.SysFont(None, 36)
        self.smaller_font = pygame.font.SysFont(None, 25)
        self.text_color = (255, 255, 255)

    def display(self):
        screen.blit(background_image, (0, 0))

        # Start game text
        start_text = self.font.render("Cyclopod!", True, (173, 216, 230))
        self.screen.blit(start_text, (SCREEN_WIDTH // 2 - start_text.get_width() // 2, SCREEN_HEIGHT // 5))

        # Instructions
        move_text = self.smaller_font.render("Eat, Grow and Defeat the Musquito Larva", True, self.text_color)
        self.screen.blit(move_text, (SCREEN_WIDTH // 2 - move_text.get_width() // 2, SCREEN_HEIGHT // 3.5))

        instruction_text = self.small_font.render("Press Enter to Start", True, self.text_color)
        self.screen.blit(instruction_text, (SCREEN_WIDTH // 2 - instruction_text.get_width() // 2, SCREEN_HEIGHT // 1.5))

        screen.blit(even_bigger_player_image, (player_pos[0] - 70, player_pos[1] - 70))

        pygame.display.flip()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_RETURN]:
            return True
        return False


# Game loop
start_screen = StartScreen(screen)
game_over_screen = None
you_won_screen = None
game_started = False

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    if not game_started:
        start_screen.display()
        pygame.mixer.music.stop()
        menu_sound.play()
        menu_sound.set_volume(0.5)

        # Input to start the game
        if start_screen.handle_input():
            menu_sound.stop()
            pygame.mixer.music.play(-1)
            game_started = True


    elif game_over:
        if game_over_screen is None:
            game_over_screen = GameOverScreen(screen, player_score)

        game_over_screen.display()

        # Input for restart or quit
        if game_over_screen.handle_input():
            player_score = 0
            player_pos = [SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2]
            food_positions = [[random.randint(0, SCREEN_WIDTH - FOOD_SIZE), random.randint(0, SCREEN_HEIGHT - FOOD_SIZE)] for _ in range(FOOD_COUNT)]
            obstacle_positions = [[random.randint(0, SCREEN_WIDTH - OBSTACLE_SIZE), random.randint(0, SCREEN_HEIGHT - OBSTACLE_SIZE)] for _ in range(OBSTACLE_COUNT)]
            obstacle2_positions = [[random.randint(0, SCREEN_WIDTH - OBSTACLE2_SIZE), random.randint(0, SCREEN_HEIGHT - OBSTACLE2_SIZE)] for _ in range(OBSTACLE2_COUNT)]
            start_time = pygame.time.get_ticks()
            seconds = 0
            player_image = pygame.transform.scale(bigger_player_image, (20, 20))
            game_over_screen = None
            game_over = False
            pygame.mixer.music.play(-1)

            continue

    elif you_won:
        if you_won_screen is None:
            you_won_screen = YouWonScreen(screen, player_score)

        you_won_screen.display()

        if you_won_screen.handle_input():
            player_score = 0
            player_pos = [SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2]
            food_positions = [[random.randint(0, SCREEN_WIDTH - FOOD_SIZE), random.randint(0, SCREEN_HEIGHT - FOOD_SIZE)] for _ in range(FOOD_COUNT)]
            obstacle_positions = [[random.randint(0, SCREEN_WIDTH - OBSTACLE_SIZE), random.randint(0, SCREEN_HEIGHT - OBSTACLE_SIZE)] for _ in range(OBSTACLE_COUNT)]
            obstacle2_positions = [[random.randint(0, SCREEN_WIDTH - OBSTACLE2_SIZE), random.randint(0, SCREEN_HEIGHT - OBSTACLE2_SIZE)] for _ in range(OBSTACLE2_COUNT)]
            start_time = pygame.time.get_ticks()
            player_image = pygame.transform.scale(bigger_player_image, (20, 20))
            game_over_screen = None
            game_over = False
            pygame.mixer.music.play(-1)

            continue

    else:
        for i, pos in enumerate(obstacle_positions):
            pos[1] += OBSTACLE_SPEED  # Move obstacle down

            # If an obstacle moves off the screen, reset its position
            if pos[1] > SCREEN_HEIGHT:
                pos[1] = -50  # Reset to top of the screen
                pos[0] = random.randint(0, SCREEN_WIDTH - OBSTACLE_SIZE)  # Random new x position

        # Update obstacle2 positions (move downward)
        for i, pos in enumerate(obstacle2_positions):
            pos[1] += OBSTACLE2_SPEED  # Move obstacle down

            # If an obstacle2 moves off the screen, reset its position
            if pos[1] > SCREEN_HEIGHT:
                pos[1] = -120  # Reset to top of the screen
                pos[0] = random.randint(0, SCREEN_WIDTH - OBSTACLE2_SIZE)  # Random new x position

        # Update obstacle3 positions (move right to left)
        for i, pos in enumerate(obstacle3_positions):
            pos[0] -= OBSTACLE3_SPEED  # Move obstacle left

            # If an obstacle moves off the screen, reset its position
            if pos[0] < -500:
                pos[0] = SCREEN_WIDTH  # Reset to the right side of the screen
                pos[1] = random.randint(0, SCREEN_HEIGHT - OBSTACLE3_SIZE)  # Random new y position

        # Update obstacle4 positions (move right to left)
        for i, pos in enumerate(obstacle4_positions):
            pos[0] += OBSTACLE3_SPEED  # Move obstacle right

            # If an obstacle moves off the screen (past the right edge), reset its position
            if pos[0] > 900:
                pos[0] = -500
                pos[1] = random.randint(0, SCREEN_HEIGHT - OBSTACLE4_SIZE)

        # Update food positions (move downward)
        for i, pos in enumerate(food_positions[:] ):
            pos[1] += FOOD_SPEED  # Move food down by FOOD_SPEED

            # If food moves off the screen, remove it and spawn new food if score is less than 10
            if pos[1] > SCREEN_HEIGHT:
                food_positions.remove(pos)
                if player_score < 10:
                    new_food_pos = [random.randint(0, SCREEN_WIDTH - FOOD_SIZE), 0]  # Spawn new food at the top
                    food_positions.append(new_food_pos)  # Add the new food position to the list

        keys = pygame.key.get_pressed()

        if keys[pygame.K_a] and player_pos[0] > 0:
            player_pos[0] -= 5
        if keys[pygame.K_d] and player_pos[0] < SCREEN_WIDTH - CELL_SIZE:
            player_pos[0] += 5
        if keys[pygame.K_w] and player_pos[1] > 0:
            player_pos[1] -= 5
        if keys[pygame.K_s] and player_pos[1] < SCREEN_HEIGHT - CELL_SIZE:
            player_pos[1] += 5

        # Check if the player collides with any obstacles
        collided_obstacle = check_collision_with_obstacles()
        if collided_obstacle and seconds > 1:
            if player_score < 10:
                print("Game Over! You hit an rotifer.")
                pygame.mixer.music.stop()
                gameover_sound.play()
                pygame.time.wait(3700)
                game_over = True
                continue    
            if player_score >= 10:
                yoshi_sound.play()
                obstacle_positions.remove(collided_obstacle)
                player_score += 2

        # Check if the player collides with any obstacles2
        collided_obstacle2 = check_collision_with_obstacles2()
        if collided_obstacle2 and seconds > 1:
            if player_score < 25:
                print("Game Over! You hit a musquito larva.")
                pygame.mixer.music.stop()
                gameover_sound.play()
                pygame.time.wait(3700)
                game_over = True
                continue    
            if player_score >= 25:
                pygame.mixer.music.stop()
                finish_sound.play()
                obstacle2_positions.remove(collided_obstacle2)
                pygame.time.set_timer(TIMER_EVENT, 0)
                player_score += 25
                end_time = pygame.time.get_ticks()
                pygame.time.wait(8500)
                you_won = True

        # Check if the player collides with obstacles
        collided_obstacle = check_collision_with_obstacles3()
        if collided_obstacle and seconds > 1:
            print("Game Over! You hit a small fish.")
            pygame.mixer.music.stop()
            gameover_sound.play()
            pygame.time.wait(3700)
            game_over = True
            continue

        # Check if the player collides with obstacles
        collided_obstacle = check_collision_with_obstacles4()
        if collided_obstacle and seconds > 1:
            print("Game Over! You hit a big fish.")
            pygame.mixer.music.stop()
            gameover_sound.play()
            pygame.time.wait(3700)
            game_over = True
            continue

        screen.blit(background_image, (0, 0))  # Draw the background image
        
        draw_obstacles()
        draw_obstacles2()
        draw_obstacles3()
        draw_obstacles4()

        # Check for food collection
        for food in food_positions[:]:
            if (player_pos[0] < food[0] + FOOD_SIZE and
                player_pos[0] + CELL_SIZE > food[0] and
                player_pos[1] < food[1] + FOOD_SIZE and
                player_pos[1] + CELL_SIZE > food[1]):
                food_positions.remove(food)
                coin_sound.play()
                player_score += 1

                if player_score <= 10:
                    new_food_pos = [random.randint(0, SCREEN_WIDTH - FOOD_SIZE), 0]
                    food_positions.append(new_food_pos)

        # Play sound when score reaches 10
        if player_score == 10 and not mushroom10_played:
            mushroom10_sound.play()
            mushroom10_played = True

        # Play sound when score reaches 25 for the first time
        if player_score == 25 and not mushroom25_played:
            mushroom25_sound.play()
            mushroom25_played = True

        # Play sound when score reaches 26 for the first time
        if player_score == 26 and not mushroom25_played:
            mushroom25_sound.play()
            mushroom25_played = True

        # Play sound when score reaches 27 for the first time
        if player_score == 27 and not mushroom25_played:
            mushroom25_sound.play()
            mushroom25_played = True

        # After ... foods eaten, change player image
        if player_score >= 10:
            player_image = pygame.transform.scale(bigger_player_image, (50, 50))
        if player_score >= 25:
            player_image = pygame.transform.scale(bigger_player_image, (100, 100))
        if player_score >= 50:
            player_image = pygame.transform.scale(bigger_player_image, (150, 150))

        draw_food()
        draw_player()

        # Time display
        if not game_over:
            elapsed_time = pygame.time.get_ticks() - start_time
            seconds = elapsed_time // 1000
            timer_text = font.render(f"Time: {seconds}s", True, WHITE)
            screen.blit(timer_text, (SCREEN_WIDTH - 150, 10))

        pygame.display.update()
        clock.tick(30)

Contamination

A small video game where your mouse is a little blue flame from a bunsen burner that needs to protect a petri dish from bacterial contamination.

Because the project will get more updates I created a project page on GitHub.

Expand to see the code
# Libraries inladen
import pygame
import random
import sys

pygame.init()
pygame.mixer.init()

# Constanten
WIDTH, HEIGHT = 700, 700
PETRI_DISH_RADIUS = 40
MAX_BACTERIA = 3
WHITE = (220, 220, 220)
GREEN = (173, 216, 230)
RED = (23, 114, 69)
BLACK = (0, 0, 0)
FPS = 60
SPAWN_INTERVAL = 2000
INITIAL_SPEED_BACTERIA = 1

# Muziek inladen
pygame.mixer.music.load(r'c:\Users\Private\Desktop\Python\Contamination\petri_dish_team_sheepolution.mp3')
pygame.mixer.music.play(loops=-1)
pygame.mixer.music.set_volume(0.5)

# Geluidseffect inladen
click_sound = pygame.mixer.Sound(r'C:\Users\Private\Desktop\Python\Contamination\click_sound.mp3')

# Afbeeldingen inladen
flame_image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Contamination\flame.png')
flame_image = pygame.transform.scale(flame_image, (25, 40))  # Opschalen

# Muis onzichtbaar maken
pygame.mouse.set_visible(False)

# Bacterie klasse
class Bacteria:
    def __init__(self):
        self.image = pygame.image.load(r'C:\Users\Private\Desktop\Python\Contamination\bacteria.png')
        self.image = pygame.transform.scale(self.image, (30, 30))  # Opschalen

        if random.random() < 0.5:
            self.x = random.choice([-50, WIDTH + 50])
            self.y = random.randint(-50, HEIGHT + 50)
        else:
            self.x = random.randint(-50, WIDTH + 50)
            self.y = random.choice([-50, HEIGHT + 50])
        
        self.size = 50
        self.color = RED
        self.is_moving_away = False

    def draw(self, surface):
        # Bacteriën plaatsen met hitbox
        rect = self.image.get_rect(center=(self.x, self.y))
        surface.blit(self.image, rect)

    def move(self, speed):
        if self.is_moving_away:

            direction_x = self.x - (WIDTH // 2)
            direction_y = self.y - (HEIGHT // 2)
            distance = (direction_x**2 + direction_y**2)**0.5

            if distance != 0:
                direction_x /= distance
                direction_y /= distance
            
            self.x += direction_x * speed
            self.y += direction_y * speed
        else:
            if self.x > WIDTH // 2:
                self.x -= speed
            if self.x < WIDTH // 2:
                self.x += speed
            if self.y > HEIGHT // 2:
                self.y -= speed
            if self.y < HEIGHT // 2:
                self.y += speed

    def is_inside_dish(self):
        # Check of een bacterie in het petrischaaltje is
        return (self.x - WIDTH // 2) ** 2 + (self.y - HEIGHT // 2) ** 2 < PETRI_DISH_RADIUS ** 2

screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Petri Dish Defense')
clock = pygame.time.Clock()

class GameOverScreen:
    def __init__(self, screen, score):
        self.screen = screen
        self.score = score
        self.font = pygame.font.SysFont(None, 64)
        self.small_font = pygame.font.SysFont(None, 24)

    def display(self):
        self.screen.fill(WHITE)
        game_over_text = self.font.render("GAME OVER", True, (34, 139, 34))
        self.screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 4))

        score_text = self.small_font.render(f"Score: {self.score}", True, (34, 139, 34))
        self.screen.blit(score_text, (WIDTH // 2 - score_text.get_width() // 2, HEIGHT // 2.8))
        
        pygame.draw.circle(screen, (255, 249, 153), (WIDTH // 2, HEIGHT // 2), PETRI_DISH_RADIUS, 0)
        pygame.draw.circle(screen, GREEN, (WIDTH // 2, HEIGHT // 2), PETRI_DISH_RADIUS, 3)
        pygame.draw.circle(screen, (34, 139, 34), (WIDTH // 2.05, HEIGHT // 1.85), 5, 0)
        pygame.draw.circle(screen, (34, 139, 34), (WIDTH // 1.9, HEIGHT // 1.9), 8, 0)
        pygame.draw.circle(screen, (34, 139, 34), (WIDTH // 2.1, HEIGHT // 2.1), 10, 0)
        pygame.draw.circle(screen, (34, 139, 34), (WIDTH // 2.07, HEIGHT // 1.99), 5, 0)

        restart_text = self.small_font.render("Press R to Restart or Q to Quit", True, (34, 139, 34))
        self.screen.blit(restart_text, (WIDTH // 2 - restart_text.get_width() // 2, HEIGHT // 1.45))

        pygame.display.flip()

    def handle_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_q]:
            pygame.quit()
            sys.exit()
        if keys[pygame.K_r]: 
            return True
        return False

def main():
    bacteria_list = [Bacteria() for _ in range(MAX_BACTERIA)]
    score = 0
    font = pygame.font.Font(None, 36)
    last_spawn_time = pygame.time.get_ticks()
    game_over_screen = None
    game_over = False

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            if event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                for b in bacteria_list:
                    if (b.x - mouse_pos[0]) ** 2 + (b.y - mouse_pos[1]) ** 2 < b.size ** 2:
                        b.is_moving_away = True
                        score += 1
                        click_sound.play()
                        break
    
        if game_over:
            if game_over_screen is None:
                game_over_screen = GameOverScreen(screen, score)

            game_over_screen.display()

            if game_over_screen.handle_input():
                bacteria_list = [Bacteria() for _ in range(MAX_BACTERIA)]
                score = 0
                game_over_screen = None
                game_over = False
                continue

            continue

        # Bacteriën versnellen met toenemen score
        SPEED_BACTERIA = INITIAL_SPEED_BACTERIA + score // 3

        # Muispositie bepalen
        mouse_x, mouse_y = pygame.mouse.get_pos()
        pygame.draw.circle(screen, (100, 100, 100), (mouse_x, mouse_y), 10)

        current_time = pygame.time.get_ticks()
        if current_time - last_spawn_time >= SPAWN_INTERVAL:
            if len(bacteria_list) < 10: 
               bacteria_list.append(Bacteria())
            else:
                bacteria_list.pop(0) 
                bacteria_list.append(Bacteria())
            last_spawn_time = current_time


        for b in bacteria_list:
            b.move(SPEED_BACTERIA)
            if b.is_inside_dish():
                print("Game Over! A bacterium reached the petri dish.")
                game_over = True

        # Achtergrond
        screen.fill(WHITE)

        # Petrischaaltje weergeven
        pygame.draw.circle(screen, (255, 249, 153), (WIDTH // 2, HEIGHT // 2), PETRI_DISH_RADIUS, 0)
        pygame.draw.circle(screen, GREEN, (WIDTH // 2, HEIGHT // 2), PETRI_DISH_RADIUS, 3)

        # Bacteriën weergeven
        for b in bacteria_list:
            b.draw(screen)

        # Score weergeven
        score_text = font.render(f'Score: {score}', True, BLACK)
        screen.blit(score_text, (10, 10))

        # Vlammetje weergeven
        screen.blit(flame_image, (mouse_x - 15, mouse_y - 15))  # Centreren

        pygame.display.flip()
        clock.tick(FPS)

if __name__ == '__main__':
    main()