Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 53 additions & 48 deletions ciphers/baconian_cipher.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
"""
Program to encode and decode Baconian or Bacon's Cipher
Wikipedia reference : https://en.wikipedia.org/wiki/Bacon%27s_cipher
"""

encode_dict = {
"a": "AAAAA",
"b": "AAAAB",
Expand Down Expand Up @@ -33,57 +28,67 @@
" ": " ",
}


decode_dict = {value: key for key, value in encode_dict.items()}


def encode(word: str) -> str:
"""
Encodes to Baconian cipher

>>> encode("hello")
'AABBBAABAAABABAABABAABBAB'
>>> encode("hello world")
'AABBBAABAAABABAABABAABBAB BABAAABBABBAAAAABABAAAABB'
>>> encode("hello world!")
Traceback (most recent call last):
...
Exception: encode() accepts only letters of the alphabet and spaces
"""
def encode(word: str, symbols=("A","B")) -> str:
a_sym, b_sym = symbols
encoded = ""
for letter in word.lower():
if letter.isalpha() or letter == " ":
encoded += encode_dict[letter]
# replace A with a_symbol, B with b_symbol
bacon = encode_dict[letter]
bacon_custom = bacon.replace("A", a_sym).replace("B", b_sym)
encoded += bacon_custom
else:
raise Exception("encode() accepts only letters of the alphabet and spaces")
return encoded


def decode(coded: str) -> str:
"""
Decodes from Baconian cipher

>>> decode("AABBBAABAAABABAABABAABBAB BABAAABBABBAAAAABABAAAABB")
'hello world'
>>> decode("AABBBAABAAABABAABABAABBAB")
'hello'
>>> decode("AABBBAABAAABABAABABAABBAB BABAAABBABBAAAAABABAAAABB!")
Traceback (most recent call last):
...
Exception: decode() accepts only 'A', 'B' and spaces
"""
if set(coded) - {"A", "B", " "} != set():
raise Exception("decode() accepts only 'A', 'B' and spaces")
decoded = ""
for word in coded.split():
while len(word) != 0:
decoded += decode_dict[word[:5]]
word = word[5:]
decoded += " "
return decoded.strip()

def decode(coded: str, symbols=("A","B")) -> str:
sym1, sym2 = symbols
# check if we need to remap
unique_symbols = set(coded.replace(" ", ""))
if unique_symbols - {sym1, sym2} != set():
raise Exception(f"decode() accepts only symbols {sym1} and {sym2} and spaces")

Check failure on line 51 in ciphers/baconian_cipher.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (EM102)

ciphers/baconian_cipher.py:51:25: EM102 Exception must not use an f-string literal, assign to variable first

Check failure on line 52 in ciphers/baconian_cipher.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

ciphers/baconian_cipher.py:52:1: W293 Blank line contains whitespace
# Try both mappings: symbol1 maps to A, symbol2 maps to B or symbol1 maps to B, symbol2 maps A

Check failure on line 53 in ciphers/baconian_cipher.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

ciphers/baconian_cipher.py:53:89: E501 Line too long (98 > 88)
candidates = []
for mapping in [(sym1, sym2), (sym2, sym1)]:
s1, s2 = mapping
# convert coded symbols to standard A/B
standard = coded.replace(s1, "A").replace(s2, "B")
try:
decoded = ""
for word in standard.split():
while len(word) != 0:
chunk = word[:5]
if chunk not in decode_dict:
raise ValueError
decoded += decode_dict[chunk]
word = word[5:]
decoded += " "
candidates.append(decoded.strip())
except ValueError:
candidates.append(None)

Check failure on line 72 in ciphers/baconian_cipher.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (W293)

ciphers/baconian_cipher.py:72:1: W293 Blank line contains whitespace
# return the valid decoding
for candidate in candidates:
if candidate is not None:
return candidate
raise Exception("No valid decoding found with the given symbols")
def detect_unique_char (cipher):
#CD CD DC returns C, D
cipher = cipher.replace(" ","")
unique_chars = set(cipher)
unique_letters = [char for char in unique_chars if char.isalpha()]
if len(unique_letters) != 2:
raise Exception("Cipher must contain exactly two unique alphabetic characters for encoding.")

Check failure on line 84 in ciphers/baconian_cipher.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

ciphers/baconian_cipher.py:84:89: E501 Line too long (101 > 88)
else:
list_unique = list(unique_letters)
return list_unique[0], list_unique[1]

if __name__ == "__main__":
from doctest import testmod

testmod()
# Example usage
cipher_text = "FEEFE EEFFF EEFEE EFFFF FEEFF EFEEE EEEFE EFEEF EEEEF FEEEE EFFEF FEFEE EFFEE EEFEF EFFEF FEFEF"

Check failure on line 91 in ciphers/baconian_cipher.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

ciphers/baconian_cipher.py:91:89: E501 Line too long (115 > 88)
symbol_1, symbol_2 = detect_unique_char(cipher_text)
decoded = decode(cipher_text, symbols=(symbol_1, symbol_2))
print(decoded) # prints the quick brown fox
Loading