global_match_memory/True_Finished_Game.py
2025-12-03 16:08:36 +01:00

449 lines
17 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pygame
import random
import sys
import os
import time
from pythonosc import dispatcher, osc_server
import threading
# ===== OSC Setup =====
touch_x, touch_y = None, None
clap_trigger = False
def osc_touch(address, x, y):
"""Wird aufgerufen, wenn /touch x y über OSC empfangen wird."""
global touch_x, touch_y
touch_x, touch_y = x, y
def osc_clap(address, *args):
"""Wird aufgerufen, wenn /clap empfangen wird."""
global clap_trigger
clap_trigger = True
def start_osc_server():
disp = dispatcher.Dispatcher()
disp.map("/touch", osc_touch)
disp.map("/clap", osc_clap)
# Dein gesture_input_osc sendet an 127.0.0.1:5005 → hier auch 5005
server = osc_server.ThreadingOSCUDPServer(("127.0.0.1", 5005), disp)
print(" OSC server läuft auf Port 5005")
server.serve_forever()
# Starte den OSC Listener im Hintergrund
threading.Thread(target=start_osc_server, daemon=True).start()
# -------------------------------
# Global Match CountryCapital Memory Game
# -------------------------------
# --- Colors & Layout ---
CARD_FRONT_COLOR = (245, 246, 248)
CARD_BACK_COLOR = (100, 100, 200)
MATCH_COLOR = (160, 220, 160)
TEXT_COLOR = (10, 30, 40)
CAPITAL_COLOR = (0, 60, 180)
BG_COLOR = (10, 42, 83)
BUTTON_FILL = (18, 122, 138)
BUTTON_BORDER = (255, 255, 255)
FPS = 30
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600
# --- Background / Logo ---
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
BACKGROUND_FILE = os.path.join(BASE_DIR, "GlobalHintergrund.png")
def build_card_back(image_path, size):
"""Load and scale a logo image to fill card backs."""
w, h = size
surf = pygame.Surface((w, h), pygame.SRCALPHA).convert_alpha()
if os.path.exists(image_path):
try:
img = pygame.image.load(image_path).convert_alpha()
img = pygame.transform.smoothscale(img, (w, h))
surf.blit(img, (0, 0))
except pygame.error as e:
print(f" Error loading {image_path}: {e}")
surf.fill((127, 127, 200))
else:
print(f"Image not found: {image_path}")
surf.fill((127, 127, 200))
return surf
class MemoryGame:
def __init__(self):
# Game Data
self.deck = []
self.pair_map = {}
self.matched = []
self.revealed = []
self.cards = []
self.card_rects = []
self.selected = []
self.scores = [0, 0]
self.current_player = 0
self.found_pairs = 0
self.total_pairs = 0
# UI and States
self.font = None
self.small_font = None
self.buttons = []
self.running = True
self.state = "mode"
self.selected_continents = []
self.level = None
self.pair_count = 6
self.player_mode = 2
# "Bestätigungsphase" = wir warten auf Klatschen
self.awaiting_confirmation = False
self.confirmation_start_time = None
self.confirmation_time_limit = 5 # Sekunden
# Visuals
self.card_back = None
# -------------------------------
# File Loading
# -------------------------------
def load_cards(self, filename):
if not os.path.exists(filename):
print(f" File not found: {filename}")
return []
pairs = []
with open(filename, "r", encoding="utf-8") as f:
for line in f:
parts = line.strip().split()
if len(parts) >= 2:
pairs.append((parts[0], parts[1]))
return pairs
def prepare_deck(self):
self.deck = []
for continent in self.selected_continents:
base = continent
if self.level == "Easy":
self.deck += self.load_cards(base + "-major.txt")
elif self.level == "Normal":
self.deck += self.load_cards(base + "-major.txt")
self.deck += self.load_cards(base + "-Minor.txt")
elif self.level == "Hard":
self.deck += self.load_cards(base + "-major.txt")
self.deck += self.load_cards(base + "-Minor.txt")
self.deck += self.load_cards(base + "-Dependent.txt")
if not self.deck:
print("No cards loaded. Check your text files.")
sys.exit()
random.shuffle(self.deck)
self.deck = self.deck[:self.pair_count]
# -------------------------------
# Setup & Layout
# -------------------------------
def setup_game(self):
self.cards = []
self.pair_map = {}
for country, capital in self.deck:
self.cards.append({"text": country, "type": "country"})
self.cards.append({"text": capital, "type": "capital"})
self.pair_map[country] = capital
self.pair_map[capital] = country
random.shuffle(self.cards)
self.matched = [False] * len(self.cards)
self.revealed = [False] * len(self.cards)
self.total_pairs = len(self.deck)
self.selected = []
self.found_pairs = 0
self.current_player = 0
self.scores = [0, 0]
self.awaiting_confirmation = False
self.confirmation_start_time = None
cols = 4
rows = (len(self.cards) + cols - 1) // cols
margin = 10
card_width = (SCREEN_WIDTH - (cols + 1) * margin) // cols
card_height = (SCREEN_HEIGHT - (rows + 1) * margin - 100) // rows
self.card_back = build_card_back(BACKGROUND_FILE, (card_width, card_height))
y_offset = 80
self.card_rects = []
for i in range(len(self.cards)):
col = i % cols
row = i // cols
x = margin + col * (card_width + margin)
y = y_offset + margin + row * (card_height + margin)
self.card_rects.append(pygame.Rect(x, y, card_width, card_height))
# -------------------------------
# Drawing
# -------------------------------
def draw_menu(self, screen, title, options):
screen.fill(BG_COLOR)
title_text = self.font.render(title, True, (255, 255, 255))
screen.blit(title_text, (SCREEN_WIDTH // 2 - title_text.get_width() // 2, 100))
self.buttons = []
for i, option in enumerate(options):
rect = pygame.Rect(SCREEN_WIDTH // 2 - 150, 200 + i * 70, 300, 50)
pygame.draw.rect(screen, BUTTON_FILL, rect, border_radius=10)
pygame.draw.rect(screen, BUTTON_BORDER, rect, 2, border_radius=10)
text = self.font.render(option, True, (255, 255, 255))
screen.blit(text, text.get_rect(center=rect.center))
self.buttons.append((rect, option))
pygame.display.flip()
def draw_game(self, screen):
screen.fill(BG_COLOR)
if self.player_mode == 2:
title = self.font.render(f"Player {self.current_player + 1}'s turn", True, (255, 255, 255))
score_text = self.font.render(f"Scores: P1={self.scores[0]} P2={self.scores[1]}", True, (220, 230, 235))
else:
title = self.font.render("Single Player Mode", True, (255, 255, 255))
score_text = self.font.render(f"Score: {self.scores[0]}", True, (220, 230, 235))
screen.blit(title, (20, 20))
screen.blit(score_text, (20, 50))
# Hinweis: wenn 2 Karten offen sind, warte auf Clap
if self.awaiting_confirmation and len(self.selected) == 2:
hint = self.small_font.render("👏 Klatschen, um das Paar zu bestätigen!", True, (255, 255, 0))
screen.blit(hint, (SCREEN_WIDTH // 2 - hint.get_width() // 2, 80))
for i, rect in enumerate(self.card_rects):
if self.matched[i]:
pygame.draw.rect(screen, MATCH_COLOR, rect)
elif self.revealed[i]:
pygame.draw.rect(screen, CARD_FRONT_COLOR, rect)
else:
screen.blit(self.card_back, rect.topleft)
pygame.draw.rect(screen, (0, 0, 0), rect, 2)
if self.revealed[i] or self.matched[i]:
card = self.cards[i]
text_color = CAPITAL_COLOR if card["type"] == "capital" else TEXT_COLOR
text = self.font.render(card["text"], True, text_color)
screen.blit(text, text.get_rect(center=(rect.centerx, rect.centery - 10)))
label = self.small_font.render(
"(Capital)" if card["type"] == "capital" else "(Country)", True, (80, 80, 80)
)
screen.blit(label, label.get_rect(center=(rect.centerx, rect.centery + 20)))
pygame.display.flip()
# -------------------------------
# Interaction Logic
# -------------------------------
def handle_click(self, pos):
# ✅ Blockiere Touch über dem Spielfeld (Menü-Bereich oben)
if self.state == "game" and pos[1] < 80:
return
# Menüs: mit Touch durchklicken
if self.state in ["mode", "continent", "americas", "difficulty", "pairs", "timer"]:
for rect, option in self.buttons:
if rect.collidepoint(pos):
if self.state == "mode":
self.player_mode = 1 if option == "1 Player" else 2
self.state = "continent"
elif self.state == "continent":
if option == "Americas":
self.state = "americas"
elif option == "All Continents":
self.selected_continents = ["Europe", "Asia", "Africa",
"Oceania", "North-America", "South-America"]
self.state = "difficulty"
else:
self.selected_continents = [option]
self.state = "difficulty"
elif self.state == "americas":
if option == "North-America":
self.selected_continents = ["North-America"]
elif option == "South-America":
self.selected_continents = ["South-America"]
elif option == "Americas":
self.selected_continents = ["North-America", "South-America"]
self.state = "difficulty"
elif self.state == "difficulty":
self.level = option
self.state = "pairs"
elif self.state == "pairs":
self.pair_count = int(option)
self.state = "timer"
elif self.state == "timer":
self.confirmation_time_limit = int(option.replace("s", ""))
self.prepare_deck()
self.setup_game()
self.state = "game"
return
# Spiel: Karten nur anklicken, wenn wir NICHT gerade auf Clap warten
elif self.state == "game":
if self.awaiting_confirmation or len(self.selected) >= 2:
# Während wir auf Klatschen warten, keine weiteren Karten öffnen
return
for i, rect in enumerate(self.card_rects):
if rect.collidepoint(pos) and not self.revealed[i] and not self.matched[i]:
self.revealed[i] = True
self.selected.append(i)
return
# -------------------------------
# OSC Input Processing
# -------------------------------
def process_osc_input(self):
"""Verarbeite die aktuellen OSC-Eingaben (Touch & Clap)."""
global touch_x, touch_y, clap_trigger
# TOUCH: als Klick ins Spiel (Menü oder Karte)
if touch_x is not None and touch_y is not None:
pos = (int(touch_x), int(touch_y))
self.handle_click(pos)
touch_x, touch_y = None, None # Reset
# CLAP: wenn 2 Karten offen und wir warten → Paar auswerten
if clap_trigger:
if self.awaiting_confirmation and len(self.selected) == 2:
self.resolve_pair()
clap_trigger = False
# -------------------------------
# Paar auswerten (nach Clap oder Timeout)
# -------------------------------
def resolve_pair(self):
"""Prüft das aktuelle Kartenpaar und aktualisiert Punkte / Spieler."""
if len(self.selected) != 2:
return
a, b = self.selected
text_a = self.cards[a]["text"]
text_b = self.cards[b]["text"]
is_match = self.pair_map.get(text_a) == text_b
if is_match:
self.matched[a] = self.matched[b] = True
self.scores[self.current_player] += 1
self.found_pairs += 1
else:
# Falsches Paar: Karten wieder umdrehen, Punkt abziehen
self.revealed[a] = self.revealed[b] = False
self.scores[self.current_player] -= 1
# Reset für nächste Runde
self.selected = []
self.awaiting_confirmation = False
self.confirmation_start_time = None
# Spielerwechsel immer nach einem Paar (wie vorher)
if self.player_mode == 2:
self.current_player = 1 - self.current_player
# -------------------------------
# Game Logic
# -------------------------------
def check_selected(self):
if self.state != "game":
return
# Wenn zwei Karten offen sind → Warte auf Clap
if len(self.selected) == 2 and not self.awaiting_confirmation:
self.awaiting_confirmation = True
self.confirmation_start_time = time.time()
# Timeout: wenn zu lange kein Clap → Karten zurückdrehen, Spielerwechsel
if self.awaiting_confirmation and self.confirmation_start_time is not None:
if time.time() - self.confirmation_start_time > self.confirmation_time_limit:
if len(self.selected) == 2:
a, b = self.selected
self.revealed[a] = self.revealed[b] = False
self.selected = []
self.awaiting_confirmation = False
self.confirmation_start_time = None
if self.player_mode == 2:
self.current_player = 1 - self.current_player
# -------------------------------
# Winner Screen
# -------------------------------
def display_winner(self, screen):
if self.player_mode == 1:
text = f" Final Score: {self.scores[0]}"
else:
if self.scores[0] > self.scores[1]:
text = " Player 1 Wins!"
elif self.scores[1] > self.scores[0]:
text = " Player 2 Wins!"
else:
text = " Draw!"
win_text = self.font.render(text, True, (255, 255, 0))
rect = win_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
screen.blit(win_text, rect)
pygame.display.flip()
# -------------------------------
# Main Loop
# -------------------------------
def run(self):
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Global Match CountryCapital Memory Game")
clock = pygame.time.Clock()
self.font = pygame.font.SysFont(None, 32)
self.small_font = pygame.font.SysFont(None, 22)
while self.running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
# Maus komplett ignorieren (kein Klick mehr)
# elif event.type == pygame.MOUSEBUTTONDOWN:
# self.handle_click(event.pos)
# Wenn du zum Debuggen Maus willst, obige Zeilen entkommentieren.
# HIER werden jetzt *jeden Frame* die OSC-Eingaben verarbeitet
self.process_osc_input()
# Menü + Spiel zeichnen
if self.state == "mode":
self.draw_menu(screen, "Select Player Mode", ["1 Player", "2 Players"])
elif self.state == "continent":
self.draw_menu(screen, "Select Continent",
["Europe", "Americas", "Asia", "Africa", "Oceania", "All Continents"])
elif self.state == "americas":
self.draw_menu(screen, "Select Region", ["North-America", "South-America", "Americas"])
elif self.state == "difficulty":
self.draw_menu(screen, "Select Difficulty", ["Easy", "Normal", "Hard"])
elif self.state == "pairs":
self.draw_menu(screen, "Select Number of Pairs", ["4", "6", "8", "10", "12"])
elif self.state == "timer":
self.draw_menu(screen, "Select Confirmation Time", ["3s", "5s", "8s", "10s"])
elif self.state == "game":
self.draw_game(screen)
self.check_selected()
if self.found_pairs == self.total_pairs:
self.display_winner(screen)
pygame.time.wait(4000)
self.running = False
clock.tick(FPS)
pygame.quit()
sys.exit()
# -------------------------------
# Run
# -------------------------------
if __name__ == "__main__":
game = MemoryGame()
game.run()