Skip to content

Commit

Permalink
Cryptogram solver
Browse files Browse the repository at this point in the history
  • Loading branch information
za3k committed Oct 25, 2020
1 parent 67189b1 commit 691e3c6
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ create-github-repo
---
Usage: `create-github-repo NAME DESCRIPTION`

cryptogram.py
---
Usage: `cryptogram.py "MIH WAIHXS TFBFL OF WHLRLTWFS QAFY WIKFIYF CLFECW MIH QTCA LFWRFJC, MIH WAIHXS FVRFJC TC."`

Interactive cryptogram solver.

decolorize
---
Strips ANSI color codes out of a stream
Expand Down
152 changes: 152 additions & 0 deletions cryptogram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#!/usr/bin/env python3
letters = "abcdefghijklmnopqrstuvwxyz"
english_frequency = "etaoinsrhldcumfpgwybvkxjqz"
UNASSIGNED = "?"
assignment = { x: UNASSIGNED for x in letters }
ciphertext = ""

def sort_by_frequency(x):
import collections
return [value for value,count in collections.Counter(x).most_common()]

def print_clear():
print("\u001b[2J\u001b[H")
def print_color(x, color="gray"):
COLORCODE = {
"black": "\u001b[30m",
"gray": "\u001b[30;1m", # "bright black"
"grey": "\u001b[30;1m",
"red": "\u001b[31m",
"green": "\u001b[32m",
"yellow": "\u001b[33m",
"blue": "\u001b[34m",
"magenta":"\u001b[35m",
"cyan": "\u001b[36m",
"white": "\u001b[37m",
}
print(COLORCODE[color], end='')
print(x, end='')
print("\u001b[0m", end='') # reset
def print_newline():
print()

def display(active_cipher=None):
print_clear()
print_color("substitution", "white")
print_newline()
for x in letters:
if x == active_cipher:
print_color(x, "blue")
elif assignment[x] == UNASSIGNED:
print_color(x)
else:
print_color(x, "green")
print_newline()
for x in letters:
if x == active_cipher:
print_color(x, "blue")
elif assignment[x] == UNASSIGNED:
print_color("?")
else:
print_color(assignment[x], "green")
print_newline()
print_newline()

print_color("frequency", "white")
print_newline()
ciphertext_frequency = "".join(sort_by_frequency([x for x in ciphertext.lower() if x in letters]))
for x in ciphertext_frequency:
if x == active_cipher:
print_color(x, "blue")
elif assignment[x] == UNASSIGNED:
print_color(x)
else:
print_color(x, "green")
print_newline()
for x in english_frequency:
count = len([y for y in letters if assignment[y] == x])
if active_cipher and x == assignment[active_cipher]:
print_color(x, "blue")
elif count > 1:
print_color(x, "red")
elif count == 1:
print_color(x, "green")
elif count == 0:
print_color(x)
print_newline()
print_newline()

print_color("cipher", "white")
print_newline()
for x in ciphertext:
if x.lower() not in letters:
print_color(x)
elif x.lower() == active_cipher:
print_color(x, "blue")
elif assignment[x.lower()] == UNASSIGNED:
print_color(x)
else:
print_color(x, "green")
print_newline()
for x in ciphertext:
if x.lower() not in letters:
print_color(x)
continue
if assignment[x.lower()] == UNASSIGNED:
plaintext = "?"
elif x in assignment and assignment[x] != UNASSIGNED:
plaintext = assignment[x]
else:
plaintext = assignment[x.lower()].upper()
count = len([y for y in letters if assignment[y] == assignment[x.lower()]])

if x.lower() == active_cipher:
print_color(plaintext, "blue")
elif plaintext == "?":
print_color("?")
elif count > 1:
print_color(plaintext, "red")
else:
print_color(plaintext, "green")
print_newline()

import sys, termios, tty
def getch():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
def get_okch(ok, error):
while True:
c = getch().lower()
if c in ok:
return c
else:
print(repr(c), error)

if __name__ == "__main__":
assert len(sys.argv) == 2
ciphertext = sys.argv[1]
while True:
display()
c = get_okch(ciphertext.lower() + "\r\x7f", "not in ciphertext, or enter (exit), or backspace (clear all)")
if c == "\r":
sys.exit(1)
elif c == "\x7f":
assignment = { x: UNASSIGNED for x in letters }
continue
display(c)

p = None
p = get_okch(letters + " \r\x7f", "not an english letter, space (clear), or enter (exit)")
if p == " " or p == "\x7f":
assignment[c] = UNASSIGNED
elif p == "\r":
sys.exit(1)
else:
assignment[c] = p


0 comments on commit 691e3c6

Please sign in to comment.