Menu
CoddyTech

Gewichtete Auswahl

Teil des Abschnitts Spielregeln der Pygame-Journey von Coddy. Lektion 18 von 29.

Ein Spiel ist interessanter, wenn nicht alles, was herunterfällt, gleich ist. Hier sind die Hälfte der herunterfallenden Dinge kleine Steine, einige sind große Steine und ein paar sind Edelsteine, die 5 Punkte wert sind. random.choice wählt jedes Element gleich oft aus. random.choices nimmt weights entgegen, also wie oft die einzelnen Elemente ausgewählt werden:

def pick_kind():
    return random.choices(["small", "big", "gem"], weights=[5, 3, 2])[0]

Die Gewichte 5, 3 und 2 ergeben zusammen 10, also sind von jeweils 10 herunterfallenden Dingen etwa 5 klein, 3 groß und 2 Edelsteine. Gewichte sind Verhältnisse, keine Prozentsätze: [50, 30, 20] bedeutet dasselbe.

random.choices kann mehrere Elemente auf einmal auswählen und gibt daher immer eine Liste zurück, auch wenn nur ein Element ausgewählt wird. [0] nimmt das Element aus der Liste.

Jede Art wird dann zu einem anderen Rect. Edelsteine kommen in eine eigene Liste, gems, denn einen zu berühren ist eine gute Nachricht. Der Starter bewegt, zeichnet und fängt Edelsteine bereits; noch lässt aber nichts einen fallen.

challenge icon

Aufgabe

Mittel
  1. Schreibe pick_kind(): Die Funktion gibt mit den Gewichtungen 5, 3 und 2 "small", "big" oder "gem" zurück.
  2. Lasse bei SPAWN das ausgewählte Objekt herunterfallen: einen kleinen Felsen mit den Maßen 24 × 24, einen großen Felsen mit den Maßen 40 × 40 (x von 0 bis 440, y -40) oder einen Edelstein mit den Maßen 20 × 20 (x von 0 bis 460, y -20), der zu gems hinzugefügt wird.

Probier es selbst

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Meteor Dodge")
clock = pygame.time.Clock()
heart = pygame.image.load("assets/images/heart.png").convert_alpha()
heart_empty = pygame.image.load("assets/images/heart_empty.png").convert_alpha()
font = pygame.font.Font("assets/fonts/pixel.ttf", 24)
big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48)
WHITE = (240, 240, 240)
PLATE = (60, 60, 90)
POINT = pygame.USEREVENT + 1
SPAWN = pygame.USEREVENT + 2


def reset():
    global player, rocks, gems, score, lives
    player = pygame.Rect(220, 300, 40, 40)
    rocks = []
    gems = []
    score = 0
    lives = 3
    pygame.time.set_timer(POINT, 1000)
    pygame.time.set_timer(SPAWN, 500)


def draw_menu():
    screen.fill((30, 30, 46))
    title = big_font.render("Meteor Dodge", True, WHITE)
    title_rect = title.get_rect(center=(240, 140))
    pygame.draw.rect(screen, PLATE, title_rect.inflate(24, 12))
    screen.blit(title, title_rect)
    hint = font.render("Press Enter to play", True, WHITE)
    screen.blit(hint, hint.get_rect(center=(240, 200)))


def draw_game():
    screen.fill((30, 30, 46))
    pygame.draw.rect(screen, (255, 196, 0), player)
    for rock in rocks:
        pygame.draw.rect(screen, (235, 80, 80), rock)
    for gem in gems:
        pygame.draw.rect(screen, (90, 220, 120), gem)
    score_text = font.render(f"{score:05}", True, WHITE)
    score_rect = score_text.get_rect(topright=(470, 10))
    pygame.draw.rect(screen, PLATE, score_rect.inflate(12, 8))
    screen.blit(score_text, score_rect)
    for i in range(3):
        if i < lives:
            screen.blit(heart, (10 + i * 40, 10))
        else:
            screen.blit(heart_empty, (10 + i * 40, 10))


def play_frame():
    global rocks, gems, score, lives, state
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player.x -= 6
    if keys[pygame.K_RIGHT]:
        player.x += 6
    player.clamp_ip(screen.get_rect())

    kept = []
    for rock in rocks:
        rock.y += 3
        if rock.colliderect(player):
            lives -= 1
        elif rock.top <= 360:
            kept.append(rock)
    rocks = kept
    kept = []
    for gem in gems:
        gem.y += 3
        if gem.colliderect(player):
            score += 5
        elif gem.top <= 360:
            kept.append(gem)
    gems = kept
    if lives <= 0:
        state = "over"
    draw_game()


def draw_over():
    draw_game()
    message = big_font.render("Game over", True, WHITE)
    screen.blit(message, message.get_rect(center=(240, 160)))
    hint = font.render("Press R to play again", True, WHITE)
    screen.blit(hint, hint.get_rect(center=(240, 200)))


reset()
state = "menu"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == POINT and state == "play":
            score += 1
        if event.type == SPAWN and state == "play":
            rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN and state == "menu":
                reset()
                state = "play"
            elif event.key == pygame.K_r and state == "over":
                reset()
                state = "play"

    if state == "menu":
        draw_menu()
    elif state == "play":
        play_frame()
    else:
        draw_over()
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Spielregeln

Übe selbstständig: Pygame-Playground