commit 02daaa94701b70adb0420b376b55e484750263ec Author: Theresa Menke Date: Thu Oct 15 15:26:07 2026 +0200 Erster Stand: compute.py diff --git a/To_the_moon/.gitignore b/To_the_moon/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/To_the_moon/AGENTS.md b/To_the_moon/AGENTS.md new file mode 100644 index 0000000..79a2eba --- /dev/null +++ b/To_the_moon/AGENTS.md @@ -0,0 +1,4 @@ +# Regeln für dieses Projekt +- Code nach PEP 8, Zeilenlänge maximal 88 Zeichen. +- Jede Funktion und Methode hat einen Docstring und Type Hints. +- Vor jedem Commit: `ruff check` und `ruff format`. \ No newline at end of file diff --git a/To_the_moon/compute.py b/To_the_moon/compute.py new file mode 100644 index 0000000..12a2144 --- /dev/null +++ b/To_the_moon/compute.py @@ -0,0 +1,106 @@ +""" +Mathematical matrix multiplication, transposition, and rotation without external libraries. + +The ``matmul`` function implements a straightforward algorithm that +multiplies two 2‑D matrices where each element is a ``float``. + +The functions perform validity checks on the dimensions: +- Matrices must be non-empty lists of lists and rectangular. + +""" + +from __future__ import annotations +import math + + +def matmul(a: list[list[float]], b: list[list[float]]) -> list[list[float]]: + """Multiply two 2‑D matrices.""" + if not a or not a[0]: + raise ValueError("Matrix 'a' must not be empty") + if not b or not b[0]: + raise ValueError("Matrix 'b' must not be empty") + + a_rows = len(a) + a_cols = len(a[0]) + for row in a: + if len(row) != a_cols: + raise ValueError("Matrix 'a' rows must all have the same length") + + b_rows = len(b) + b_cols = len(b[0]) + for row in b: + if len(row) != b_cols: + raise ValueError("Matrix 'b' rows must all have the same length") + + if a_cols != b_rows: + raise ValueError( + f"Cannot multiply: number of columns in 'a' ({a_cols}) " + f"does not equal number of rows in 'b' ({b_rows})" + ) + + result: list[list[float]] = [] + for i in range(a_rows): + result_row: list[float] = [] + for j in range(b_cols): + element = 0.0 + for k in range(a_cols): + element += a[i][k] * b[k][j] + result_row.append(element) + result.append(result_row) + + return result + + +def transpose(a: list[list[float]]) -> list[list[float]]: + """Transposes a 2‑D matrix.""" + if not a or not a[0]: + raise ValueError("Matrix must not be empty") + + a_rows = len(a) + a_cols = len(a[0]) + for row in a: + if len(row) != a_cols: + raise ValueError("Matrix rows must all have the same length") + + result: list[list[float]] = [] + for j in range(a_cols): + result_row: list[float] = [] + for i in range(a_rows): + result_row.append(a[i][j]) + result.append(result_row) + + return result + + +def rot_2D(theta: float) -> list[list[float]]: + """Erzeugt eine 2D-Rotationsmatrix für den Winkel theta (im Bogenmaß).""" + cos_t = math.cos(theta) + sin_t = math.sin(theta) + return [ + [cos_t, -sin_t], + [sin_t, cos_t] + ] + + +if __name__ == "__main__": + # Valid example: multiplication + matrix_a = [[1, 2, 3], [4, 5, 6]] + matrix_b = [[7, 8], [9, 10], [11, 12]] + print("Valid multiplication result:") + print(matmul(matrix_a, matrix_b)) + + # Valid example: transposition + print("\nTransposed matrix_a result:") + print(transpose(matrix_a)) + + # Valid example: rotation (2D) + print("\nRotated 2D matrix (theta = 0.5) result:") + print(rot_2D(0.5)) + + # Example that raises ValueError + try: + invalid_b = [[1, 2], [3, 4]] + print("\nThis should raise an error:") + print(matmul(matrix_a, invalid_b)) + except ValueError as e: + print("Caught ValueError as expected:", e) \ No newline at end of file diff --git a/To_the_moon/game.py b/To_the_moon/game.py new file mode 100644 index 0000000..e88c245 --- /dev/null +++ b/To_the_moon/game.py @@ -0,0 +1,98 @@ +"""Basisklasse ``Game`` für einfache pygame-Anwendungen.""" + +import pygame + + +class Game: + """Basisklasse für pygame-Spiele. + + Unterklassen passen das Verhalten durch Überschreiben von + :meth:`handle_event`, :meth:`update_game` und :meth:`draw_game` an. + """ + + def __init__( + self, + title: str, + fps: int = 60, + size: tuple[int, int] = (640, 400), + ) -> None: + """Legt Fenstertitel, Bildrate und Fenstergröße fest. + + Args: + title: Titel des Fensters. + fps: Maximale Bilder pro Sekunde. + size: Fenstergröße als (Breite, Höhe) in Pixeln. + """ + self.title: str = title + self.fps: int = fps + self.size: tuple[int, int] = size + self.clock: pygame.time.Clock = pygame.time.Clock() + self.dt: float = 0 + self.screen: pygame.Surface | None = None + + def init_game(self) -> None: + """Initialisiert pygame und erzeugt das Fenster.""" + pygame.init() + pygame.display.set_caption(self.title) + self.screen = pygame.display.set_mode(self.size) + + def game_loop(self) -> None: + """Führt die Hauptschleife aus, bis Events oder Update ``False`` liefern.""" + while True: + # Berechnung der Zeitdifferenz seit dem letzten Frame + self.dt = self.clock.tick(self.fps) / 1000 + if self.event_handling() == False: + break + if self.update_game() == False: + break + self.draw_game() + + def exit_game(self) -> None: + """Beendet pygame.""" + pygame.quit() + + def event_handling(self) -> bool: + """Verteilt alle anstehenden Events an :meth:`handle_event`. + + Bleibt in der Unterklasse unverändert. + + Returns: + ``False``, sobald ein Event das Spiel beenden soll, sonst ``True``. + """ + for event in pygame.event.get(): + if not self.handle_event(event): + return False + return True + + def handle_event(self, event: pygame.event.Event) -> bool: + """Verarbeitet ein einzelnes Event. + + Wird in der Unterklasse überschrieben. + + Args: + event: Das zu verarbeitende pygame-Event. + + Returns: + ``False`` bei ``pygame.QUIT``, sonst ``True``. + """ + if event.type == pygame.QUIT: + return False + return True + + def update_game(self) -> bool: + """Aktualisiert den Spielzustand um einen Frame. + + Returns: + ``False``, wenn das Spiel beendet werden soll, sonst ``True``. + """ + return True + + def draw_game(self) -> None: + """Zeichnet den aktuellen Frame und bringt ihn zur Anzeige.""" + pygame.display.flip() + + def run(self) -> None: + """Startet das Spiel: Initialisierung, Hauptschleife, Beenden.""" + self.init_game() + self.game_loop() + self.exit_game() \ No newline at end of file diff --git a/To_the_moon/moon.py b/To_the_moon/moon.py new file mode 100644 index 0000000..9e40be8 --- /dev/null +++ b/To_the_moon/moon.py @@ -0,0 +1,64 @@ +import pygame + +from compute import matmul, rot_2D, transpose +from game import Game + +EARTH_RADIUS = 30 +MOON_RADIUS = 10 +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +BLUE = (0, 0, 255) + + +class Moon(Game): + """Der Mond kreist um die Erde.""" + + def __init__( + self, + title: str = "Moon", + fps: int = 60, + size: tuple[int, int] = (640, 400), + ) -> None: + """Legt Startposition, Drehwinkel und Rotationsmatrix fest.""" + super().__init__(title, fps, size) + + # Startposition des Mondes relativ zum Mittelpunkt (z. B. 120 Pixel nach rechts) + self.position_moon = (120.0, 0.0) + + # Kleiner Drehwinkel im Bogenmaß und Erstellung der Rotationsmatrix + theta = 0.01 + self.rot_moon = rot_2D(theta) + + def update_game(self) -> bool: + """Dreht den Mond ein Stück weiter.""" + # Mondposition mit der Rotationsmatrix weiterdrehen + point = transpose([list(self.position_moon)]) # [x, y] wird zur Spalte [[x], [y]] + result = matmul(self.rot_moon, point) # Ergebnis: [[x'], [y']] + self.position_moon = (result[0][0], result[1][0]) + return True + + def draw_game(self) -> None: + """Zeichnet Erde und Mond.""" + # Bildschirm löschen + self.screen.fill(BLACK) + + # Mittelpunkt des Fensters ermitteln + center_x = self.screen.get_width() // 2 + center_y = self.screen.get_height() // 2 + + # Erde als blauen Kreis in der Mitte zeichnen + pygame.draw.circle(self.screen, BLUE, (center_x, center_y), EARTH_RADIUS) + + # Mond als weißen Kreis auf der Kreisbahn zeichnen + moon_x = int(center_x + self.position_moon[0]) + moon_y = int(center_y + self.position_moon[1]) + pygame.draw.circle(self.screen, WHITE, (moon_x, moon_y), MOON_RADIUS) + + # Bild anzeigen + pygame.display.flip() + + +if __name__ == "__main__": + # Moon-Objekt erzeugen und starten + game = Moon() + game.run() \ No newline at end of file diff --git a/To_the_moon/pyproject.toml b/To_the_moon/pyproject.toml new file mode 100644 index 0000000..46e3b47 --- /dev/null +++ b/To_the_moon/pyproject.toml @@ -0,0 +1,3 @@ +[project] +name = "matrixmania" +version = "0.1.0" \ No newline at end of file