-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🛠️ Add (package): Behavioral Template Method pattern
- Loading branch information
Showing
4 changed files
with
206 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
105 changes: 105 additions & 0 deletions
105
gamma_categorization/behavioral/template_method/exercise.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
92 changes: 92 additions & 0 deletions
92
gamma_categorization/behavioral/template_method/template_method.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |