The Algorithms logo
The Algorithms
alphabet = {

def generate_table(key: str) -> list[tuple[str, str]]:
    >>> generate_table('marvin')  # doctest: +NORMALIZE_WHITESPACE
    return [alphabet[char] for char in key.upper()]

def encrypt(key: str, words: str) -> str:
    >>> encrypt('marvin', 'jessica')
    cipher = ""
    count = 0
    table = generate_table(key)
    for char in words.upper():
        cipher += get_opponent(table[count], char)
        count = (count + 1) % len(table)
    return cipher

def decrypt(key: str, words: str) -> str:
    >>> decrypt('marvin', 'QRACRWU')
    return encrypt(key, words)

def get_position(table: tuple[str, str], char: str) -> tuple[int, int]:
    >>> get_position(generate_table('marvin')[0], 'M')
    (0, 12)
    # `char` is either in the 0th row or the 1st row
    row = 0 if char in table[0] else 1
    col = table[row].index(char)
    return row, col

def get_opponent(table: tuple[str, str], char: str) -> str:
    >>> get_opponent(generate_table('marvin')[0], 'M')
    row, col = get_position(table, char.upper())
    if row == 1:
        return table[0][col]
        return table[1][col] if row == 0 else char

if __name__ == "__main__":
    import doctest

    doctest.testmod()  # Fist ensure that all our tests are passing...

    Enter key: marvin
    Enter text to encrypt: jessica
    Encrypted: QRACRWU
    Decrypted with key: JESSICA
    key = input("Enter key: ").strip()
    text = input("Enter text to encrypt: ").strip()
    cipher_text = encrypt(key, text)

    print(f"Encrypted: {cipher_text}")
    print(f"Decrypted with key: {decrypt(key, cipher_text)}")

Porta Cipher