Skip to main content

How to Decrypt a Triangle Cipher in Python

A triangle cipher is a transposition cipher where characters are arranged in a right-angled triangle. To decrypt it, we extract the last character of every row to form the hidden message. This guide implements a robust decryption function in Python that handles arithmetic sequences and edge cases like whitespace or empty inputs.

Understanding the Triangle Logic

Visualizing the cipher is crucial before coding. Given the string "Tcudctsdxosdxcr", the characters fill a triangle row by row, increasing the row length by 1 each time.

Row 1 (1 char):  T              -> Last char: T
Row 2 (2 chars): c u -> Last char: u
Row 3 (3 chars): d c t -> Last char: t
Row 4 (4 chars): s d x o -> Last char: o
Row 5 (5 chars): s d x c r -> Last char: r

Decrypted Message: "Tutor"

We need to jump through the string indices based on an increasing step size (1, 2, 3, 4, ...).

Step 1: Input Validation and Cleaning

Before processing, we must ensure the input is valid. The function should handle None types gracefully and strip unnecessary whitespace that might affect index calculations.

Handling None and Whitespace:

def prepare_text(text):
# ⛔️ Incorrect: Calling string methods on None causes a crash
# cleaned = text.strip()

# ✅ Correct: Check for existence first
if text:
# Manual stripping logic (or simply use text.strip())
while text.startswith(" "):
text = text[1:]
while text.endswith(" "):
text = text[:-1]
return text
else:
return None
tip

While the manual while loops above work, Python's built-in text.strip() method is the standard, most efficient way to remove leading and trailing whitespace.

Step 2: Implementing the Decryption Loop

The core logic involves calculating the index of the last character of each row. We start at index 0, and the gap between the ends of rows increases by 1 in every iteration.

The Index Jumping Algorithm:

text = "Tcudctsdxosdxcr"
decryption_text = ""
i = 0 # Current index
step = 1 # Size of the current row

# Iterate while the index is within bounds
while i < len(text):
# Capture the character at the current "end of row"
decryption_text += text[i]

# Calculate the next index:
# Current Index + 1 (move to next char) + Step (skip the rest of the new row)
i = i + 1 + step

# Increase the size of the next row
step += 1

# Handle incomplete triangles (leftover characters)
# If the loop overshot the length but didn't land exactly at the end
if i - step + 1 != len(text):
decryption_text += text[-1]

print(f"Result: {decryption_text}")

Output:

Result: Tutor
note

The condition if i - step + 1 != len(text) handles "imperfect" triangles where the last row might not be fully complete according to the arithmetic sequence, ensuring the absolute last character is captured if missed.

Complete Code Solution

Here is the fully functional script combining validation, cleaning, and the arithmetic logic.

from typing import Optional

def triangle_decryption(text: Optional[str]) -> Optional[str]:
"""
Decrypts a string arranged in a right-triangle pattern by
extracting the last character of each row.
"""
decryption_text = ""

# 1. Validate Input
if text:
# 2. Clean Input (Remove whitespace)
while text.startswith(" "):
text = text[1:]
while text.endswith(" "):
text = text[:-1]

# 3. Decryption Loop
i = 0
step = 1
while i < len(text):
decryption_text += text[i]

# Jump to the end of the next row
# logic: current_pos + 1 (start of next row) + step (length of next row)
i = i + 1 + step
step += 1

# 4. Handle Edge Case (Incomplete last row)
# Check if the calculated 'i' perfectly matches the end or if we missed the last char
# Previous i was (i - step - 1).
if i - step + 1 != len(text):
decryption_text += text[-1]

else:
# Handle None input
decryption_text = None

return decryption_text

if __name__ == "__main__":
# Test Case 1: Perfect Triangle
print(f"Input: 'Tcudctsdxosdxcr' -> {triangle_decryption('Tcudctsdxosdxcr')}")

# Test Case 2: Imperfect Triangle
print(f"Input: 'Tcudc' -> {triangle_decryption('Tcudc')}")

# Test Case 3: Leading Whitespace
print(f"Input: ' Tcudctsdxosdxcr'-> {triangle_decryption(' Tcudctsdxosdxcr')}")

# Test Case 4: None
print(f"Input: None -> {triangle_decryption(None)}")

Output:

Input: 'Tcudctsdxosdxcr' -> Tutor
Input: 'Tcudc' -> Tuc
Input: ' Tcudctsdxosdxcr'-> Tutor
Input: None -> None

Conclusion

Decrypting a triangle cipher is an exercise in identifying arithmetic progressions within string indices.

  1. Visualize the shape to determine the row lengths (1, 2, 3, ...).
  2. Calculate the indices: The gap between selected characters grows linearly.
  3. Validate inputs to prevent errors with None or whitespace.