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.