← Back to Blog

ADFGVX Project — Round 5/5

We have the substituted message recovered. Here's where we are and what we're working with:

subst_key         = WASHINGTON
transp_key        = BERLIN
original_message  = ATTACKATDAWN

substituted_message = ADDDDDADDVFGADDDDDXADAAAX
final_message       = AAAA DDDD DFDA DVDA DGXX DDDA

# Step 1 — complete
# From final_message + transp_key → substituted_message ✓

# Step 2 — in progress
# From substituted_message + subst_key → ATTACKATDAWN

The transposition is reversed. Every character is back in its correct position. Every pair of letters now represents a real character — as long as we know what each pair means. And for that, we need the substitution key.

Substitution key — same validation, same relaxed rules

final_subst_key = ""
while not final_subst_key:
    subst_key = input("Please enter key: ")
    clean_subst_key = ""
    for letter in subst_key.upper():
        if letter in all_chars:
            clean_subst_key += letter
    final_subst_key = ""
    if clean_subst_key:
        for letter in clean_subst_key:
            if letter not in final_subst_key:
                final_subst_key += letter

We could have placed the decryption logic before asking for the second key — giving the impression that the first key alone determines the result. For someone initiated, that's immediately suspicious. So we leave the deception element for later. Whatever order you choose — have a reason for it.

Rebuilding the substitution dictionary — reversed

Same construction as the encoder. But this time the dictionary is inverted — the ADFGVX pair is the key, the character is the value. Looking up matrix_dict["AD"] is simpler than scanning all values to find which key maps to "A".

sorted_chars = ""
reference = final_subst_key + all_chars

for letter in reference:
    if letter not in sorted_chars:
        sorted_chars += letter

With key WASHINGTON, sorted_chars is:

WASHINGTOBCDEFJKLMPQRUVXYZ0123456789

Placed in the 6×6 grid — same as at encoding:

   A  D  F  G  V  X
A  W  A  S  H  I  N
D  G  T  O  B  C  D
F  E  F  J  K  L  M
G  P  Q  R  U  V  X
V  Y  Z  0  1  2  3
X  4  5  6  7  8  9
matrix_letters = [
    "AA", "AD", "AF", "AG", "AV", "AX",
    "DA", "DD", "DF", "DG", "DV", "DX",
    "FA", "FD", "FF", "FG", "FV", "FX",
    "GA", "GD", "GF", "GG", "GV", "GX",
    "VA", "VD", "VF", "VG", "VV", "VX",
    "XA", "XD", "XF", "XG", "XV", "XX"
]

matrix_dict = {}
for i in range(0, len(sorted_chars)):
    matrix_dict[matrix_letters[i]] = sorted_chars[i]

Now matrix_dict["AD"] returns "A". matrix_dict["DD"] returns "T". The reverse of what the encoder built.

Decryption — and the deception

Before iterating the substituted message, we handle the "Q" case. If the transposition failed — wrong key, wrong message format — subst_message was set to "Q". Instead of an error, we return a plausible but meaningless message. Someone without the correct key gets a response that looks real enough to be convincing.

if subst_message == "Q":
    decoded_message = "WENEEDCOFFEEASAP"
else:
    decoded_message = ""
    for i in range(0, len(subst_message), 2):
        pair = subst_message[i:i+2]
        decoded_message += matrix_dict[pair]

We iterate with a step of 2. At each position, we take a two-character slice — subst_message[i:i+2] — and look it up in matrix_dict. The slice excludes the second index, so [0:2] gives us characters at index 0 and 1. Clean, no overlap.

Verification — our known values

subst_message = ADDDDDADDVFGADDDDDXADAAAX

Pairs:
AD → A
DD → T
DD → T
AD → A
DV → C
FG → K
AD → A
DD → T
DX → D
AD → A
AA → W
AX → N

decoded_message = ATTACKATDAWN ✓

Both steps complete. The message is recovered.

Export

import datetime
today_str = datetime.date.today().strftime("%Y_%m_%d")

export_name = f"decoded_{message_date}.txt"

with open(export_name, "w", encoding="utf-8") as f:
    f.write(f"Decoded on: {today_str}")
    f.write("\n")
    f.write("=" * 20)
    f.write(f"\n{decoded_message}")

print(f"Message decoded and exported successfully as {export_name}")

The filename references the original coded message date — so encoded and decoded files can be paired in an archive. The file itself records when the decryption happened.

This was one of my favorite exercises. When I got it right for the first time, I felt genuinely in control. Not because it's difficult — the pieces are on Google, on StackOverflow, in an AI. But here, the thinking, the order of steps, the sequence, the discipline matter more than the concepts. Without them, it's extremely hard regardless of the tools available.

In the first project we lit the fire. In the second we built the fort. Now we're on the counterattack — and Ares is smiling at us. Python is no longer an abstract concept, something untouchable. It's an ally now.

What This Code Can Become — and then OOP.

[ login to bookmark ] // copied! 35 views · 3 min
// resources
Other coded_2025_04_07.txt Exercise adfgvx_decoder.py
← prev ADFGVX Project — Round 4/5 next → ADFGVX Project — What This Code Can Become
// 0 comments
// No comments yet. Be the first.
// leave a comment

// Your comment will appear after approval.