I am implementing a game in python, which will give the user a sample encryption, and explain each step from plaintext to cipher. The task for the player is to decrypt a word by undoing each step shown above on the new word.
For this, I have created an abstract algorithm which requires an encrypt function, and a current development status. In the future I think it will require other properties such as explanation text or a difficulty level.
The abstract algorithm looks like this:
from abc import ABC, abstractmethod from enum import Enum class Status(Enum): """The current development status""" in_development = 0 under_testing = 1 production_ready = 2 known_bugs = 3 class BaseAlgorithm(ABC): """This is the base on which all other algs are to be built""" @abstractmethod def encrypt(self, word): """Encrypt the word using an internal alg""" pass @abstractmethod def decrypt(self, word): """Decrypt the word using an internal alg If this is not overloaded, it is assumed that""" return self.encrypt(word) @property @abstractmethod def status(self): """The current progress and readiness of the alg""" pass
An example algorithm is implemented in rotate.py
from . import BaseAlgorithm, Status class RotateAlg(BaseAlgorithm): """Takes a rotation amount which it uses to encrypt or decrypt all words pass into it""" def __init__(self, offset): try: self.offset = int(offset) except ValueError: raise ValueError("the offset must be an integer") from None # Decides which direction is the default way to rotate self.rotate_left = True def encrypt(self, word): """Encrypts by rotating the word""" return self.rotate(word) def decrypt(self, word): """Decrypts by rotating the word back""" return self.rotate(word, inverse=True) def rotate(self, word, inverse=False): """Rotates the word to the left""" offset = self.offset # Inverse and rotate_left both flip the direction # If both are the same the direction remains unchanged if inverse == self.rotate_left: offset *= -1 return rotate(word, offset) @property def status(self): return Status.in_development def rotate(word, offset): """Rotates the word right by the amount specified""" offset %= len(word) return word[offset:] + word[:offset]
Finally how these algorithms would be composed together is a simple accumulating loop
def decode(cipher, encryptions): for encryption in reversed(encryptions): cipher = encryption.decrypt(cipher) return cipher
I feel like this is the wrong approach to take though, as there is a lot of boilerplate for what is essentially a function which is used in two ways. Is there a better design pattern to use in this scenario? I suspect a more functional approach would suit, such as passing the encryption functions into a class that binds them together. My concern is I lose the option of providing metadata with the function with that style.