Crappy bird touch(flappybird'ish) for Librem 5 and Whatever

Who wants to play a worse-than-Atari version of flappy bird on your Librem with green bar pipes, a blue background, and a red dot for bird. Bang away at the touch screen to 'flap", edit the python to fix speed or pipe sizes.

import pygame
import sys
import random
from enum import Enum

class GameState(Enum):
    MENU = 1
    PLAYING = 2
    GAME_OVER = 3

class FlappyBird:
    def __init__(self):
        pygame.init()
        
        # Screen dimensions
        self.WIDTH = 400
        self.HEIGHT = 600
        self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
        pygame.display.set_caption("Flappy Bird")
        
        # Colors
        self.WHITE = (255, 255, 255)
        self.BLACK = (0, 0, 0)
        self.GREEN = (0, 200, 0)
        self.RED = (255, 0, 0)
        self.BLUE = (100, 149, 237)
        
        # Clock for FPS
        self.clock = pygame.time.Clock()
        self.fps = 60
        
        # Game state
        self.state = GameState.MENU
        self.score = 0
        self.high_score = 0
        
        # Initialize game objects
        self.init_game()
    
    def init_game(self):
        """Initialize or reset game objects"""
        # Bird properties
        self.bird_x = self.WIDTH // 4
        self.bird_y = self.HEIGHT // 2
        self.bird_width = 30
        self.bird_height = 30
        self.bird_velocity = 0
        self.gravity = 0.6
        self.flap_strength = -12
        
        # Pipes
        self.pipe_width = 50
        self.pipe_gap = 120
        self.pipe_velocity = -5
        self.pipes = []
        self.spawn_pipe()
        
        self.score = 0
    
    def spawn_pipe(self):
        """Create a new pipe pair"""
        min_height = 50
        max_height = self.HEIGHT - self.pipe_gap - 50
        pipe_height = random.randint(min_height, max_height)
        
        self.pipes.append({
            'x': self.WIDTH,
            'top_height': pipe_height,
            'bottom_y': pipe_height + self.pipe_gap,
            'scored': False
        })
    
    def handle_input(self):
        """Handle user input"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            
            # Detect any touch/click to flap
            if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.KEYDOWN:
                if self.state == GameState.MENU:
                    self.state = GameState.PLAYING
                elif self.state == GameState.PLAYING:
                    self.bird_velocity = self.flap_strength
                elif self.state == GameState.GAME_OVER:
                    self.state = GameState.MENU
                    self.init_game()
        
        return True
    
    def update(self):
        """Update game logic"""
        if self.state != GameState.PLAYING:
            return
        
        # Apply gravity
        self.bird_velocity += self.gravity
        self.bird_y += self.bird_velocity
        
        # Update pipes
        for pipe in self.pipes:
            pipe['x'] += self.pipe_velocity
            
            # Check if bird passed the pipe
            if not pipe['scored'] and pipe['x'] + self.pipe_width < self.bird_x:
                pipe['scored'] = True
                self.score += 1
        
        # Remove pipes that are off-screen
        self.pipes = [p for p in self.pipes if p['x'] + self.pipe_width > 0]
        
        # Spawn new pipe when needed
        if len(self.pipes) == 0 or self.pipes[-1]['x'] < self.WIDTH - 200:
            self.spawn_pipe()
        
        # Check collisions
        if self.check_collision():
            self.state = GameState.GAME_OVER
            if self.score > self.high_score:
                self.high_score = self.score
    
    def check_collision(self):
        """Check if bird collided with pipes or boundaries"""
        # Ground and ceiling collision
        if self.bird_y + self.bird_height > self.HEIGHT or self.bird_y < 0:
            return True
        
        # Pipe collision
        bird_rect = pygame.Rect(self.bird_x, self.bird_y, self.bird_width, self.bird_height)
        
        for pipe in self.pipes:
            # Top pipe
            top_pipe_rect = pygame.Rect(pipe['x'], 0, self.pipe_width, pipe['top_height'])
            if bird_rect.colliderect(top_pipe_rect):
                return True
            
            # Bottom pipe
            bottom_pipe_rect = pygame.Rect(pipe['x'], pipe['bottom_y'], self.pipe_width, self.HEIGHT - pipe['bottom_y'])
            if bird_rect.colliderect(bottom_pipe_rect):
                return True
        
        return False
    
    def draw(self):
        """Draw game elements"""
        self.screen.fill(self.BLUE)
        
        # Draw pipes
        for pipe in self.pipes:
            # Top pipe
            pygame.draw.rect(self.screen, self.GREEN, (pipe['x'], 0, self.pipe_width, pipe['top_height']))
            # Bottom pipe
            pygame.draw.rect(self.screen, self.GREEN, (pipe['x'], pipe['bottom_y'], self.pipe_width, self.HEIGHT - pipe['bottom_y']))
        
        # Draw bird
        pygame.draw.rect(self.screen, self.RED, (self.bird_x, self.bird_y, self.bird_width, self.bird_height))
        
        # Draw UI
        font = pygame.font.Font(None, 36)
        score_text = font.render(f"Score: {self.score}", True, self.WHITE)
        self.screen.blit(score_text, (10, 10))
        
        # Draw game state text
        if self.state == GameState.MENU:
            menu_font = pygame.font.Font(None, 48)
            title = menu_font.render("Flappy Bird", True, self.WHITE)
            instruction = font.render("Tap/Click to Start", True, self.WHITE)
            self.screen.blit(title, (self.WIDTH // 2 - title.get_width() // 2, self.HEIGHT // 3))
            self.screen.blit(instruction, (self.WIDTH // 2 - instruction.get_width() // 2, self.HEIGHT // 2))
        
        elif self.state == GameState.GAME_OVER:
            gameover_font = pygame.font.Font(None, 48)
            gameover_text = gameover_font.render("Game Over", True, self.WHITE)
            high_score_text = font.render(f"High Score: {self.high_score}", True, self.WHITE)
            restart_text = font.render("Tap/Click to Restart", True, self.WHITE)
            
            self.screen.blit(gameover_text, (self.WIDTH // 2 - gameover_text.get_width() // 2, self.HEIGHT // 3))
            self.screen.blit(high_score_text, (self.WIDTH // 2 - high_score_text.get_width() // 2, self.HEIGHT // 2 - 40))
            self.screen.blit(restart_text, (self.WIDTH // 2 - restart_text.get_width() // 2, self.HEIGHT // 2 + 40))
        
        pygame.display.flip()
    
    def run(self):
        """Main game loop"""
        running = True
        while running:
            running = self.handle_input()
            self.update()
            self.draw()
            self.clock.tick(self.fps)
        
        pygame.quit()
        sys.exit()

if __name__ == "__main__":
    game = FlappyBird()
    game.run()

I made a .deb for myself if that is desired

2 Likes

here is something slightly better looking with cute clouds and the bird has an eye but also worse somehow

#!/usr/bin/env python3
import pygame
import sys
import math
import random
from enum import Enum

# Initialize Pygame
pygame.init()
pygame.mixer.init()

# Screen dimensions
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600

# Colors
SKY_BLUE = (135, 206, 235)
GRASS_GREEN = (34, 139, 34)
PIPE_GREEN = (34, 177, 76)
PIPE_DARK = (0, 100, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Game physics
GRAVITY = 1 
FLAP_STRENGTH = -12 
PIPE_VELOCITY = -30 
PIPE_WIDTH = 52
PIPE_GAP = 120

class GameState(Enum):
    MENU = 1
    PLAYING = 2
    GAME_OVER = 3

class Bird:
    def __init__(self):
        self.x = 50
        self.y = SCREEN_HEIGHT // 2
        self.velocity = 0
        self.radius = 12
    
    def flap(self):
        self.velocity = FLAP_STRENGTH
    
    def update(self):
        self.velocity += GRAVITY
        self.y += self.velocity
    
    def draw(self, surface):
        # Draw bird as a circle with a simple eye
        pygame.draw.circle(surface, RED, (int(self.x), int(self.y)), self.radius)
        # Eye
        pygame.draw.circle(surface, WHITE, (int(self.x) + 5, int(self.y) - 3), 4)
        pygame.draw.circle(surface, BLACK, (int(self.x) + 6, int(self.y) - 3), 2)

class Pipe:
    def __init__(self, x):
        self.x = x
        self.gap_y = random.randint(100, SCREEN_HEIGHT - PIPE_GAP - 100)
        self.width = PIPE_WIDTH
        self.gap = PIPE_GAP
        self.passed = False
    
    def update(self):
        self.x += PIPE_VELOCITY
    
    def draw(self, surface):
        # Top pipe
        top_height = self.gap_y
        pygame.draw.rect(surface, PIPE_GREEN, (self.x, 0, self.width, top_height))
        pygame.draw.rect(surface, PIPE_DARK, (self.x, 0, self.width, top_height - 4))
        
        # Bottom pipe
        bottom_y = self.gap_y + self.gap
        bottom_height = SCREEN_HEIGHT - bottom_y
        pygame.draw.rect(surface, PIPE_GREEN, (self.x, bottom_y, self.width, bottom_height))
        pygame.draw.rect(surface, PIPE_DARK, (self.x, bottom_y, self.width, bottom_height - 4))

def draw_clouds(surface, offset):
    """Draw simple clouds with parallax effect"""
    cloud_color = (240, 240, 240)
    clouds = [
        (50, 80), (150, 120), (250, 60), (350, 100)
    ]
    for cx, cy in clouds:
        x = (cx + offset) % (SCREEN_WIDTH + 100) - 50
        # Cloud: three circles
        pygame.draw.circle(surface, cloud_color, (int(x), cy), 20)
        pygame.draw.circle(surface, cloud_color, (int(x) + 20, cy), 25)
        pygame.draw.circle(surface, cloud_color, (int(x) + 40, cy), 20)

def draw_background(surface, cloud_offset):
    """Draw sky and clouds"""
    surface.fill(SKY_BLUE)
    draw_clouds(surface, cloud_offset)
    # Simple grass at bottom
    pygame.draw.rect(surface, GRASS_GREEN, (0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, 20))


def load_sounds():
    """Load sound effects from package data"""
    sounds = {}
    try:
        # Try to load from package installation directory
        sounds['flap'] = pygame.mixer.Sound('/usr/share/flappybird/whoosh.ogg')
        sounds['bing'] = pygame.mixer.Sound('/usr/share/flappybird/fwooosh.ogg')
    except:
        print("Warning: Sound files not found. Game will run without sound.")
        # Create silent placeholder sounds so the game doesn't crash
        silent = pygame.mixer.Sound(buffer=bytes([0] * 1000))
        sounds['flap'] = silent
        sounds['bing'] = silent
    
    return sounds


def main():
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("Flappy Bird Clone")
    clock = pygame.time.Clock()
    font = pygame.font.Font(None, 48)
    small_font = pygame.font.Font(None, 24)
    
    # Load sounds
    sounds = load_sounds()
    
    # Game variables
    state = GameState.MENU
    bird = Bird()
    pipes = []
    score = 0
    cloud_offset = 0
    pipe_spawn_counter = 0
    
    running = True
    while running:
        clock.tick(60)
        
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.KEYDOWN:
                if state == GameState.MENU:
                    state = GameState.PLAYING
                    bird = Bird()
                    pipes = []
                    score = 0
                    pipe_spawn_counter = 0
                elif state == GameState.PLAYING:
                    bird.flap()
                    sounds['flap'].play()
                elif state == GameState.GAME_OVER:
                    state = GameState.MENU
        
        # Update game logic
        if state == GameState.PLAYING:
            bird.update()
            cloud_offset -= 0.5  # Slow cloud parallax
            
            # Spawn pipes
            pipe_spawn_counter += 1
            if pipe_spawn_counter > 90:  # Spawn every ~1.5 seconds at 60 FPS
                pipes.append(Pipe(SCREEN_WIDTH))
                pipe_spawn_counter = 0
            
            # Update pipes
            for pipe in pipes:
                pipe.update()
                # Check if bird passed the pipe
                if not pipe.passed and pipe.x + pipe.width < bird.x:
                    pipe.passed = True
                    score += 1
                    sounds['bing'].play()
            
            # Remove off-screen pipes
            pipes = [p for p in pipes if p.x > -p.width]
            
            # Collision detection
            if bird.y - bird.radius < 0 or bird.y + bird.radius > SCREEN_HEIGHT - 20:
                state = GameState.GAME_OVER
            
            for pipe in pipes:
                # Check if bird collides with pipe
                if (bird.x + bird.radius > pipe.x and 
                    bird.x - bird.radius < pipe.x + pipe.width):
                    if (bird.y - bird.radius < pipe.gap_y or 
                        bird.y + bird.radius > pipe.gap_y + pipe.gap):
                        state = GameState.GAME_OVER
        
        # Draw everything
        draw_background(screen, cloud_offset)
        
        if state == GameState.MENU:
            title = font.render("Flappy Bird", True, BLACK)
            start = small_font.render("Click or Press Any Key to Start", True, BLACK)
            screen.blit(title, (SCREEN_WIDTH // 2 - title.get_width() // 2, 150))
            screen.blit(start, (SCREEN_WIDTH // 2 - start.get_width() // 2, 300))
            bird.draw(screen)
        
        elif state == GameState.PLAYING:
            bird.draw(screen)
            for pipe in pipes:
                pipe.draw(screen)
            score_text = font.render(str(score), True, BLACK)
            screen.blit(score_text, (10, 10))
        
        elif state == GameState.GAME_OVER:
            bird.draw(screen)
            for pipe in pipes:
                pipe.draw(screen)
            game_over_text = font.render("Game Over", True, BLACK)
            final_score = small_font.render(f"Score: {score}", True, BLACK)
            restart = small_font.render("Click or Press Any Key to Restart", True, BLACK)
            screen.blit(game_over_text, (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2, 150))
            screen.blit(final_score, (SCREEN_WIDTH // 2 - final_score.get_width() // 2, 250))
            screen.blit(restart, (SCREEN_WIDTH // 2 - restart.get_width() // 2, 320))
        
        pygame.display.flip()
    
    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()