Skip to content

Instantly share code, notes, and snippets.

@gaurav
Created February 4, 2011 18:59
Show Gist options
  • Save gaurav/811562 to your computer and use it in GitHub Desktop.
Save gaurav/811562 to your computer and use it in GitHub Desktop.
A program to crack the cipher at http://keiththomsonbooks.com/cipher-02022012.html
#!/usr/bin/perl
# This is the program I wrote to crack this fun little cipher: http://keiththomsonbooks.com/cipher-02022012.html
# The clue clearly indicates that this is just a simple substitution cipher [1]; even better, it looks like ROT13 [2],
# except that you clearly don't move every letter along by exactly 13 places. So how many letters do you need to move
# it along by? There's only 26 letters in the English alphabet: let's try every possible value!
#
# [1] http://en.wikipedia.org/wiki/Substitution_cipher
# [2] http://en.wikipedia.org/wiki/ROT13
# *All* good Perl programs start with:
use strict;
use warnings;
# So the one-liner I used eventually was:
# echo 'pm aol lhysf ipyk nlaz aol dvyt doha kvlz aol lhysf dvyt nla?' | perl -e '@letters = split(//, <STDIN>); for my $X ('a' .. 'z') { print $X . ": "; for(@letters) { if(/ /) { print; next; }print chr(ord('a') + ((ord($_) - ord($X)) % 26)); } print "\n"; }'
# Here it is in longer, cleaned up form:
my $code = 'pm aol lhysf ipyk nlaz aol dvyt doha kvlz aol lhysf dvyt nla?';
# First, we need the letters to work on.
my @letters = split(//, $code); # split(//, $string) will split the string up into an array, with one character in each array cell.
# Now, we're going to shift each character along by fixed value, but
# we'll try every possible fixed value between 0 and 25. Since there's
# no harm in making it readable ...
for my $based_on_alphabet ('a' .. 'z') { # ... let's just loop over every letter in the alphabet!
# Which letter are we working on?
print "$based_on_alphabet: ";
# Let's go over all the letters in the code string.
for my $letter (@letters) {
# Shifting non-alphabetic characters won't help. So ignore them.
unless($letter =~ /[a-z]/) {
print $letter;
} else {
# Okay, we've got an alphabetical character. Now it gets interesting.
# We need to shift it along a certain number of characters. But we also
# need to make sure it 'loops around' at 26: so once it gets to
# character #26 ('z'), adding one to it should make it go back to #1 ('a').
# That'd be pretty hard to make work, except for the wonderful 'mod' (%) operator.
# This is standard in all programming languages, and what it does is divides
# one number by another, and gives you the *remainder*. This works perfectly, but
# only if you count from zero, which is quite easy to do. So:
# 24 % 26 = 24, 25 % 26 = 25, 26 % 26 = 0, 27 % 26 = 1.
# Perfect.
# There's one last complication to work around: this would work perfectly if 'a' was
# 0 and 'z' was 25, but computers use the ASCII scheme to give numbers to letters so that the
# computer can use them. In the ASCII scheme, 'a' is 97. Luckily, it's as we'd expect
# after that: 'b' is 98, 'c' is 98, and 'z' is 122. In order to use mod to 'loop around',
# we're going to have to subtract 97 from the character code we have in $letter.
# $letter_position = ord($letter) - 97;
# would have been perfect, but we can do one better. Perl doesn't see the difference between
# the character 97 and the letter 'a', since it thinks in ASCII. So we can make our code a
# bit more readable by writing:
my $letter_position = ord($letter) - ord('a');
# Now we just shift it along by the letter stored in $based_on_alphabet and
# mod it by 26. But $based_on_alphabet is in ASCII too, remember? So:
my $shift_by = ord($based_on_alphabet) - ord('a');
# Now a's are represented by 0, b's by 1, and z's by 25. Perfect.
my $new_character = ($letter_position + $shift_by) % 26;
# Now we print this - oh, wait! Perl can't print this as
# a character unless it's back as ASCII, right?
$new_character = $new_character + ord('a');
# Now all we need to do is print that, and we're done!
print chr($new_character);
}
}
print "\n"; # Print a newline so the output is neat.
}
__DATA__
# Try it! You should get:
a: pm aol lhysf ipyk nlaz aol dvyt doha kvlz aol lhysf dvyt nla?
b: qn bpm miztg jqzl omba bpm ewzu epib lwma bpm miztg ewzu omb?
c: ro cqn njauh kram pncb cqn fxav fqjc mxnb cqn njauh fxav pnc?
d: sp dro okbvi lsbn qodc dro gybw grkd nyoc dro okbvi gybw qod?
e: tq esp plcwj mtco rped esp hzcx hsle ozpd esp plcwj hzcx rpe?
f: ur ftq qmdxk nudp sqfe ftq iady itmf paqe ftq qmdxk iady sqf?
g: vs gur rneyl oveq trgf gur jbez jung qbrf gur rneyl jbez trg?
h: wt hvs sofzm pwfr ushg hvs kcfa kvoh rcsg hvs sofzm kcfa ush?
i: xu iwt tpgan qxgs vtih iwt ldgb lwpi sdth iwt tpgan ldgb vti?
j: yv jxu uqhbo ryht wuji jxu mehc mxqj teui jxu uqhbo mehc wuj?
k: zw kyv vricp sziu xvkj kyv nfid nyrk ufvj kyv vricp nfid xvk?
l: ax lzw wsjdq tajv ywlk lzw ogje ozsl vgwk lzw wsjdq ogje ywl?
m: by max xtker ubkw zxml max phkf patm whxl max xtker phkf zxm?
n: cz nby yulfs vclx aynm nby qilg qbun xiym nby yulfs qilg ayn?
o: da ocz zvmgt wdmy bzon ocz rjmh rcvo yjzn ocz zvmgt rjmh bzo?
p: eb pda awnhu xenz capo pda skni sdwp zkao pda awnhu skni cap?
q: fc qeb bxoiv yfoa dbqp qeb tloj texq albp qeb bxoiv tloj dbq?
r: gd rfc cypjw zgpb ecrq rfc umpk ufyr bmcq rfc cypjw umpk ecr?
s: he sgd dzqkx ahqc fdsr sgd vnql vgzs cndr sgd dzqkx vnql fds?
t: if the early bird gets the worm what does the early worm get?
u: jg uif fbsmz cjse hfut uif xpsn xibu epft uif fbsmz xpsn hfu?
v: kh vjg gctna dktf igvu vjg yqto yjcv fqgu vjg gctna yqto igv?
w: li wkh hduob elug jhwv wkh zrup zkdw grhv wkh hduob zrup jhw?
x: mj xli ievpc fmvh kixw xli asvq alex hsiw xli ievpc asvq kix?
y: nk ymj jfwqd gnwi ljyx ymj btwr bmfy itjx ymj jfwqd btwr ljy?
z: ol znk kgxre hoxj mkzy znk cuxs cngz juky znk kgxre cuxs mkz?
# Do one of those lines look like English? Congratulations, you've
# broken the cipher!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment