Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
First solution in 3rd party category for Grille Cipher Attack by kdim
from typing import List
from numpy import logical_and
def grille_to_2d(grille):
members = set()
for member in grille:
if isinstance(member, set):
members |= member
else:
members.add(member)
return [[j * 8 + i in members for i in range(8)] for j in range(8)]
def grille_rotate(grille):
g = grille_to_2d(grille)
g = list(zip(*g[::-1]))
return sorted(j * 8 + i for i in range(8) for j in range(8) if g[j][i])
def grille_flatten(grille):
grillemax = [max(i) for i in grille[1:]] + [64]
grillemin = [-1] + [min(i) for i in grille[:-1]]
for idx, members in enumerate(grille):
for member in list(members):
if member >= grillemax[idx] or member <= grillemin[idx] or member > 48 + idx or member < idx:
members.remove(member)
return grille_flatten(grille)
return grille
def grille_detect(grille, path, memberprev, grilleset):
if not grille:
yield path
else:
for member in grille[0]:
if member < memberprev:
continue
j, i = divmod(member, 8)
memberset = {member, 56 - i * 8 + j, 7 + i * 8 - j, 63 - i - j * 8}
if memberset & grilleset:
continue
yield from grille_detect(grille[1:], path + [member], member, memberset | grilleset)
def find_grille(plaintext: str, cryptogram: str) -> List[str]:
indices = lambda x: [n for n, c in enumerate(cryptogram) if c == x]
grillekeys = [[set(indices(c)) for c in plaintext[i:i + 16]] for i in range(0, 64, 16)]
grillekeys = [grille_flatten(grille) for grille in grillekeys]
grille = grille_to_2d(grillekeys[1])
for idx in range(1, 4):
grille = logical_and(list(zip(*grille[::-1])), grille_to_2d(grillekeys[(idx + 1) % 4]))
grillekey = [[idx for idx in members if grille[idx // 8][idx % 8]] for members in grillekeys[0]]
for key in grille_detect(grillekey, [], 0, set()):
textkey = ["".join("X" if j * 8 + i in key else "." for i in range(8)) for j in range(8)]
text, k = "", key
for _ in range(3):
k = grille_rotate(k)
text += "".join(cryptogram[idx] for idx in k)
if text in plaintext:
return textkey
if __name__ == "__main__":
print("Example:")
print(
find_grille(
"quickbrownfoxjumpsoverthelazydogandjackdawslovesmysphinxofquartz",
"quicpsovkbroerthwnfoelazxjumydogmyspandjhinxackdofquawslartzoves",
)
)
# These "asserts" are used for self-checking and not for an auto-testing
assert find_grille(
"quickbrownfoxjumpsoverthelazydogandjackdawslovesmysphinxofquartz",
"quicpsovkbroerthwnfoelazxjumydogmyspandjhinxackdofquawslartzoves",
) == [
"XXXX....",
"XXXX....",
"XXXX....",
"XXXX....",
"........",
"........",
"........",
"........",
]
assert find_grille(
"weareallfromxanthcubesaidquicklyjustvisitingphazewewontbeforlong",
"wejhewucuaeswtbrveeoisantsalilbifdteifrqunooigrmplxcakhonnlagtyz",
) == [
"X...X...",
".X.....X",
"..X...X.",
"...X.X..",
"X.....X.",
"...X...X",
"..X.X...",
".X...X..",
]
assert find_grille(
"theideaofcognitivebiasinpsychologyworksinananalogouswayacognitiv",
"tgovgeubyhsiawseiinorkdepaswoasifcyncyaanaognconaginihlttoiivloo",
) == [
"X.......",
".X.....X",
"X.....XX",
".X..X...",
"XX......",
"..XXX...",
"..X....X",
"...X....",
]
print("Coding complete? Click 'Check' to earn cool rewards!")
Sept. 5, 2022
Comments: