Spielfeld formen

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
cupo_56
User
Beiträge: 2
Registriert: Freitag 2. Juni 2023, 20:05

Hallo zusammen. ich programmiere gerade das spiel "mensch aerger dich nicht". Ich habe schon die farbauswahl implementiert indem ein extra fenster zuerst geoeffnet wird. nach der farbauswahl wird das spielfeld angezeigt. allerding fehlt noch die eigentliche spiellogik, heisst ich baue zuerst dei grundlagen. das spielfeld wird allerding als rechteck angezeigt bzw ich habe die zielfelder und startfelder sowie die teamfelder in der entsprechenden farbe dargestellt. Mich stoeren die unnotigen felder dazwischen und das mein spielfeld nicht kreuzfoermig ist. hat jemand einen vorschlag wie ich das umschreiben koennte. Tut mir leid wegen den umlauten, habe eine amerikansiche tastatur.

Code: Alles auswählen

import pygame
import sys
import random

# Pygame initialisieren
pygame.init()

# Fenstergröße festlegen
WINDOW_SIZE = (400, 200)

# Fenster erstellen
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption('Farbauswahl')

# Farben definieren
RED = (255, 0, 0)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

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

# Spielfarben
spielfarben = [
    (RED, "Rot", False),
    (GREEN, "Grün", False),
    (YELLOW, "Gelb", False),
    (BLUE, "Blau", False)
]

# Farbauswahl-Funktion für Spieler
def farbauswahl(spieler_nummer):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                for i, (farbe, name, gewaehlt) in enumerate(spielfarben):
                    if 50 + i * 80 <= mouse_pos[0] <= 50 + (i + 1) * 80 and 80 <= mouse_pos[1] <= 160:
                        if not gewaehlt:
                            spielfarben[i] = (farbe, name, True)
                            return farbe

        # Hintergrund füllen
        screen.fill(WHITE)

        # Farbauswahl anzeigen
        for i, (farbe, name, gewaehlt) in enumerate(spielfarben):
            if gewaehlt:
                pygame.draw.rect(screen, (200, 200, 200), pygame.Rect(50 + i * 80, 80, 60, 60))
                text = font.render(str(spieler_nummer), True, BLACK)
                text_rect = text.get_rect(center=(50 + i * 80 + 30, 160))
                screen.blit(text, text_rect)
            else:
                pygame.draw.rect(screen, farbe, pygame.Rect(50 + i * 80, 80, 60, 60))
                text = font.render(name, True, BLACK)
                text_rect = text.get_rect(center=(50 + i * 80 + 30, 160))
                screen.blit(text, text_rect)

        # Anzeige aktualisieren
        pygame.display.flip()

# Spielerfarben wählen
spieler_farben = []
for spieler_nummer in range(1, 5):
    farbe = farbauswahl(spieler_nummer)
    spieler_farben.append(farbe)
    spielfarben = [(color, name, gewaehlt) for color, name, gewaehlt in spielfarben if color != farbe]

# Fenstergröße festlegen
WINDOW_SIZE = (600, 600)

# Fenster erstellen
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption('Mensch Ärger Dich Nicht')

# Breite und Höhe eines einzelnen Quadrats
SQUARE_SIZE = WINDOW_SIZE[0] // 13

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

# Würfelfunktion
def wuerfeln():
    return random.randint(1, 6)

# Position des Würfelquadrats
dice_square_x = 6
dice_square_y = 6

