diff --git a/Hausaufgaben/apollo/.env b/Hausaufgaben/apollo/.env new file mode 100644 index 0000000..c8249df --- /dev/null +++ b/Hausaufgaben/apollo/.env @@ -0,0 +1,20 @@ +# Anzeige +WIDTH=800 +HEIGHT=600 +FPS=60 +BG_COLOR=#0a0a14 + +# 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/apollo/1. Apollo.py b/Hausaufgaben/apollo/1. Apollo.py new file mode 100644 index 0000000..705ca83 --- /dev/null +++ b/Hausaufgaben/apollo/1. Apollo.py @@ -0,0 +1,42 @@ +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: + 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) + 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() + + #created by chat + + diff --git a/Hausaufgaben/apollo/compute.py b/Hausaufgaben/apollo/compute.py new file mode 100644 index 0000000..f0e8dcb --- /dev/null +++ b/Hausaufgaben/apollo/compute.py @@ -0,0 +1,69 @@ +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 + + +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/apollo/config.py b/Hausaufgaben/apollo/config.py new file mode 100644 index 0000000..635364c --- /dev/null +++ b/Hausaufgaben/apollo/config.py @@ -0,0 +1,101 @@ +"""Konfigurations-Loader für das Moon/Apollo-Projekt. +""" + +from __future__ import annotations + +import os +import re +from dataclasses import dataclass +from typing import Tuple + +from dotenv import load_dotenv + +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/apollo/game.py b/Hausaufgaben/apollo/game.py new file mode 100644 index 0000000..bcaccdc --- /dev/null +++ b/Hausaufgaben/apollo/game.py @@ -0,0 +1,31 @@ +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/apollo/moon.py b/Hausaufgaben/apollo/moon.py new file mode 100644 index 0000000..4be72ee --- /dev/null +++ b/Hausaufgaben/apollo/moon.py @@ -0,0 +1,64 @@ +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) +w + 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()