Card 08/ 09
Practice: Finding the Template Method in a Game Loop
A base Game class with play(), and chess and draughts subclasses.
A game loop is a natural Template Method, and this exercise asks you to find every part of the pattern in one. Read the listing, then answer the questions before opening the solution.
from abc import ABC, abstractmethod
class Game(ABC):
def play(self) -> None:
self.set_up()
turn = 0
while not self.is_over(turn):
self.before_turn(turn)
self.take_turn(turn)
turn += 1
print(f"{type(self).__name__}: {self.winner(turn)} wins after {turn} turns")
@abstractmethod
def set_up(self) -> None: ...
@abstractmethod
def take_turn(self, turn: int) -> None: ...
@abstractmethod
def is_over(self, turn: int) -> bool: ...
def before_turn(self, turn: int) -> None:
pass
def winner(self, turn: int) -> str:
return "player 1" if turn % 2 else "player 2"
class Chess(Game):
def set_up(self) -> None:
print("Chess: 32 pieces placed")
def take_turn(self, turn: int) -> None:
print(f" move {turn + 1}")
def is_over(self, turn: int) -> bool:
return turn == 4
class Draughts(Game):
def set_up(self) -> None:
print("Draughts: 24 pieces placed")
def before_turn(self, turn: int) -> None:
print(f" checking captures before move {turn + 1}")
def take_turn(self, turn: int) -> None:
print(f" move {turn + 1}")
def is_over(self, turn: int) -> bool:
return turn == 3
Chess().play()
Draughts().play()- Which method is the template method?
- Which methods are abstract steps, and which are hooks?
- Which class overrides a hook, and what does it add?
- What does each game print?
Show a solution
| Method | Role | Why |
|---|---|---|
play | Template method | It fixes the order: set up, then turns until the game is over, then the winner |
set_up, take_turn, is_over | Abstract steps | Marked @abstractmethod, so every game must write them |
before_turn | Hook | It has a default that does nothing |
winner | Hook | It has a default, and no game here overrides it |
from abc import ABC, abstractmethod
class Game(ABC):
def play(self) -> None:
self.set_up()
turn = 0
while not self.is_over(turn):
self.before_turn(turn)
self.take_turn(turn)
turn += 1
print(f"{type(self).__name__}: {self.winner(turn)} wins after {turn} turns")
@abstractmethod
def set_up(self) -> None: ...
@abstractmethod
def take_turn(self, turn: int) -> None: ...
@abstractmethod
def is_over(self, turn: int) -> bool: ...
def before_turn(self, turn: int) -> None:
pass
def winner(self, turn: int) -> str:
return "player 1" if turn % 2 else "player 2"
class Chess(Game):
def set_up(self) -> None:
print("Chess: 32 pieces placed")
def take_turn(self, turn: int) -> None:
print(f" move {turn + 1}")
def is_over(self, turn: int) -> bool:
return turn == 4
class Draughts(Game):
def set_up(self) -> None:
print("Draughts: 24 pieces placed")
def before_turn(self, turn: int) -> None:
print(f" checking captures before move {turn + 1}")
def take_turn(self, turn: int) -> None:
print(f" move {turn + 1}")
def is_over(self, turn: int) -> bool:
return turn == 3
Chess().play()
Draughts().play()OutputRan with python:3.12-slim (Python 3.12.13)
Chess: 32 pieces placed
move 1
move 2
move 3
move 4
Chess: player 2 wins after 4 turns
Draughts: 24 pieces placed
checking captures before move 1
move 1
checking captures before move 2
move 2
checking captures before move 3
move 3
Draughts: player 1 wins after 3 turnsDraughts overrides the before_turn hook to check for captures before each move, and Chess leaves it alone. Chess ends after four turns and draughts after three, and both use the default winner. Neither game contains the loop: it is written once, in play.
← → move through sections and cards