Trafid Cipher

# https://en.wikipedia.org/wiki/Trifid_cipher
from __future__ import annotations


def __encryptPart(messagePart: str, character2Number: dict[str, str]) -> str:
    one, two, three = "", "", ""
    tmp = []

    for character in messagePart:
        tmp.append(character2Number[character])

    for each in tmp:
        one += each[0]
        two += each[1]
        three += each[2]

    return one + two + three


def __decryptPart(
    messagePart: str, character2Number: dict[str, str]
) -> tuple[str, str, str]:
    tmp, thisPart = "", ""
    result = []

    for character in messagePart:
        thisPart += character2Number[character]

    for digit in thisPart:
        tmp += digit
        if len(tmp) == len(messagePart):
            result.append(tmp)
            tmp = ""

    return result[0], result[1], result[2]


def __prepare(
    message: str, alphabet: str
) -> tuple[str, str, dict[str, str], dict[str, str]]:
    # Validate message and alphabet, set to upper and remove spaces
    alphabet = alphabet.replace(" ", "").upper()
    message = message.replace(" ", "").upper()

    # Check length and characters
    if len(alphabet) != 27:
        raise KeyError("Length of alphabet has to be 27.")
    for each in message:
        if each not in alphabet:
            raise ValueError("Each message character has to be included in alphabet!")

    # Generate dictionares
    numbers = (
        "111",
        "112",
        "113",
        "121",
        "122",
        "123",
        "131",
        "132",
        "133",
        "211",
        "212",
        "213",
        "221",
        "222",
        "223",
        "231",
        "232",
        "233",
        "311",
        "312",
        "313",
        "321",
        "322",
        "323",
        "331",
        "332",
        "333",
    )
    character2Number = {}
    number2Character = {}
    for letter, number in zip(alphabet, numbers):
        character2Number[letter] = number
        number2Character[number] = letter

    return message, alphabet, character2Number, number2Character


def encryptMessage(
    message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5
) -> str:
    message, alphabet, character2Number, number2Character = __prepare(message, alphabet)
    encrypted, encrypted_numeric = "", ""

    for i in range(0, len(message) + 1, period):
        encrypted_numeric += __encryptPart(message[i : i + period], character2Number)

    for i in range(0, len(encrypted_numeric), 3):
        encrypted += number2Character[encrypted_numeric[i : i + 3]]

    return encrypted


def decryptMessage(
    message: str, alphabet: str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period: int = 5
) -> str:
    message, alphabet, character2Number, number2Character = __prepare(message, alphabet)
    decrypted_numeric = []
    decrypted = ""

    for i in range(0, len(message) + 1, period):
        a, b, c = __decryptPart(message[i : i + period], character2Number)

        for j in range(0, len(a)):
            decrypted_numeric.append(a[j] + b[j] + c[j])

    for each in decrypted_numeric:
        decrypted += number2Character[each]

    return decrypted


if __name__ == "__main__":
    msg = "DEFEND THE EAST WALL OF THE CASTLE."
    encrypted = encryptMessage(msg, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ")
    decrypted = decryptMessage(encrypted, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ")
    print(f"Encrypted: {encrypted}\nDecrypted: {decrypted}")
Algerlogo

Β© Alger 2022

About us

We are a group of programmers helping each other build new things, whether it be writing complex encryption programs, or simple ciphers. Our goal is to work together to document and model beautiful, helpful and interesting algorithms using code. We are an open-source community - anyone can contribute. We check each other's work, communicate and collaborate to solve problems. We strive to be welcoming, respectful, yet make sure that our code follows the latest programming guidelines.