# Spielfigur-Klasse
class Spielfigur:
    def __init__(self, color, start_x, start_y):
        self.color = color
        self.x = start_x
        self.y = start_y

    def draw(self):
        pygame.draw.circle(screen, self.color, ((self.x + 0.5) * SQUARE_SIZE, (self.y + 0.5) * SQUARE_SIZE),
                           SQUARE_SIZE // 4)
        pygame.draw.circle(screen, BLACK, ((self.x + 0.5) * SQUARE_SIZE, (self.y + 0.5) * SQUARE_SIZE),
                           SQUARE_SIZE // 4, 2)


# Erstellen der Spielfiguren
spielfiguren = []
for i, farbe in enumerate(spieler_farben):
    if farbe == RED:
        spielfiguren.extend([
            Spielfigur(RED, 1, 1),
            Spielfigur(RED, 1, 2),
            Spielfigur(RED, 2, 1),
            Spielfigur(RED, 2, 2)
        ])
    elif farbe == GREEN:
        spielfiguren.extend([
            Spielfigur(GREEN, 10, 10),
            Spielfigur(GREEN, 10, 11),
            Spielfigur(GREEN, 11, 10),
            Spielfigur(GREEN, 11, 11)
        ])
    elif farbe == YELLOW:
        spielfiguren.extend([
            Spielfigur(YELLOW, 1, 10),
            Spielfigur(YELLOW, 1, 11),
            Spielfigur(YELLOW, 2, 10),
            Spielfigur(YELLOW, 2, 11)
        ])
    elif farbe == BLUE:
        spielfiguren.extend([
            Spielfigur(BLUE, 10, 1),
            Spielfigur(BLUE, 10, 2),
            Spielfigur(BLUE, 11, 1),
            Spielfigur(BLUE, 11, 2)
        ])

# Spielhauptschleife
aktueller_spieler = -1
wurf_count = 0
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_pos = pygame.mouse.get_pos()
            click_x = mouse_pos[0] // SQUARE_SIZE
            click_y = mouse_pos[1] // SQUARE_SIZE
            if click_x == dice_square_x and click_y == dice_square_y:
                dice_value = wuerfeln()
                if dice_value == 6:
                    # Setze Spielfigur des Spielers in das Startfeld
                    if spieler_farben[aktueller_spieler] == RED:
                        spielfiguren[aktueller_spieler * 4].x = 1
                        spielfiguren[aktueller_spieler * 4].y = 5
                    elif spieler_farben[aktueller_spieler] == GREEN:
                        spielfiguren[aktueller_spieler * 4].x = 11
                        spielfiguren[aktueller_spieler * 4].y = 7
                    elif spieler_farben[aktueller_spieler] == YELLOW:
                        spielfiguren[aktueller_spieler * 4].x = 5
                        spielfiguren[aktueller_spieler * 4].y = 11
                    elif spieler_farben[aktueller_spieler] == BLUE:
                        spielfiguren[aktueller_spieler * 4].x = 7
                        spielfiguren[aktueller_spieler * 4].y = 1
                else:
                    if spielfiguren[aktueller_spieler * 4].x != -1 or dice_value == 6:
                        # Vorwärtsbewegung um die gewürfelte Zahl
                        if spielfiguren[aktueller_spieler * 4].x != -1:
                            new_x = spielfiguren[aktueller_spieler * 4].x + dice_value
                            new_y = spielfiguren[aktueller_spieler * 4].y

                            if new_x > 12:
                                steps_to_goal = new_x - 12
                                if new_y == 6 and steps_to_goal <= 4:
                                    new_x = 6 + steps_to_goal
                                    new_y = aktueller_spieler * 3 + 7
                                else:
                                    new_x = spielfiguren[aktueller_spieler * 4].x
                                    new_y = spielfiguren[aktueller_spieler * 4].y
                            else:
                                if new_x == 6 and new_y >= aktueller_spieler * 3 + 7 and new_y <= aktueller_spieler * 3 + 10:
                                    new_x = spielfiguren[aktueller_spieler * 4].x
                                    new_y = spielfiguren[aktueller_spieler * 4].y

                            spielfiguren[aktueller_spieler * 4].x = new_x
                            spielfiguren[aktueller_spieler * 4].y = new_y

                    if wurf_count >= 3 or (spielfiguren[aktueller_spieler * 4].x == -1 and dice_value != 6):
                        aktueller_spieler += 1
                        if aktueller_spieler >= len(spieler_farben):
                            aktueller_spieler = 0
                        wurf_count = 0

    # Hintergrund füllen
    screen.fill(WHITE)

    # Gitter zeichnen
    for x in range(13):
        for y in range(13):
            color = (255, 255, 255)

            # Team-Bereiche farbig machen
            if 1 <= x < 3 and 1 <= y < 3:
                color = RED
            elif 10 <= x < 12 and 1 <= y < 3:
                color = BLUE
            elif 1 <= x < 3 and 10 <= y < 12:
                color = YELLOW
            elif 10 <= x < 12 and 10 <= y < 12:
                color = GREEN

            # Zielfelder farbig machen
            if 2 <= x < 6 and y == 6:
                color = RED
            elif x == 6 and 7 <= y < 11:
                color = YELLOW
            elif x == 6 and 2 <= y < 6:
                color = BLUE
            if 7 <= x < 11 and y == 6:
                color = GREEN

            # Startfelder
            if 1 <= x < 2 and y == 5:
                color = RED
            elif x == 5 and 11 <= y < 12:
                color = YELLOW
            elif x == 7 and 1 <= y < 2:
                color = BLUE
            if 11 <= x < 12 and y == 7:
                color = GREEN

            pygame.draw.rect(screen, color, pygame.Rect(x * SQUARE_SIZE, y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE))
            pygame.draw.rect(screen, BLACK, pygame.Rect(x * SQUARE_SIZE, y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE), 1)

    # Feld, in dem das Würfelergebnis angezeigt wird
    pygame.draw.rect(screen, (200, 200, 200),
                     pygame.Rect(dice_square_x * SQUARE_SIZE, dice_square_y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE))

    # Würfelrand in Spielerfarbe zeichnen
    pygame.draw.rect(screen, spieler_farben[aktueller_spieler],
                     pygame.Rect(dice_square_x * SQUARE_SIZE, dice_square_y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE), 4)

    # Würfelergebnis anzeigen, wenn vorhanden
    if 'dice_value' in locals():
        text = font.render(str(dice_value), True, BLACK)
        text_rect = text.get_rect(center=((dice_square_x + 0.5) * SQUARE_SIZE, (dice_square_y + 0.5) * SQUARE_SIZE))
        screen.blit(text, text_rect)

    # Spielfiguren zeichnen
    for spielfigur in spielfiguren:
        spielfigur.draw()

    # Anzeige aktualisieren
    pygame.display.flip()
    
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@cupo_56: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Insbesondere verteilt man das Hauptprogramm nicht zwischen Funktions- und Klassendefinitionen. Das ist extrem unübersichtlich.

`farbauswahl()` braucht noch ein paar mehr Argumente, denn wenn das Hauptprogramm in einer Funktion steckt, kann man nicht mehr einfach so magisch auf alles mögliche zugreifen. Und `Spielfigur.draw()` braucht den `screen` als Argument.

Konstanten heissen so weil sich der Wert nicht ändert. `WINDOW_SIZE` werden zwei verschiedene Werte zugewiesen. Zudem werden Konstanten auf Modulebene nach den Importen definiert, und nicht mitten im Programm.

`font` wird auch zweimal definiert. Da allerdings zweimal *gleich*. Das könnte man auch als Konstante definieren.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Man sollte sich für eine Sprache bei den Namen festlegen. Am besten Englisch, weil auch die Sprache und die Bibliotheken Englische Schlüsselworte und Namen verwenden. Bei einem Gemisch kommen so komische Sachen wie ``if color != farbe`` zustande, wo man sich fragt was den der Unterschied zwischen `color` und `farbe` ist. So Denglisch wie `wurf_count` ist auch alles andere als schön.

Das dritte Element in den `spielfarben`-Tupeln wird nie wirklich verwendet. Innerhalb von `farbauswahl()` ist das immer wenn es geprüft wird `False` und ausserhalb wird es nie geprüft. Dann könnte man aus `spielfarben` ein Wörterbuch machen.

In der Schleife in der die Spielfiguren erstellt werden, wird `i` nicht verwendet, womit das `enumerate()` dort entfallen kann.

In dieser Schleife steht viermal nahezu das gleiche, da lässt sich noch etwas herausziehen.

`aktueller_spieler` wird am Anfang auf -1 gesetzt — warum? Sonst ist das immer im Bereich 0 bis Spieleranzahl - 1. Das sollte es auch bei ersten Zug sein.

`sys.exit()` verwendet man nur, wenn mindestens potentiell ein anderer Rückgabecode als 0 verwendet wird. Hier ist das einfach nur ein Hack um irgendwie aus dem Programm zu kommen, wo man einfach ``return`` verwenden könnte um die Hauptfunktion zu beenden.

Es gibt in dem Quelltext zu viele magische Zahlen. Zum Beispiel das `aktueller_spieler` mit 4 multipliziert wird hängt ja daran, dass es vier Spieler gibt. An anderer Stelle steht keine hart kodierte 4 sondern ``len(spieler_farben)``.

Im ``else`` zum ``if dice_value == 6`` steht diese Bedingung noch mal als Teilbedingung, aber die kann ja gar nicht wahr sein, denn sonst wäre man ja nicht im ``else`` gelandet. Und in diesem ``if`` wo das geprüft wird, steht ein weiteres ``if`` wo noch mal die gleiche Bedingung steht. Das ist vielleicht alles ein bisschen unübersichtlich wenn man solche Fehler macht. ``dice_value != 6`` in einem späteren ``if`` in diesem ``else`` ist dagegen natürlich *immer* wahr.

Das da gefühlt 100 mal der Teilausdruck ``spielfiguren[aktueller_spieler * 4]`` steht, trägt auch nicht gerade zur Lesbarkeit bei.

`wurf_count` ist und bleibt immer 0. Ich vermute mal das sollte nach dem Würfeln um 1 hochgezählt werden‽

Nicht existierende Namen sind ein Fehler. Man testet nicht ob ein Name existiert, sondern sorgt dafür *das* er existiert.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import random
import sys

import pygame


COLOR_CHOICE_WINDOW_SIZE = (400, 200)
GAME_WINDOW_SIZE = (600, 600)
SQUARE_SIZE = GAME_WINDOW_SIZE[0] // 13

RED = (255, 0, 0)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)


def farbauswahl(screen, font, color_to_name):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return None

            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                rect = pygame.Rect(0, 80, 60, 60)
                for i, (farbe, name) in enumerate(color_to_name.items()):
                    rect.left = 50 + i * 80
                    if rect.collidepoint(mouse_pos):
                        return farbe

        screen.fill(WHITE)

        for i, (farbe, name) in enumerate(color_to_name.items()):
            x = 50 + i * 80
            pygame.draw.rect(screen, farbe, pygame.Rect(x, 80, 60, 60))
            text = font.render(name, True, BLACK)
            text_rect = text.get_rect(center=(x + 30, 160))
            screen.blit(text, text_rect)

        pygame.display.flip()


def wuerfeln():
    return random.randint(1, 6)


class Spielfigur:
    def __init__(self, color, x, y):
        self.color = color
        self.x = x
        self.y = y

    def draw(self, screen):
        center = ((self.x + 0.5) * SQUARE_SIZE, (self.y + 0.5) * SQUARE_SIZE)
        radius = SQUARE_SIZE // 4
        pygame.draw.circle(screen, self.color, center, radius)
        pygame.draw.circle(screen, BLACK, center, radius, 2)


def main():
    #
    # TODO Way too much code.  Refactor into functions.
    #
    pygame.init()
    try:
        screen = pygame.display.set_mode(COLOR_CHOICE_WINDOW_SIZE)
        pygame.display.set_caption("Farbauswahl")
        font = pygame.font.Font(None, 36)

        color_to_name = {
            RED: "Rot",
            GREEN: "Grün",
            YELLOW: "Gelb",
            BLUE: "Blau",
        }
        spieler_farben = []
        for _ in range(len(color_to_name)):
            farbe = farbauswahl(screen, font, color_to_name)
            if farbe is None:
                return
            del color_to_name[farbe]
            spieler_farben.append(farbe)
        assert not color_to_name

        screen = pygame.display.set_mode(GAME_WINDOW_SIZE)
        pygame.display.set_caption("Mensch Ärger Dich Nicht")

        dice_square_x = 6
        dice_square_y = 6

        spielfiguren = []
        for farbe, x, y in [
            (RED, 1, 1),
            (GREEN, 10, 10),
            (YELLOW, 1, 10),
            (BLUE, 10, 1),
        ]:
            for i in range(2):
                for j in range(2):
                    spielfiguren.append(Spielfigur(farbe, x + i, y + j))

        color_to_start_coordinates = {
            RED: (1, 5),
            GREEN: (11, 7),
            YELLOW: (5, 11),
            BLUE: (7, 1),
        }
        aktueller_spieler = 0
        dice_value = None
        wurf_count = 0
        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    return

                elif event.type == pygame.MOUSEBUTTONDOWN:
                    mouse_pos = pygame.mouse.get_pos()
                    click_x = mouse_pos[0] // SQUARE_SIZE
                    click_y = mouse_pos[1] // SQUARE_SIZE
                    if click_x == dice_square_x and click_y == dice_square_y:
                        spielfigur = spielfiguren[aktueller_spieler * 4]
                        dice_value = wuerfeln()
                        wurf_count += 1  # TODO Correct?
                        if dice_value == 6:
                            #
                            # Setze Spielfigur des Spielers in das Startfeld.
                            #
                            x, y = color_to_start_coordinates[
                                spieler_farben[aktueller_spieler]
                            ]
                            spielfigur.x = x
                            spielfigur.y = y
                        else:
                            #
                            # Vorwärtsbewegung um die gewürfelte Zahl.
                            #
                            if spielfigur.x != -1:
                                x = spielfigur.x + dice_value
                                y = spielfigur.y

                                if x > 12:
                                    steps_to_goal = x - 12
                                    if y == 6 and steps_to_goal <= 4:
                                        x = 6 + steps_to_goal
                                        y = aktueller_spieler * 3 + 7
                                    else:
                                        x = spielfigur.x
                                        y = spielfigur.y
                                else:
                                    if x == 6 and (
                                        aktueller_spieler * 3 + 7
                                        <= y
                                        <= aktueller_spieler * 3 + 10
                                    ):
                                        x = spielfigur.x
                                        y = spielfigur.y

                                spielfigur.x = x
                                spielfigur.y = y

                            if wurf_count >= 3 or spielfigur.x == -1:
                                aktueller_spieler = (
                                    aktueller_spieler + 1
                                ) % len(spieler_farben)
                                wurf_count = 0

            screen.fill(WHITE)
            #
            # Gitter zeichnen.
            #
            for x in range(13):
                for y in range(13):
                    color = WHITE
                    #
                    # Team-Bereiche farbig machen.
                    #
                    if 1 <= x < 3 and 1 <= y < 3:
                        color = RED
                    elif 10 <= x < 12 and 1 <= y < 3:
                        color = BLUE
                    elif 1 <= x < 3 and 10 <= y < 12:
                        color = YELLOW
                    elif 10 <= x < 12 and 10 <= y < 12:
                        color = GREEN
                    #
                    # Zielfelder farbig machen.
                    #
                    if 2 <= x < 6 and y == 6:
                        color = RED
                    elif x == 6 and 7 <= y < 11:
                        color = YELLOW
                    elif x == 6 and 2 <= y < 6:
                        color = BLUE
                    elif 7 <= x < 11 and y == 6:
                        color = GREEN
                    #
                    # Startfelder
                    #
                    if 1 <= x < 2 and y == 5:
                        color = RED
                    elif x == 5 and 11 <= y < 12:
                        color = YELLOW
                    elif x == 7 and 1 <= y < 2:
                        color = BLUE
                    elif 11 <= x < 12 and y == 7:
                        color = GREEN

                    rect = pygame.Rect(
                        x * SQUARE_SIZE,
                        y * SQUARE_SIZE,
                        SQUARE_SIZE,
                        SQUARE_SIZE,
                    )
                    pygame.draw.rect(screen, color, rect)
                    pygame.draw.rect(screen, BLACK, rect, 1)
            #
            # Feld, in dem das Würfelergebnis angezeigt wird.
            #
            rect = pygame.Rect(
                dice_square_x * SQUARE_SIZE,
                dice_square_y * SQUARE_SIZE,
                SQUARE_SIZE,
                SQUARE_SIZE,
            )
            pygame.draw.rect(screen, (200, 200, 200), rect)
            pygame.draw.rect(
                screen, spieler_farben[aktueller_spieler], rect, 4
            )
            #
            # Würfelergebnis anzeigen, wenn vorhanden.
            #
            if dice_value:
                text = font.render(str(dice_value), True, BLACK)
                text_rect = text.get_rect(
                    center=(
                        (dice_square_x + 0.5) * SQUARE_SIZE,
                        (dice_square_y + 0.5) * SQUARE_SIZE,
                    )
                )
                screen.blit(text, text_rect)

            for spielfigur in spielfiguren:
                spielfigur.draw(screen)

            pygame.display.flip()

    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
Das mit dem berechnen wird so nicht wirklich funktionieren. Ist IMHO auch ziemlich unübersichtlich. Auch das ``aktueller_spieler * 4`` ist selbst ohne hart kodierte 4 nicht sinnvoll, weil man damit ja nur eine/die erste Spielfigur jedes Spielers bekommt. Ich würde das ganze etwas abstrakter modellieren als Graph von Spielfeldern die den/die Wege repräsentieren. Da kann dann jedes Feld eine Koordinate haben und einen Typ/eine Farbe, also zum Beispiel normales (weisses) Feld und (farbiges) Endfeld.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten