From de3b48f7f4e5f976332ea5543d9d57571c817095 Mon Sep 17 00:00:00 2001 From: Juan Pablo Cadena Aguilar Date: Sun, 8 Oct 2023 14:10:15 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Add=20(package):=20Beha?= =?UTF-8?q?vioral=20Template=20Method=20pattern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 4 +- .../behavioral/template_method/__init__.py | 7 ++ .../behavioral/template_method/exercise.py | 105 ++++++++++++++++++ .../template_method/template_method.py | 92 +++++++++++++++ 4 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 gamma_categorization/behavioral/template_method/exercise.py create mode 100644 gamma_categorization/behavioral/template_method/template_method.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d0edb8..33c26b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_language_version: python: python3.11 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -22,7 +22,7 @@ repos: args: [ --markdown-linebreak-ext=md ] - repo: https://github.com/asottile/pyupgrade - rev: v3.14.0 + rev: v3.15.0 hooks: - id: pyupgrade args: diff --git a/gamma_categorization/behavioral/template_method/__init__.py b/gamma_categorization/behavioral/template_method/__init__.py index 2e0c00c..2b45a51 100644 --- a/gamma_categorization/behavioral/template_method/__init__.py +++ b/gamma_categorization/behavioral/template_method/__init__.py @@ -1,3 +1,10 @@ """ Template method pattern package initialization """ + +# Algorithms decomposed into parts + specifics +# Algorithm at high level is the template +# Inheritance instead composition. Override abstract members + +# Define skeleton of algorithm with concrete implementations defined in +# subclasses diff --git a/gamma_categorization/behavioral/template_method/exercise.py b/gamma_categorization/behavioral/template_method/exercise.py new file mode 100644 index 0000000..b5fe4c3 --- /dev/null +++ b/gamma_categorization/behavioral/template_method/exercise.py @@ -0,0 +1,105 @@ +""" +A module for exercise in the gamma categorization.behavioral.template + method package. +""" +from abc import ABC, abstractmethod + + +class Creature: + """ + Creature class representation + """ + + def __init__(self, attack: int, health: int): + self.initial_health: int = health # For temporary damage reset + self.health: int = health + self.attack: int = attack + + +class CardGame(ABC): + """ + Card Game representation from Abstract Base Class + """ + + def __init__(self, creatures: list[Creature]): + self.creatures: list[Creature] = creatures + + def combat(self, c1_index: int, c2_index: int) -> int: + """ + Combat in the card game + :param c1_index: The index of the first creature + :type c1_index: int + :param c2_index: The index of the second creature + :type c2_index: int + :return: The index of the living survivor creature; + -1 if both are alive + :rtype: int + """ + first: Creature = self.creatures[c1_index] + second: Creature = self.creatures[c2_index] + self.hit(first, second) + self.hit(second, first) + is_first_alive = first.health > 0 + is_second_alive = second.health > 0 + # Reset health for temporary damage games + self.reset_health(first) + self.reset_health(second) + if is_first_alive == is_second_alive: + return -1 + return c1_index if is_first_alive else c2_index + + @abstractmethod + def hit(self, attacker: Creature, defender: Creature) -> None: + """ + Hit abstract method for the creatures + :param attacker: The creature that attacks + :type attacker: Creature + :param defender: The creature that defense + :type defender: Creature + :return: None + :rtype: NoneType + """ + pass + + @abstractmethod + def reset_health(self, creature: Creature) -> None: + """ + Reset health abstract method for a creature + :param creature: The creature that will be reset the health + :type creature: Creature + :return: None + :rtype: NoneType + """ + pass + + +class TemporaryDamageCardGame(CardGame): + def hit(self, attacker: Creature, defender: Creature) -> None: + defender.health -= attacker.attack + + def reset_health(self, creature: Creature) -> None: + creature.health = creature.initial_health + + +class PermanentDamageCardGame(CardGame): + def hit(self, attacker: Creature, defender: Creature) -> None: + defender.health -= attacker.attack + + def reset_health(self, creature: Creature) -> None: + pass # No need to reset health in permanent damage game + + +# Example usage +if __name__ == "__main__": + creature1: Creature = Creature(1, 2) + creature2: Creature = Creature(1, 2) + game: CardGame = TemporaryDamageCardGame([creature1, creature2]) + print( + game.combat(0, 1) + ) # Output will be -1 because both creatures will be alive + creature1 = Creature(1, 1) + creature2 = Creature(2, 2) + game = PermanentDamageCardGame([creature1, creature2]) + print( + game.combat(0, 1) + ) # Output will be 1 because the second creature wins diff --git a/gamma_categorization/behavioral/template_method/template_method.py b/gamma_categorization/behavioral/template_method/template_method.py new file mode 100644 index 0000000..84e471b --- /dev/null +++ b/gamma_categorization/behavioral/template_method/template_method.py @@ -0,0 +1,92 @@ +""" +A module for template in the gamma categorization.behavioral.template method + package. +""" +from abc import ABC, abstractmethod + + +class Game(ABC): + """ + Game representation from Abstract Base Class + """ + + def __init__(self, number_of_players: int) -> None: + self.number_of_players: int = number_of_players + self.current_player: int = 0 + + def run(self) -> None: + """ + Run the game + :return: None + :rtype: NoneType + """ + self.start() + while not self.have_winner: + self.take_turn() + print(f'Player {self.winning_player} wins!') + + def start(self) -> None: + """ + Start the game + :return: None + :rtype: NoneType + """ + pass + + @abstractmethod + @property + def have_winner(self) -> bool: + """ + Have a winner from the game + :return: True if there's a winner; False if tied + :rtype: bool + """ + pass + + def take_turn(self) -> None: + """ + Take turns in the game + :return: None + :rtype: NoneType + """ + pass + + @abstractmethod + @property + def winning_player(self) -> int: + """ + Winning player + :return: The identifier for the winning player + :rtype: int + """ + pass + + +class Chess(Game): + def __init__(self) -> None: + super().__init__(2) + self.max_turns: int = 10 + self.turn: int = 1 + + def start(self) -> None: + print( + f'Starting a game of chess with {self.number_of_players} players.' + ) + + @property + def have_winner(self) -> bool: + return self.turn == self.max_turns + + def take_turn(self) -> None: + print(f'Turn {self.turn} taken by player {self.current_player}') + self.turn += 1 + self.current_player = 1 - self.current_player + + @property + def winning_player(self) -> int: + return self.current_player + + +if __name__ == '__main__': + chess: Chess = Chess() + chess.run()