Mr. Do! with pygame

make a game like mr. do!

import pygame
import random

# Initialize Pygame
pygame.init()

# Set up the display
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mr. Do!-like Game")

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)

# Player
player_size = 40
player_x = WIDTH // 2
player_y = HEIGHT // 2
player_speed = 5

# Cherries
cherry_size = 20
cherries = []

# Enemies
enemy_size = 30
enemies = []

# Score
score = 0
font = pygame.font.Font(None, 36)

def spawn_cherry():
    x = random.randint(0, WIDTH - cherry_size)
    y = random.randint(0, HEIGHT - cherry_size)
    cherries.append(pygame.Rect(x, y, cherry_size, cherry_size))

def spawn_enemy():
    x = random.randint(0, WIDTH - enemy_size)
    y = random.randint(0, HEIGHT - enemy_size)
    enemies.append(pygame.Rect(x, y, enemy_size, enemy_size))

# Game loop
running = True
clock = pygame.time.Clock()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Move player
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player_x -= player_speed
    if keys[pygame.K_RIGHT]:
        player_x += player_speed
    if keys[pygame.K_UP]:
        player_y -= player_speed
    if keys[pygame.K_DOWN]:
        player_y += player_speed

    # Keep player on screen
    player_x = max(0, min(player_x, WIDTH - player_size))
    player_y = max(0, min(player_y, HEIGHT - player_size))

    # Spawn cherries
    if random.randint(1, 60) == 1:
        spawn_cherry()

    # Spawn enemies
    if random.randint(1, 180) == 1:
        spawn_enemy()

    # Check collisions
    player_rect = pygame.Rect(player_x, player_y, player_size, player_size)
    for cherry in cherries[:]:
        if player_rect.colliderect(cherry):
            cherries.remove(cherry)
            score += 10

    for enemy in enemies[:]:
        if player_rect.colliderect(enemy):
            running = False

    # Draw everything
    screen.fill(BLACK)
    pygame.draw.rect(screen, WHITE, (player_x, player_y, player_size, player_size))
    for cherry in cherries:
        pygame.draw.rect(screen, RED, cherry)
    for enemy in enemies:
        pygame.draw.rect(screen, GREEN, enemy)

    # Draw score
    score_text = font.render(f"Score: {score}", True, WHITE)
    screen.blit(score_text, (10, 10))

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

pygame.quit()

2. The labyrinth

first attempt

put the player and the enemies into a labyrinth and make the enemies chace the player

second attempt

draw the labyrint and if the game is over, make it possible to start again. Add score for every cherry eaten

Now we got the labyrinth, not bad if it wasn’t for the fact that we cannot escape the enemies.

Let’s ask to be able to dig and destroy walls with a key and fire a ball with another that bounces on the wall and kill enemies the enemies. We got one ball and we get touch it after we throu it we can launch it again.

import pygame
import random

# Initialize Pygame
pygame.init()

# Set up the display
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mr. Do!-like Game")

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WALL_COLOR = (100, 50, 0)  # Brown color for walls

# Grid settings
GRID_SIZE = 40
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE

# Font
font = pygame.font.Font(None, 36)

# Game variables
player_x = player_y = 1
enemies = []
cherries = []
score = 0
game_over = False

def create_labyrinth():
    labyrinth = [[1 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]

    def create_path(x, y):
        labyrinth[y][x] = 0
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        random.shuffle(directions)
        for dx, dy in directions:
            nx, ny = x + dx*2, y + dy*2
            if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT and labyrinth[ny][nx] == 1:
                labyrinth[y + dy][x + dx] = 0
                create_path(nx, ny)

    create_path(1, 1)
    return labyrinth

def place_cherries():
    cherries = []
    for _ in range(20):
        x, y = random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)
        if labyrinth[y][x] == 0:
            cherries.append((x, y))
    return cherries

def place_enemies():
    enemies = []
    for _ in range(3):
        while True:
            x, y = random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)
            if labyrinth[y][x] == 0 and (x, y) != (player_x, player_y):
                enemies.append([x, y])
                break
    return enemies

def move_towards(enemy, target_x, target_y):
    dx = target_x - enemy[0]
    dy = target_y - enemy[1]
    if abs(dx) > abs(dy):
        return (enemy[0] + (1 if dx > 0 else -1), enemy[1])
    else:
        return (enemy[0], enemy[1] + (1 if dy > 0 else -1))

