From c8a3244f5e6052ec69b22ffb30735c66d008ae50 Mon Sep 17 00:00:00 2001 From: chris Date: Tue, 9 Dec 2025 14:20:13 +0100 Subject: [PATCH] =?UTF-8?q?ha4=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Hausaufgaben/Ha4/game.py | 32 +++++++++ Hausaufgaben/ha2/.env | 20 ++++++ Hausaufgaben/ha2/1. Apollo.py | 41 +++++++++++ Hausaufgaben/ha2/compute.py | 125 ++++++++++++++++++++++++++++++++++ Hausaufgaben/ha2/config.py | 105 ++++++++++++++++++++++++++++ Hausaufgaben/ha2/game.py | 32 +++++++++ Hausaufgaben/ha2/moon.py | 65 ++++++++++++++++++ 7 files changed, 420 insertions(+) create mode 100644 Hausaufgaben/Ha4/game.py create mode 100644 Hausaufgaben/ha2/.env create mode 100644 Hausaufgaben/ha2/1. Apollo.py create mode 100644 Hausaufgaben/ha2/compute.py create mode 100644 Hausaufgaben/ha2/config.py create mode 100644 Hausaufgaben/ha2/game.py create mode 100644 Hausaufgaben/ha2/moon.py diff --git a/Hausaufgaben/Ha4/game.py b/Hausaufgaben/Ha4/game.py new file mode 100644 index 0000000..5ff21f3 --- /dev/null +++ b/Hausaufgaben/Ha4/game.py @@ -0,0 +1,32 @@ +import pygame + +class Game: + def __init__(self, width=800, height=600, fps=60, title="Game"): + pygame.init() + self.width = width + self.height = height + self.fps = fps + self.title = title + self.screen = pygame.display.set_mode((width, height)) + pygame.display.set_caption(title) + self.clock = pygame.time.Clock() + self.running = True + + def run(self): + """Startet die Hauptschleife.""" + while self.running: + dt = self.clock.tick(self.fps) / 1000 # Zeit seit letztem Frame + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.running = False + self.update(dt) + self.draw(self.screen) + pygame.display.flip() + pygame.quit() + + + def update(self, dt: float): + pass + + def draw(self, surface: pygame.Surface): + pass diff --git a/Hausaufgaben/ha2/.env b/Hausaufgaben/ha2/.env new file mode 100644 index 0000000..4a07e0e --- /dev/null +++ b/Hausaufgaben/ha2/.env @@ -0,0 +1,20 @@ +# Anzeige +WIDTH=800 +HEIGHT=600 +FPS=60 +BG_COLOR=#0a0a13 + +# Erde +EARTH_COLOR=#2878ff +EARTH_RADIUS=30 + +# Mond +MOON_COLOR=#f0f0f0 +MOON_RADIUS=10 +MOON_ORBIT_RADIUS=160 +MOON_ANGULAR_SPEED_DEG=45 # Grad/Sekunde + +# Apollo (optional, nur falls apollo.py genutzt wird) +APOLLO_COLOR=#dc3232 +APOLLO_RADIUS=7 +APOLLO_ORBIT_RADIUS=40 diff --git a/Hausaufgaben/ha2/1. Apollo.py b/Hausaufgaben/ha2/1. Apollo.py new file mode 100644 index 0000000..106b8f8 --- /dev/null +++ b/Hausaufgaben/ha2/1. Apollo.py @@ -0,0 +1,41 @@ +from __future__ import annotations +import math +from typing import Tuple, List +import pygame # type: ignore + +from moon import Moon +import compute + + +class Apollo(Moon): + """Fügt die Apollo-Kapsel hinzu; liest alle Werte aus Settings (env).""" + + def get_apollo_angle(self) -> float: + # doppelte Winkelgeschwindigkeit, entgegengesetzte Richtung + return -2.0 * self.get_moon_angle() + + def get_apollo_position(self) -> Tuple[int, int]: + theta = self.get_apollo_angle() + r = float(self.settings.apollo_orbit_radius) + rot: List[List[float]] = [ + [math.cos(theta), -math.sin(theta)], + [math.sin(theta), math.cos(theta)], + ] + vec: List[List[float]] = [[r], [0.0]] + res = compute.matmul(rot, vec) + mx, my = self.get_moon_position() + return int(mx + res[0][0]), int(my + res[1][0]) + + def draw(self, surface: pygame.Surface) -> None: + super().draw(surface) + # kleine Orbit-Linie um den Mond (optional) + pygame.draw.circle( + surface, (90, 50, 50), self.get_moon_position(), self.settings.apollo_orbit_radius, width=1 + ) + ax, ay = self.get_apollo_position() + pygame.draw.circle(surface, self.settings.apollo_color, (ax, ay), self.settings.apollo_radius) + + +if __name__ == "__main__": + Apollo().run() + diff --git a/Hausaufgaben/ha2/compute.py b/Hausaufgaben/ha2/compute.py new file mode 100644 index 0000000..17a7f85 --- /dev/null +++ b/Hausaufgaben/ha2/compute.py @@ -0,0 +1,125 @@ +from typing import List + +def matmul(A: List[List[float]], B: List[List[float]]) -> List[List[float]]: + """ + Multiplies two matrices A and B (nested Python lists). + + Requirements: + - A has shape (m x n), B has shape (n x p). + - All rows in A and B must have equal length. + - Elements are numeric (float/int). + + Args: + A: Left matrix, list of rows (m x n). + B: Right matrix, list of rows (n x p). + + Returns: + New matrix C = A * B with shape (m x p). + + Raises: + ValueError: If matrices are empty, ragged, + or shapes are incompatible. + + """ + if not A or not B: + raise ValueError("Empty matrices are not supported.") + if not A[0] or not B[0]: + raise ValueError("Matrices must have at least one column.") + + a_cols = len(A[0]) + + for row in A: + if len(row) != a_cols: + raise ValueError("Left matrix has inconsistent row lengths.") + b_cols = len(B[0]) + for row in B: + if len(row) != b_cols: + raise ValueError("Right matrix has inconsistent row lengths.") + + if a_cols != len(B): + raise ValueError( + f"Incompatible shapes: A is {len(A)}x{a_cols}, " + f"B is {len(B)}x{b_cols}; need cols(A) == rows(B)." + ) + + m, n, p = len(A), a_cols, b_cols + C: List[List[float]] = [[0 for _ in range(p)] for _ in range(m)] + + for i in range(m): + for k in range(n): + a_ik = A[i][k] + for j in range(p): + C[i][j] += a_ik * B[k][j] + + return C + +from math import cos, sin +from typing import List + + +def rot_3D(angle: float, axis: str) -> List[List[float]]: + """ + Berechnet die 3x3-Rotationsmatrix für eine Rotation im 3D-Raum. + + Die Rotationsmatrizen lauten: + + Rotation um die x-Achse: + Rx(θ) = [[1, 0, 0], + [0, cos θ, -sin θ], + [0, sin θ, cos θ]] + + Rotation um die y-Achse: + Ry(θ) = [[ cos θ, 0, sin θ], + [ 0, 1, 0], + [-sin θ, 0, cos θ]] + + Rotation um die z-Achse: + Rz(θ) = [[cos θ, -sin θ, 0], + [sin θ, cos θ, 0], + [ 0, 0, 1]] + + :param angle: Rotationswinkel in Radiant. + :param axis: Rotationsachse: 'x', 'y' oder 'z' (Groß-/Kleinschreibung egal). + :return: 3x3-Rotationsmatrix als verschachtelte Liste. + + :raises ValueError: Wenn axis nicht 'x', 'y' oder 'z' ist. + """ + a = axis.lower() + c = cos(angle) + s = sin(angle) + + if a == "x": + return [ + [1.0, 0.0, 0.0], + [0.0, c, -s ], + [0.0, s, c ], + ] + if a == "y": + return [ + [c, 0.0, s ], + [0.0, 1.0, 0.0], + [-s, 0.0, c ], + ] + if a == "z": + return [ + [c, -s, 0.0], + [s, c, 0.0], + [0.0, 0.0, 1.0], + ] + + raise ValueError("axis must be 'x', 'y' or 'z'") + + +if __name__ == "__main__": + matrix_a = [[3, 4, -1, 4], + [-2, 2, 5, 1]] + matrix_b = [[1, 3, -2], + [2, 5, 1], + [-1, 4, -4], + [2, 3, 6]] + matrix_c = matmul(matrix_a, matrix_b) + print("Ergebnis C = A * B:") + for row in matrix_c: + print(row) + + diff --git a/Hausaufgaben/ha2/config.py b/Hausaufgaben/ha2/config.py new file mode 100644 index 0000000..839cf52 --- /dev/null +++ b/Hausaufgaben/ha2/config.py @@ -0,0 +1,105 @@ +"""Konfigurations-Loader für das Moon/Apollo-Projekt. + +Lädt Werte aus .env in Umgebungsvariablen und stellt sie typisiert bereit. +Verwendet python-dotenv. Enthält Validierung & sinnvolle Defaults. +""" + +from __future__ import annotations + +import os +import re +from dataclasses import dataclass +from typing import Tuple + +from dotenv import load_dotenv + +# .env → os.environ laden +load_dotenv(override=False) + + +def _get_env(name: str, default: str) -> str: + val = os.getenv(name) + return val if val is not None and val != "" else default + + +def _parse_int(name: str, default: int, min_val: int | None = None) -> int: + raw = _get_env(name, str(default)) + try: + v = int(raw) + if min_val is not None and v < min_val: + raise ValueError + return v + except Exception as _: + return default + + +def _parse_float(name: str, default: float, min_val: float | None = None) -> float: + raw = _get_env(name, str(default)) + try: + v = float(raw) + if min_val is not None and v < min_val: + raise ValueError + return v + except Exception as _: + return default + + +_HEX = re.compile(r"^#?([0-9a-fA-F]{6})$") + + +def _parse_rgb(name: str, default_hex: str) -> Tuple[int, int, int]: + """Erlaubt '#rrggbb' oder 'rrggbb'. Fällt auf default zurück, wenn ungültig.""" + raw = _get_env(name, default_hex) + m = _HEX.match(raw) + hx = m.group(1) if m else default_hex.lstrip("#") + r = int(hx[0:2], 16) + g = int(hx[2:4], 16) + b = int(hx[4:6], 16) + return (r, g, b) + + +@dataclass(frozen=True) +class Settings: + # Anzeige + width: int + height: int + fps: int + bg_color: Tuple[int, int, int] + + # Erde + earth_color: Tuple[int, int, int] + earth_radius: int + + # Mond + moon_color: Tuple[int, int, int] + moon_radius: int + moon_orbit_radius: int + moon_angular_speed_deg: float # Grad/Sekunde + + # Apollo (optional) + apollo_color: Tuple[int, int, int] + apollo_radius: int + apollo_orbit_radius: int + + +def get_settings() -> Settings: + """Erzeugt Settings aus Umgebungsvariablen oder Defaults.""" + return Settings( + # Anzeige + width=_parse_int("WIDTH", 800, min_val=100), + height=_parse_int("HEIGHT", 600, min_val=100), + fps=_parse_int("FPS", 60, min_val=1), + bg_color=_parse_rgb("BG_COLOR", "#0a0a14"), + # Erde + earth_color=_parse_rgb("EARTH_COLOR", "#2878ff"), + earth_radius=_parse_int("EARTH_RADIUS", 30, min_val=1), + # Mond + moon_color=_parse_rgb("MOON_COLOR", "#f0f0f0"), + moon_radius=_parse_int("MOON_RADIUS", 10, min_val=1), + moon_orbit_radius=_parse_int("MOON_ORBIT_RADIUS", 160, min_val=10), + moon_angular_speed_deg=_parse_float("MOON_ANGULAR_SPEED_DEG", 45.0, min_val=0.1), + # Apollo + apollo_color=_parse_rgb("APOLLO_COLOR", "#dc3232"), + apollo_radius=_parse_int("APOLLO_RADIUS", 7, min_val=1), + apollo_orbit_radius=_parse_int("APOLLO_ORBIT_RADIUS", 40, min_val=5), + ) diff --git a/Hausaufgaben/ha2/game.py b/Hausaufgaben/ha2/game.py new file mode 100644 index 0000000..5ff21f3 --- /dev/null +++ b/Hausaufgaben/ha2/game.py @@ -0,0 +1,32 @@ +import pygame + +class Game: + def __init__(self, width=800, height=600, fps=60, title="Game"): + pygame.init() + self.width = width + self.height = height + self.fps = fps + self.title = title + self.screen = pygame.display.set_mode((width, height)) + pygame.display.set_caption(title) + self.clock = pygame.time.Clock() + self.running = True + + def run(self): + """Startet die Hauptschleife.""" + while self.running: + dt = self.clock.tick(self.fps) / 1000 # Zeit seit letztem Frame + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.running = False + self.update(dt) + self.draw(self.screen) + pygame.display.flip() + pygame.quit() + + + def update(self, dt: float): + pass + + def draw(self, surface: pygame.Surface): + pass diff --git a/Hausaufgaben/ha2/moon.py b/Hausaufgaben/ha2/moon.py new file mode 100644 index 0000000..df7bf85 --- /dev/null +++ b/Hausaufgaben/ha2/moon.py @@ -0,0 +1,65 @@ +from __future__ import annotations +import math +from typing import Tuple, List +import pygame # type: ignore + +from game import Game +import compute +from config import get_settings + + +class Moon(Game): + """Unterklasse von Game: Animation Mond-um-Erde mit .env-Konfiguration.""" + + def __init__(self) -> None: + self.settings = get_settings() + super().__init__( + width=self.settings.width, + height=self.settings.height, + fps=self.settings.fps, + title="Moon Orbit (env-configured)", + ) + self._theta: float = 0.0 + self._center: Tuple[float, float] = (self.width / 2, self.height / 2) + + # Hooks + def get_earth_center(self) -> Tuple[float, float]: + return self._center + + def get_moon_angle(self) -> float: + return self._theta + + def get_moon_orbit_radius(self) -> int: + return self.settings.moon_orbit_radius + + def get_moon_position(self) -> Tuple[int, int]: + theta = self.get_moon_angle() + r = float(self.get_moon_orbit_radius()) + rot: List[List[float]] = [ + [math.cos(theta), -math.sin(theta)], + [math.sin(theta), math.cos(theta)], + ] + vec: List[List[float]] = [[r], [0.0]] + res = compute.matmul(rot, vec) + cx, cy = self.get_earth_center() + return int(cx + res[0][0]), int(cy + res[1][0]) + + def update(self, dt: float) -> None: + omega = math.radians(self.settings.moon_angular_speed_deg) + self._theta = (self._theta + omega * dt) % (2.0 * math.pi) + + def draw(self, surface: pygame.Surface) -> None: + s = self.settings + surface.fill(s.bg_color) + + ex, ey = self.get_earth_center() + pygame.draw.circle(surface, s.earth_color, (int(ex), int(ey)), s.earth_radius) + + pygame.draw.circle(surface, (60, 60, 80), (int(ex), int(ey)), s.moon_orbit_radius, width=1) + + mx, my = self.get_moon_position() + pygame.draw.circle(surface, s.moon_color, (mx, my), s.moon_radius) + + +if __name__ == "__main__": + Moon().run()