import pygame import sys import random import os from typing import List, Tuple # ---------------------------------------------------------- # Design / Layout # ---------------------------------------------------------- WINDOW_W, WINDOW_H = 900, 900 BG_COLOR = (10, 58, 94) # Global-Match Blau HUD_BAR_H = 88 PADDING = 28 GRID_COLS, GRID_ROWS = 4, 3 # 12 Karten (6 Paare) GAP = 18 CARD_RADIUS = 20 CARD_BORDER = (220, 226, 235) CARD_FACE = (255, 255, 255) CARD_TEXT = (22, 44, 66) BADGE_BG = (18, 122, 138) BADGE_TEXT = (255, 255, 255) HIGHLIGHT = (255, 224, 128) LOGO_FILE = "LogoSpiel.png" # <— Dein Logo (Rückseite) MISMATCH_DELAY_MS = 900 SINGLEPLAYER_SECONDS = 90 FPS = 60 # ---------------------------------------------------------- # Daten (Deutsch) # ---------------------------------------------------------- DATA = { "Europa": [ ("Deutschland", "Berlin"), ("Frankreich", "Paris"), ("Italien", "Rom"), ("Spanien", "Madrid"), ("Portugal", "Lissabon"), ("Niederlande", "Amsterdam"), ("Belgien", "Brüssel"), ("Österreich", "Wien"), ("Schweiz", "Bern"), ("Polen", "Warschau"), ("Tschechien", "Prag"), ("Ungarn", "Budapest"), ("Dänemark", "Kopenhagen"), ("Schweden", "Stockholm"), ("Norwegen", "Oslo") ], "Amerika": [ ("USA", "Washington, D.C."), ("Kanada", "Ottawa"), ("Mexiko", "Mexiko-Stadt"), ("Brasilien", "Brasília"), ("Argentinien", "Buenos Aires"), ("Chile", "Santiago"), ("Peru", "Lima"), ("Kolumbien", "Bogotá"), ("Venezuela", "Caracas"), ("Uruguay", "Montevideo"), ("Bolivien", "Sucre"), ("Ecuador", "Quito") ], "Asien": [ ("China", "Peking"), ("Japan", "Tokio"), ("Südkorea", "Seoul"), ("Indien", "Neu-Delhi"), ("Pakistan", "Islamabad"), ("Bangladesch", "Dhaka"), ("Sri Lanka", "Sri Jayewardenepura Kotte"), ("Nepal", "Kathmandu"), ("Saudi-Arabien", "Riad"), ("VAE", "Abu Dhabi"), ("Türkei", "Ankara"), ("Indonesien", "Jakarta"), ("Malaysia", "Kuala Lumpur"), ("Thailand", "Bangkok"), ("Vietnam", "Hanoi") ], } # ---------------------------------------------------------- # Helpers # ---------------------------------------------------------- def load_logo_scaled(size: Tuple[int, int]) -> pygame.Surface: """Lädt LogoSpiel.png und skaliert mit 10% Rand. Fallback: dunkles GM-Pattern.""" w, h = size surf = pygame.Surface((w, h), pygame.SRCALPHA) # weiße Karte pygame.draw.rect(surf, CARD_FACE, (0, 0, w, h), border_radius=CARD_RADIUS) pygame.draw.rect(surf, CARD_BORDER, (0, 0, w, h), width=2, border_radius=CARD_RADIUS) inner_pad = int(min(w, h) * 0.12) logo_rect = pygame.Rect(inner_pad, inner_pad, w - 2*inner_pad, h - 2*inner_pad) if os.path.exists(LOGO_FILE): try: img = pygame.image.load(LOGO_FILE).convert_alpha() img = scale_to_fit(img, logo_rect.size) surf.blit(img, ( logo_rect.centerx - img.get_width() // 2, logo_rect.centery - img.get_height() // 2 )) return surf except Exception: pass # Fallback: Text "GLOBAL MATCH" font = pygame.font.SysFont(None, int(h * 0.16), bold=True) t1 = font.render("GLOBAL", True, (10, 80, 110)) t2 = font.render("MATCH", True, (240, 180, 60)) surf.blit(t1, (w//2 - t1.get_width()//2, h//2 - t1.get_height())) surf.blit(t2, (w//2 - t2.get_width()//2, h//2 + 4)) return surf def scale_to_fit(img: pygame.Surface, target_size: Tuple[int, int]) -> pygame.Surface: tw, th = target_size iw, ih = img.get_width(), img.get_height() scale = min(tw / iw, th / ih) return pygame.transform.smoothscale(img, (int(iw * scale), int(ih * scale))) def wrap_text(text: str, font: pygame.font.Font, max_w: int) -> List[str]: words = text.split() lines, line = [], "" for w in words: t = (line + " " + w).strip() if font.size(t)[0] <= max_w: line = t else: if line: lines.append(line) line = w if line: lines.append(line) return lines def pick_payloads(level: str, n_pairs=6) -> List[dict]: pool = DATA[level][:] if level in DATA else DATA["Europa"][:] random.shuffle(pool) chosen = pool[:n_pairs] payloads = [] pid = 0 for land, cap in chosen: payloads.append({"pair": pid, "label": land, "kind": "country"}) payloads.append({"pair": pid, "label": cap, "kind": "capital"}) pid += 1 random.shuffle(payloads) return payloads # ---------------------------------------------------------- # Card # ---------------------------------------------------------- class Card: def __init__(self, rect: pygame.Rect, payload: dict, fonts, back_img: pygame.Surface): self.rect = rect self.payload = payload self.font, self.small = fonts self.back = pygame.transform.smoothscale(back_img, (rect.w, rect.h)) self.front = pygame.Surface((rect.w, rect.h), pygame.SRCALPHA) self.is_revealed = False self.is_matched = False self._render_front() def _render_front(self): w, h = self.rect.w, self.rect.h # weiße Karte + Rand pygame.draw.rect(self.front, CARD_FACE, (0, 0, w, h), border_radius=CARD_RADIUS) pygame.draw.rect(self.front, CARD_BORDER, (0, 0, w, h), width=2, border_radius=CARD_RADIUS) # Badge badge = "Land" if self.payload["kind"] == "country" else "Hauptstadt" bsurf = self.small.render(badge, True, BADGE_TEXT) bw, bh = bsurf.get_size() badge_bg = pygame.Surface((bw + 18, bh + 8), pygame.SRCALPHA) pygame.draw.rect(badge_bg, BADGE_BG, badge_bg.get_rect(), border_radius=12) badge_bg.blit(bsurf, (9, 4)) self.front.blit(badge_bg, (w - badge_bg.get_width() - 12, 12)) # Text lines = wrap_text(self.payload["label"], self.font, w - 28) total_h = len(lines) * self.font.get_height() y = h//2 - total_h//2 for line in lines: ts = self.font.render(line, True, CARD_TEXT) self.front.blit(ts, (w//2 - ts.get_width()//2, y)) y += self.font.get_height() def draw(self, screen: pygame.Surface): screen.blit(self.front if (self.is_revealed or self.is_matched) else self.back, (self.rect.x, self.rect.y)) def handle_click(self, pos) -> bool: if self.rect.collidepoint(pos) and not self.is_matched and not self.is_revealed: self.is_revealed = True return True return False # ---------------------------------------------------------- # Main Game # ---------------------------------------------------------- def run_memory(level: str = "Europa", mode: str = "single"): """ level: 'Europa' | 'Amerika' | 'Asien' mode : 'single' (Zeit) | 'two' (Punkte, abwechselnd) """ pygame.init() screen = pygame.display.set_mode((WINDOW_W, WINDOW_H)) pygame.display.set_caption(f"Global Match – {level} ({'1 Spieler' if mode=='single' else '2 Spieler'})") clock = pygame.time.Clock() ui_font = pygame.font.SysFont(None, 32, bold=True) big_font = pygame.font.SysFont(None, 50, bold=True) card_font = pygame.font.SysFont(None, 28, bold=True) small_font = pygame.font.SysFont(None, 20, bold=True) # Grid berechnen grid_w = WINDOW_W - 2 * PADDING grid_h = WINDOW_H - HUD_BAR_H - 2 * PADDING cell_w = (grid_w - (GRID_COLS - 1) * GAP) // GRID_COLS cell_h = (grid_h - (GRID_ROWS - 1) * GAP) // GRID_ROWS card_size = min(cell_w, cell_h) start_x = PADDING + (grid_w - (card_size * GRID_COLS + GAP * (GRID_COLS - 1))) // 2 start_y = HUD_BAR_H + PADDING + (grid_h - (card_size * GRID_ROWS + GAP * (GRID_ROWS - 1))) // 2 # Karten erzeugen back_img = load_logo_scaled((card_size, card_size)) payloads = pick_payloads(level, n_pairs=6) cards: List[Card] = [] k = 0 for r in range(GRID_ROWS): for c in range(GRID_COLS): if k >= len(payloads): continue rect = pygame.Rect(start_x + c * (card_size + GAP), start_y + r * (card_size + GAP), card_size, card_size) cards.append(Card(rect, payloads[k], (card_font, small_font), back_img)) k += 1 # Spielstatus revealed: List[Card] = [] lock_until = 0 total_matches = 0 need_matches = len(payloads) // 2 p_turn = 1 score = {1: 0, 2: 0} time_left = SINGLEPLAYER_SECONDS * 1000 if mode == "single" else None end_text = None # Loop while True: dt = clock.tick(FPS) now = pygame.time.get_ticks() # Events for e in pygame.event.get(): if e.type == pygame.QUIT: pygame.quit() sys.exit() if e.type == pygame.KEYDOWN and end_text: if e.key in (pygame.K_RETURN, pygame.K_SPACE): pygame.quit() return if e.type == pygame.MOUSEBUTTONDOWN and e.button == 1 and not end_text: if now < lock_until: continue for card in cards: if card.handle_click(e.pos): revealed.append(card) if len(revealed) == 2: a, b = revealed # Match (gleiches Paar, aber unterschiedliche Art) if a.payload["pair"] == b.payload["pair"] and a.payload["kind"] != b.payload["kind"]: a.is_matched = b.is_matched = True revealed.clear() total_matches += 1 if mode == "two": score[p_turn] += 1 if total_matches == need_matches: if mode == "single": end_text = "Geschafft! Alle 6 Paare." else: if score[1] > score[2]: end_text = f"Spielende – Spieler 1 gewinnt ({score[1]}:{score[2]})" elif score[2] > score[1]: end_text = f"Spielende – Spieler 2 gewinnt ({score[2]}:{score[1]})" else: end_text = f"Unentschieden ({score[1]}:{score[2]})" else: lock_until = now + MISMATCH_DELAY_MS elif len(revealed) > 2: # Sicherheitsreset – nur die letzten 2 behalten for old in revealed[:-2]: old.is_revealed = False revealed = revealed[-2:] # Timer (Singleplayer) if not end_text and mode == "single" and time_left is not None: time_left -= dt if time_left <= 0: time_left = 0 end_text = "Zeit abgelaufen!" # Mismatch zurückdrehen + Spielerwechsel if not end_text and len(revealed) == 2 and lock_until and now >= lock_until: a, b = revealed a.is_revealed = False b.is_revealed = False revealed.clear() lock_until = 0 if mode == "two": p_turn = 2 if p_turn == 1 else 1 # ----------------- Render ----------------- screen.fill(BG_COLOR) # HUD pygame.draw.rect(screen, (7, 42, 70), (0, 0, WINDOW_W, HUD_BAR_H)) title = ui_font.render(f"Global Match – {level}", True, (255, 255, 255)) screen.blit(title, (PADDING, HUD_BAR_H//2 - title.get_height()//2)) if mode == "single": tsec = (time_left // 1000) if time_left is not None else 0 timer = ui_font.render(f"Zeit: {tsec}s", True, HIGHLIGHT) screen.blit(timer, (WINDOW_W - PADDING - timer.get_width(), HUD_BAR_H//2 - timer.get_height()//2)) else: s = ui_font.render(f"P1: {score[1]} P2: {score[2]} Zug: P{p_turn}", True, HIGHLIGHT) screen.blit(s, (WINDOW_W - PADDING - s.get_width(), HUD_BAR_H//2 - s.get_height()//2)) # Karten for card in cards: card.draw(screen) # Overlay / Ende if end_text: overlay = pygame.Surface((WINDOW_W, WINDOW_H), pygame.SRCALPHA) overlay.fill((0, 0, 0, 140)) screen.blit(overlay, (0, 0)) msg = big_font.render(end_text, True, (255, 255, 255)) sub = ui_font.render("ENTER/SPACE zum Beenden", True, (240, 240, 240)) screen.blit(msg, (WINDOW_W//2 - msg.get_width()//2, WINDOW_H//2 - 30)) screen.blit(sub, (WINDOW_W//2 - sub.get_width()//2, WINDOW_H//2 + 20)) pygame.display.flip() # ---------------------------------------------------------- # Direktstart (zu Testzwecken) # ---------------------------------------------------------- if __name__ == "__main__": lvl = "Europa" md = "single" if len(sys.argv) >= 2: lvl = sys.argv[1] if len(sys.argv) >= 3: md = sys.argv[2] run_memory(level=lvl, mode=md)