Wizards, Thieves and Knights

“Wizards, Thieves and Knights” (WTK) game is a “Paper, Rock and Scissors” clone but in a fantasy setting. It comes with a simple command line interface where the use must type in his or her choice. The enemy is controlled by the script. The player’s goal is to gain as many score points, as it possible.

Code organization

Use separate modules to maintain your code base. For example:

/
|-- engine.py
|-- exceptions.py
|-- models.py
|-- settings.py

General playground description

The game process is divided into rounds. Each round consists of attack and defence stages. Rounds are repeated, until player is defeated.

Fight rules

It’s simple…

  • Wizard beats Knight

  • Thief beats Wizard

  • Knight beats Thief

Attack stage

Player selects the choice to attack from wizard, thief or knight, enemy selects the choice for defence from the same options by random. If the attack is successful:

  • enemy health is decreased

  • player gains score points

In case enemy is defeated:

  • a new enemy instance is initialized using higher level

  • player gains some extra score points

  • next defence stage is skipped, and player attacks again

Defence stage

Player selects the choice to defend from wizard, thief or knight, enemy selects the choice to attack from the same options by random. If the attack is successful:

  • player health is decreased

If player is defeated:

  • report the message about gained score points to the terminal

  • write down player’s name and score points to “scores.txt” file

Exceptions

Enemy down

This is an exceptional scenario when enemy is defeated. A custom exception EnemyDown should be used to track these cases. Exception should provide the details on the enemy’s instance, especially its level.

class wtk.exceptions.EnemyDown(model: _AbstractModel)

Raised when an enemy is defeated

Game over

This is an exceptional scenario when player is defeated. A custom exception GameOver should be used to track these cases. Exception should provide the details on the player’s instance, especially its score points.

class wtk.exceptions.GameOver(model: _AbstractModel)

Raised when a player is defeated

Models

Enemy

class wtk.models.Enemy(level: int = 1)

Enemy model

Variables:
  • level – enemy’s level value

  • health – enemy’s instance health points

Represents the playing enemy-bot.

__init__(level: int = 1) None

Initialize instance

Parameters:

level (int) – an enemy’s level indicator

Health value is equal to the level value.

decrease_health() None

Decrease health points

Raise:

EnemyDown

This method decreases the health meter value. When it comes to be less than 1 (one) an EnemyDown exception is raised.

select_attack() FightChoice

Return a random fight choice

Returns:

a fight choice

Choices made by an enemy are random.

select_defence() FightChoice

Return a random fight choice

Returns:

a fight choice

Choices made by an enemy are random.

You are free to implement other methods you like, if needed.

Player

class wtk.models.Player(name: str)

Player model

Variables:
  • name – player’s name

  • health – player’s instance health points

  • score – player’s instance gained score points

This model is controlled by the player.

__init__(name: str) None

Initialize instance

Parameters:

name (str) – a player’s name

This method performs player instance initialization. It set instance name, initial score points value and health.

attack(enemy: Enemy) None

Attack an enemy

Perform attack on an enemy instance. This method takes an enemy instance as an argument. After that, it takes attack choice from the player model and the defence choice from an enemy model. After fight result calculation required operation are to be performed (decrease enemy health, assign score points etc.). Based on fight result should print out a message:

  • “YOUR ATTACK IS SUCCESSFUL!”

  • “YOUR ATTACK IS FAILED!”

  • “IT’S A DRAW!”

decrease_health() None

Decrease health points

Raise:

GameOver

This method decreases the health meter value. When it comes to be less than 1 (one) an GameOver exception is raised.

defence(enemy: Enemy) None

Defend from an enemy’s attack

Perform defence from an enemy attack. This method takes an enemy instance as an argument. After that, it takes defence choice from the player model and the attack choice from an enemy model. After fight result calculation required operation are to be performed (decrease player health). Based on fight result should print out a message:

  • “YOUR DEFENCE IS SUCCESSFUL!”

  • “YOUR DEFENCE IS FAILED!”

  • “IT’S A DRAW!”

static fight(attack: FightChoice, defence: FightChoice) FightResult

Fight result calculation interface

The method calculates the fight result based on the game rules:

  • wizard beats knight

  • thief beats wizard

  • knight beats thief

select_attack() FightChoice

Return fight choice from the user’s prompt

Returns:

a fight choice

The player is asked to make their decision for the upcoming fight. The chosen value is validated and if it is invalid the question is repeated.

select_defence() FightChoice

Return fight choice from the user’s prompt

Returns:

a fight choice

The player is asked to make their decision for the upcoming fight. The chosen value is validated and if it is invalid the question is repeated.

Settings

Settings module contains constants values for the game.

For example,

wtk.settings.INITIAL_PLAYER_HEALTH: int

Initial player health value

wtk.settings.INITIAL_ENEMY_LEVEL: int

Initial enemy level value

wtk.settings.SCORE_SUCCESS_ATTACK: int

Score points value to assign when player’s attack is successful

wtk.settings.SCORE_ENEMY_DOWN: int

Score points value to assign when enemy is defeated

You may also define messages with this module, for example:

wtk.settings.MSG_SUCCESS_ATTACK: str = 'YOUR ATTACK IS SUCCESSFUL!'

Successful attack message

wtk.settings.MSG_SUCCESS_DEFENCE: str = 'YOUR DEFENCE IS SUCCESSFUL!'

Successful defence message

wtk.settings.MSG_FAILURE_ATTACK: str = 'YOUR ATTACK IS FAILED!'

Failed attack message

wtk.settings.MSG_FAILURE_DEFENCE: str = 'YOUR DEFENCE HAS BEEN BREACHED!'

Failed defence message

wtk.settings.MSG_DRAW: str = "IT'S A DRAW!"

Draw fight message

Engine

Engine module should provide two functions:

  • get_player_name

  • play

Player name getter

Asks the user to type in his or her name and return it back. Leading and trailing whitespaces are to be trimmed. Name should contain at least one character.

wtk.engine.get_player_name() str

Return a player’s name from the user prompt

Returns:

a player defined name

A validation process is performed as well. The player name cannot be an empty string.

Play

This function initializes player and enemy instance. It processes game rounds inside of an endless loop stage by stage. If an enemy is defeated - a new one should be initialized with level increased by 1 (one). This case should be reported to the terminal. If a player is defeated - the “Game Over” message should be reported to the terminal. KeyboardInterrupt should be handled as well - it’s behavior is similar to “Game Over” event, but “game over” message should be omitted.

wtk.engine.play() None

Run the game

The function initializes player and enemy instances. After that it runs the game process in an endless loop. Once the player is defeated - it stops the execution.

Optional Enhancements

  1. Add scores processor to show top-10 scores from a record table.

  2. Create game menu, for example:

    AVAILABLE MENU CHOICES: PLAY, SCORES, EXIT
    TYPE YOUR CHOICE HERE:
    
  3. Store score table to the database instead of using text file.