diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..457f44d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.typeCheckingMode": "basic" +} \ No newline at end of file diff --git a/Tutorium/tut09/slides.pdf b/Tutorium/tut09/slides.pdf index f6ee115..196224e 100644 Binary files a/Tutorium/tut09/slides.pdf and b/Tutorium/tut09/slides.pdf differ diff --git a/Tutorium/tut09/src/my_collections.py b/Tutorium/tut09/src/my_collections.py index a8986c0..d7f8d31 100644 --- a/Tutorium/tut09/src/my_collections.py +++ b/Tutorium/tut09/src/my_collections.py @@ -3,27 +3,27 @@ from dataclasses import dataclass, InitVar @dataclass class MyList[T]: - internal_list: InitVar[list[T]] - length: InitVar[int] - - def __init__(self): - self.__internal_list = [] + _internal_list: InitVar[list[T]] + _length: InitVar[int] + + def __init__(self) -> None: + self.__internal_list: list[T] = [] self.__length = 0 - - def add(self, item: T): + + def add(self, item: T) -> None: self.__internal_list += [item] self.__length += 1 - @property def length(self) -> int: return self.__length + @dataclass class GameObject: - position: InitVar[tuple[int, int]] + _position: InitVar[tuple[int, int]] - def __post_init__(self, position: tuple[int, int]): + def __post_init__(self, position: tuple[int, int]) -> None: assert (0, 0) <= position self.__position = position @@ -32,16 +32,17 @@ class GameObject: return self.__position @position.setter - def position(self, position: tuple[int, int]): + def position(self, position: tuple[int, int]) -> None: if (0, 0) > position: return self.__position = position - + + if __name__ == "__main__": xs: MyList[int] = MyList() xs.add(100) assert xs.length == 1 - position = (0, 0) + position: tuple[int, int] = (0, 0) my_obj = GameObject(position) assert my_obj.position == (0, 0) try: @@ -49,7 +50,7 @@ if __name__ == "__main__": except AssertionError: pass else: - raise AssertionError(f"{my_obj} should have thrown a assertation error") + raise AssertionError( + f"{my_obj} should have thrown a assertation error") my_obj.position = (-1, 0) assert my_obj.position == (0, 0) - \ No newline at end of file diff --git a/Tutorium/tut10/README.md b/Tutorium/tut10/README.md index e69de29..d28f04d 100644 --- a/Tutorium/tut10/README.md +++ b/Tutorium/tut10/README.md @@ -0,0 +1,17 @@ +--- +marp: true +paginate: true +class: invert +# theme: uncover +footer: Tutorium 10 - 22.12.2023 - Nils Pukropp - https://s.narl.io/s/tutorium-10 +header: +--- + +# Tutorium 10 - 22.12.2023 + +Weihnachtsaufgabe :) + +--- + +# Aufgabe - SpaceArena + diff --git a/Tutorium/tut10/src/main.py b/Tutorium/tut10/src/main.py new file mode 100644 index 0000000..d643987 --- /dev/null +++ b/Tutorium/tut10/src/main.py @@ -0,0 +1,26 @@ +from result import Err, Ok, Panick, Result +from ui import run_command, Color +from spacearena import Difficulty, SpaceArena + + + +if __name__ == '__main__': + + +if __name__ == '__main__': + difficulty: Difficulty | None = None + while not difficulty: + difficulty = Difficulty.get_difficulty( + input(f"Choose your difficulty {[e.name.lower() for e in list(Difficulty)]}\n> ").upper()) + print(f"Difficulty: {difficulty} selected!") + game = SpaceArena(difficulty) + print("Starting game!") + while game.is_running: + Err("error value hehe").unwrap_or("yay still works") + match run_command(input("> "), game): + case Ok(value): + print(f"{Color.OK}{value}{Color.ENDC}") + case Err(value): + print(f"{Color.BOLD}{Color.FAIL}Error:{Color.ENDC} {Color.WARNING}{value}{Color.ENDC}") + + diff --git a/Tutorium/tut10/src/result.py b/Tutorium/tut10/src/result.py new file mode 100644 index 0000000..a013f39 --- /dev/null +++ b/Tutorium/tut10/src/result.py @@ -0,0 +1,92 @@ +from abc import ABC +from dataclasses import dataclass +from typing import Callable, overload + +class Panick(Exception): + def __init__(self, msg_res: 'Err | str') -> None: + match msg_res: + case str(msg): + super().__init__(msg) + case _: + super().__init__(f"thread `__main__` panicked:\ncalled `Result::unwrap()` on an `Err` value: \"{msg_res.value}\"") + + +@dataclass +class Result[V, E](ABC): + def unwrap(self) -> V: + match self: + case Ok(value): + return value + raise Panick(self) # type: ignore + + def expect(self, msg: str) -> V: + match self: + case Ok(value): + return value + raise Panick(msg) + + def is_ok(self) -> bool: + return isinstance(self, Ok) + + def is_err(self) -> bool: + return isinstance(self, Err) + + def unwrap_or(self, value: V) -> V: + match self: + case Ok(val): + return val + return value + + def and_[O](self, other: 'Result[O, E]') -> 'Result[O, E]': + match self, other: + case Err(value), _: + return Err(value) + return other + + def and_then[O](self, func: Callable[[V], 'Result[O, E]']) -> 'Result[O, E]': + match self: + case Ok(value): + return func(value) + return Err(self.value) # type: ignore + +@dataclass +class Ok[V, E](Result[V, E]): + value: V + +@dataclass +class Err[V, E](Result[V, E]): + value: E + +def __sqrt(num: float) -> Result[float, str]: + if num < 0: + return Err('negative sqrt') + return Ok(num ** 0.5) + +def test_result() -> None: + assert Ok(4.0).and_then(__sqrt) == Ok(2.0) + assert Ok(-5.0).and_then(__sqrt) == Err('negative sqrt') + assert Err("haha error").and_then(__sqrt) == Err("haha error") + assert Ok("test").unwrap() == "test" + try: + Err("haha error").unwrap() + except Panick: + pass + else: + assert False, 'didn\'t throw Panick' + assert Err('haha error').unwrap_or("doch nicht") == 'doch nicht' + + result = Ok('test result') + + match result: + case Ok(res): + assert res == 'test result' + case Err(): + pass + + result = Err('test result') + + match result: + case Ok(): + pass + case Err(res): + assert res == 'test result' \ No newline at end of file diff --git a/Tutorium/tut10/src/spacearena.py b/Tutorium/tut10/src/spacearena.py new file mode 100644 index 0000000..c0b5863 --- /dev/null +++ b/Tutorium/tut10/src/spacearena.py @@ -0,0 +1,44 @@ +from dataclasses import dataclass, InitVar +from enum import Enum +from typing import Optional + + +class Difficulty(Enum): + EASY = 0.5 + NORMAL = 1.0 + HARD = 2.0 + + @staticmethod + def get_difficulty(input: str) -> Optional['Difficulty']: + for e in list(Difficulty): + if e.name == input: + return e + + def __str__(self): + return self.name + + + +@dataclass +class SpaceArena: + _difficulty: InitVar[Difficulty] = Difficulty.NORMAL + + def __post_init__(self, difficulty: Difficulty = Difficulty.NORMAL) -> None: + self.__difficulty = difficulty + self.__is_running = True + + + @property + def difficulty(self) -> Difficulty: + return self.__difficulty + + @difficulty.setter + def difficulty(self, new_diff: Difficulty): + self.__difficulty = new_diff + + @property + def is_running(self) -> bool: + return self.__is_running + + def stop(self): + self.__is_running = False \ No newline at end of file diff --git a/Tutorium/tut10/src/ui.py b/Tutorium/tut10/src/ui.py new file mode 100644 index 0000000..409c47f --- /dev/null +++ b/Tutorium/tut10/src/ui.py @@ -0,0 +1,79 @@ +from abc import ABC, abstractmethod +from enum import Enum +import re +from typing import override +from result import Ok, Err, Result +from spacearena import Difficulty, SpaceArena + +class Color(Enum): + OK = '\033[92m' + FAIL = '\033[91m' + WARNING = '\033[93m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + def __str__(self) -> str: + return self.value + + +class AbstractCommand(ABC): + + def matches(self, inp: str) -> Result[re.Match[str], None]: + if m := re.match(self.pattern(), inp): + return Ok(m) + return Err(None) + + @abstractmethod + def pattern(self) -> str: + pass + + @abstractmethod + def run(self, game: SpaceArena, args: list[str]) -> Result[str, str]: + pass + + +class QuitCommand(AbstractCommand): + @override + def pattern(self) -> str: + return r"(quit)" + + @override + def run(self, game: SpaceArena, _: list[str]) -> Result[str, str]: + game.stop() + return Ok('quitting game!') + +class DifficultyCommand(AbstractCommand): + @override + def pattern(self) -> str: + return r"(difficulty|diff)\s+(increase|decrease)" + + @override + def run(self, game: SpaceArena, args: list[str]) -> Result[str, str]: + diffs = list(Difficulty) + curr = diffs.index(game.difficulty) + + match args[0]: + case 'increase' if curr + 1 < len(diffs): + game.difficulty = diffs[curr + 1] + return Ok(f"Increasing difficulty to {game.difficulty}") + case 'increase': + return Err("maximum difficulty") + case 'decrease' if curr - 1 >= 0: + game.difficulty = diffs[curr - 1] + return Ok(f"Decreasing difficulty to {game.difficulty}") + case 'decrease': + return Err("minimum difficulty") + return Err("invalid input") + +ALL_COMMANDS: list[AbstractCommand] = [QuitCommand(), DifficultyCommand()] + +def run_command(inp: str, game: SpaceArena, commands: list[AbstractCommand] = ALL_COMMANDS) -> Result[str, str]: + for command in commands: + match command.matches(inp): + case Ok(value): + args = [str(s) for s in value.groups()] + return command.run(game, args[1:]) + + + return Err("command not found") \ No newline at end of file