def draw_game():
    screen.fill(BLACK)
    
    # Draw labyrinth
    for y in range(GRID_HEIGHT):
        for x in range(GRID_WIDTH):
            if labyrinth[y][x] == 1:
                pygame.draw.rect(screen, WALL_COLOR, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE))
                pygame.draw.rect(screen, BLACK, (x * GRID_SIZE + 2, y * GRID_SIZE + 2, GRID_SIZE - 4, GRID_SIZE - 4))

    # Draw player
    pygame.draw.circle(screen, WHITE, (player_x * GRID_SIZE + GRID_SIZE // 2, player_y * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 2 - 2)

    # Draw cherries
    for cherry in cherries:
        pygame.draw.circle(screen, RED, (cherry[0] * GRID_SIZE + GRID_SIZE // 2, cherry[1] * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 4)

    # Draw enemies
    for enemy in enemies:
        pygame.draw.rect(screen, GREEN, (enemy[0] * GRID_SIZE + 2, enemy[1] * GRID_SIZE + 2, GRID_SIZE - 4, GRID_SIZE - 4))

    # Draw score
    score_text = font.render(f"Score: {score}", True, WHITE)
    screen.blit(score_text, (10, 10))

    if game_over:
        game_over_text = font.render("Game Over! Press R to Restart", True, WHITE)
        screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - game_over_text.get_height() // 2))

    pygame.display.flip()

def reset_game():
    global player_x, player_y, enemies, cherries, score, game_over, labyrinth
    player_x = player_y = 1
    labyrinth = create_labyrinth()
    cherries = place_cherries()
    enemies = place_enemies()
    score = 0
    game_over = False

# Initial game setup
reset_game()

# Game loop
running = True
clock = pygame.time.Clock()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if game_over:
                if event.key == pygame.K_r:
                    reset_game()
            else:
                new_x, new_y = player_x, player_y
                if event.key == pygame.K_LEFT:
                    new_x -= 1
                elif event.key == pygame.K_RIGHT:
                    new_x += 1
                elif event.key == pygame.K_UP:
                    new_y -= 1
                elif event.key == pygame.K_DOWN:
                    new_y += 1
                
                if 0 <= new_x < GRID_WIDTH and 0 <= new_y < GRID_HEIGHT and labyrinth[new_y][new_x] == 0:
                    player_x, player_y = new_x, new_y

    if not game_over:
        # Move enemies
        for enemy in enemies:
            new_pos = move_towards(enemy, player_x, player_y)
            if labyrinth[new_pos[1]][new_pos[0]] == 0:
                enemy[0], enemy[1] = new_pos

        # Check collisions
        if (player_x, player_y) in cherries:
            cherries.remove((player_x, player_y))
            score += 10

        if [player_x, player_y] in enemies:
            game_over = True

    draw_game()
    clock.tick(10)  # Slower speed for grid-based movement

pygame.quit()

3. Ball bouncing

Make the player able to destroy walls with “z” and to launch a ball with “x”. The has a diagonal direction and bounces on the walls. If it reaches an enemy, kills the enemy and return to the player that can launch one ball at the time until it returns it back. If the player get into the ball the ball stops to move and the player can launch the ball again.

Claude stopped me for a couple of hour because I exeeded my messages, so I turned to Chat GPT to correct a bug there was in the code and it gave me this

import pygame
import random
import math

# Initialize Pygame
pygame.init()

# Set up the display
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mr. Do!-like Game")

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WALL_COLOR = (100, 50, 0)
BALL_COLOR = (255, 165, 0)  # Orange

# Grid settings
GRID_SIZE = 40
GRID_WIDTH = WIDTH // GRID_SIZE
GRID_HEIGHT = HEIGHT // GRID_SIZE

# Font
font = pygame.font.Font(None, 36)

# Game variables
player_x = player_y = 1
player_direction = 0  # 0: right, 1: down, 2: left, 3: up
enemies = []
cherries = []
score = 0
game_over = False

# Ball variables
ball_x = ball_y = None
ball_direction = None
ball_active = False

def create_labyrinth():
    labyrinth = [[1 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]

    def create_path(x, y):
        labyrinth[y][x] = 0
        directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        random.shuffle(directions)
        for dx, dy in directions:
            nx, ny = x + dx*2, y + dy*2
            if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT and labyrinth[ny][nx] == 1:
                labyrinth[y + dy][x + dx] = 0
                create_path(nx, ny)

    create_path(1, 1)
    return labyrinth

def place_cherries():
    return [(x, y) for y in range(GRID_HEIGHT) for x in range(GRID_WIDTH) 
            if labyrinth[y][x] == 0 and random.random() < 0.1]

def place_enemies():
    enemy_list = []
    while len(enemy_list) < 3:
        x, y = random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)
        if labyrinth[y][x] == 0 and (x, y) != (player_x, player_y):
            enemy_list.append([x, y])
    return enemy_list

def move_towards(enemy, target_x, target_y):
    dx = target_x - enemy[0]
    dy = target_y - enemy[1]
    if abs(dx) > abs(dy):
        return (enemy[0] + (1 if dx > 0 else -1), enemy[1])
    else:
        return (enemy[0], enemy[1] + (1 if dy > 0 else -1))

def launch_ball():
    global ball_x, ball_y, ball_direction, ball_active
    if not ball_active:
        ball_x, ball_y = player_x, player_y
        ball_direction = player_direction
        ball_active = True

def move_ball():
    global ball_x, ball_y, ball_direction, ball_active, enemies, score
    if ball_active:
        directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]
        dx, dy = directions[ball_direction]
        new_x, new_y = ball_x + dx, ball_y + dy

        if 0 <= new_x < GRID_WIDTH and 0 <= new_y < GRID_HEIGHT:
            if labyrinth[new_y][new_x] == 1:
                if dx != 0 and labyrinth[ball_y][new_x] == 1:
                    ball_direction = (ball_direction + 2) % 4  # Reverse x direction
                elif dy != 0 and labyrinth[new_y][ball_x] == 1:
                    ball_direction = (ball_direction + 2) % 4  # Reverse y direction
                else:
                    ball_direction = (ball_direction + 1) % 4  # Change direction
            else:
                ball_x, ball_y = new_x, new_y

                # Check for enemy collision
                for enemy in enemies[:]:
                    if ball_x == enemy[0] and ball_y == enemy[1]:
                        enemies.remove(enemy)
                        score += 50
                        ball_active = False
                        return

                # Check for player collision
                if ball_x == player_x and ball_y == player_y:
                    ball_active = False
        else:
            ball_active = False  # Deactivate ball at screen edge

def draw_game():
    screen.fill(BLACK)
    
    # Draw labyrinth
    for y in range(GRID_HEIGHT):
        for x in range(GRID_WIDTH):
            if labyrinth[y][x] == 1:
                pygame.draw.rect(screen, WALL_COLOR, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE))
                pygame.draw.rect(screen, BLACK, (x * GRID_SIZE + 2, y * GRID_SIZE + 2, GRID_SIZE - 4, GRID_SIZE - 4))

    # Draw player
    pygame.draw.circle(screen, WHITE, (player_x * GRID_SIZE + GRID_SIZE // 2, player_y * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 2 - 2)

    # Draw cherries
    for cherry in cherries:
        pygame.draw.circle(screen, RED, (cherry[0] * GRID_SIZE + GRID_SIZE // 2, cherry[1] * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 4)

    # Draw enemies
    for enemy in enemies:
        pygame.draw.rect(screen, GREEN, (enemy[0] * GRID_SIZE + 2, enemy[1] * GRID_SIZE + 2, GRID_SIZE - 4, GRID_SIZE - 4))

    # Draw ball
    if ball_active:
        pygame.draw.circle(screen, BALL_COLOR, (ball_x * GRID_SIZE + GRID_SIZE // 2, ball_y * GRID_SIZE + GRID_SIZE // 2), GRID_SIZE // 4)

    # Draw score
    score_text = font.render(f"Score: {score}", True, WHITE)
    screen.blit(score_text, (10, 10))

    if game_over:
        game_over_text = font.render("Game Over! Press R to Restart", True, WHITE)
        screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2 - game_over_text.get_height() // 2))

    pygame.display.flip()

def reset_game():
    global player_x, player_y, player_direction, enemies, cherries, score, game_over, labyrinth, ball_active
    player_x = player_y = 1
    player_direction = 0
    labyrinth = create_labyrinth()
    cherries = place_cherries()
    enemies = place_enemies()
    score = 0
    game_over = False
    ball_active = False

# Initial game setup
reset_game()

# Game loop
running = True
clock = pygame.time.Clock()

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if game_over:
                if event.key == pygame.K_r:
                    reset_game()
            else:
                new_x, new_y = player_x, player_y
                if event.key == pygame.K_LEFT:
                    new_x -= 1
                    player_direction = 2
                elif event.key == pygame.K_RIGHT:
                    new_x += 1
                    player_direction = 0
                elif event.key == pygame.K_UP:
                    new_y -= 1
                    player_direction = 3
                elif event.key == pygame.K_DOWN:
                    new_y += 1
                    player_direction = 1
                elif event.key == pygame.K_z:
                    # Destroy wall
                    dx, dy = [(1, 0), (0, 1), (-1, 0), (0, -1)][player_direction]
                    wall_x, wall_y = player_x + dx, player_y + dy
                    if 0 <= wall_x < GRID_WIDTH and 0 <= wall_y < GRID_HEIGHT:
                        if labyrinth[wall_y][wall_x] == 1:
                            labyrinth[wall_y][wall_x] = 0
                            score += 5
                elif event.key == pygame.K_x:
                    launch_ball()
                
                if 0 <= new_x < GRID_WIDTH and 0 <= new_y < GRID_HEIGHT and labyrinth[new_y][new_x] == 0:
                    player_x, player_y = new_x, new_y

    if not game_over:
        # Move enemies
        for enemy in enemies:
            new_pos = move_towards(enemy, player_x, player_y)
            if labyrinth[new_pos[1]][new_pos[0]] == 0:
                enemy[0], enemy[1] = new_pos

        # Move ball
        move_ball()

        # Check collisions
        if (player_x, player_y) in cherries:
            cherries.remove((player_x, player_y))
            score += 10

        if [player_x, player_y] in enemies:
            game_over = True

    draw_game()
    clock.tick(10)  # Slower speed for grid-based movement

pygame.quit()

As you can see the game is getting some sense, even if the tajectory of the ball is not what I intended but it comes back and works well apart for the direction, but that can be fixed in the next post. Bye.


Subscribe to the newsletter for updates
Tkinter templates

Avatar My youtube channel

Twitter: @pythonprogrammi - python_pygame

Claude's Games

Arkanoid
Platform 2d

1. Memory game

Videos

Speech recognition game

Pygame's Platform Game

Other Pygame's posts

Advertisement