|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Payments Humor Music |
Create and solve substitution ciphers, Visual C++, GNU g++ (Encipher, Decipher)A substitution cipher is text in which every letter A-Z has been changed to some other letter A-Z. It looks like nonsense. The game is to determine which letter in the ciphered text stands for which letter in the alphabet and discover what the original text was. Substitution cipher puzzles are published in newspapers and game magazines. The Encipher program creates a substitution cipher from console input or from a text file. It is for either Visual C++ in Windows or the GNU GCC g++ compiler in Linux. The Decipher program assists you with solving a substitution cipher. It provides an interface where you can easily and quickly swap letter translations and see the results immediately in the text. Because the methods for colorizing text on the screen are different for Windows and Linux, the Windows version is on this page, and there is a separate page for the Linux version. There is also a previous version in Borland C++ 4.0 for an MSDOS target . In addition to the source code on this page, these programs also require my.h and mylib.cpp.
Screenshot of the decipher programshowing letter frequencies and the text being decoded. Locked letters are green.
|
/* DECIPHER.CPP 5-31-07
MS VCPP Version.
Copyright (C)1994-1996, 2007 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
Version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Assists with solving substitution cipher puzzles such as are sometimes published in newspapers.
A substitution cipher is text in which every letter A-Z has been transposed to some other
letter A-Z. The text looks like nonsense, but if you can determine which letter in the
ciphered text stands for which letter in the alphabet, you can restore the text to its
original state.
The program loads the source text file you specify on the command line, then does a quick
preliminary decode in which the most common letters in the text are translated to the most
commonly occurring letters in most English texts. It may or may not be correct, but it's
a start. Then it provides an interface where you can easily and quickly swap letter
translations and see the results immediately in the text. This is a lot easier than doing
it on paper, but of course if you use this program you are cheating!
When swapping letters, just press S (for Swap), then the letter that you think is wrong,
then the letter you think it should be. (Order of the swapped letters doesn't matter.)
-----
TO DO
Revised letter frequencies, from a tv show: ETAONI....JKXQZ.
TH is the most common letter pair. EA is the most common vowel pair.
Could be automated, checking the resulting words against a dictionary,
and swapping letters until they are all known words.
*/
#include "stdafx.h"
#include "..\..\mylib\mylib.cpp"
using namespace System;
using namespace std;
//////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
//----------------------------------------------------------------------------
struct translate // TRANSLATION TABLE
{
char orig; // LETTER FROM INPUT TEXT
char trans; // LETTER IT'S TRANSLATED TO
int freq; // FREQUENCY OF ORIG IN INPUT TEXT
char lock; // PREVENTS TRANS FROM BEING CHANGED
} letter[26];
string intext; // INPUT TEXT
//----------------------------------------------------------------------------
// DETERMINE LETTER FREQUENCIES
void freqtally()
{
for(uint i = 0 ; i < 26 ; i++) // FOR EACH LETTER,
for(uint j = 0 ; j < intext.length() ; j++) // SEARCH FOR IT,
if(intext[j] == letter[i].orig) // AND, WHEN FOUND,
letter[i].freq++; // INCREMENT COUNT
}
//----------------------------------------------------------------------------
// INITIALIZES TRANSLATION TABLE
void initable()
{
string source("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
for(int i = 0 ; i < 26 ; i++)
{
letter[i].orig = source[i];
letter[i].trans = source[i];
letter[i].freq = 0;
letter[i].lock = false;
}
}
//----------------------------------------------------------------------------
// GETS INPUT TEXT FROM DISK FILE
// RETURNS true IF OK, false if not
bool readtext(const char *filename)
{
ifstream infile(filename);
if(!infile)
{
Console::WriteLine("Error reading file, or not found.");
return(false);
}
intext.clear();
stringfromfile(intext, infile);
toupper(intext);
return(true);
}
//----------------------------------------------------------------------------
// DISPLAY TEXT, AS TRANSLATED, and optionally also write to disk file.
// This looks kind of messy, but putting it into 2 different functions
// didn't look much better. It would probably be ok to just use one
// function, with the constream manipulators, and let them be ignored
// when writing to a disk file. Could also break some of the sections here
// into separate functions (showtranslations(), showfreq(), showastranslated())
void showtrans(int tofile) // FLAG WHETHER TO also WRITE TO DISK FILE
{
ofstream outfile;
if(tofile)
outfile.open("deciphed.txt");
uint i;
for(i = 0 ; i < 26 ; i++) // SHOW CURRENT CODE SCHEME
{
// DIFFERENT COLOR IF LOCKED. The Console:: functions can be intermingled with
// ordinary cout calls. A good thing because Console::Write outputs chars as decimal numbers.
// Couldn't figure out how to force their output as letters.
Console::ForegroundColor = (letter[i].lock ? ConsoleColor::Green : ConsoleColor::Gray);
cout << setw(3) << setiosflags(ios::left) << letter[i].trans;
if(tofile)
outfile << setw(3) << setiosflags(ios::left) << letter[i].trans;
}
cout << endl;
if(tofile)
outfile << endl;
Console::ForegroundColor = ConsoleColor::Red;
for(i = 0 ; i < 26 ; i++) // SHOW LETTER FREQUENCIES
{
cout << setw(3) << setiosflags(ios::left) << letter[i].freq;
if(tofile)
outfile << setw(3) << setiosflags(ios::left) << letter[i].freq;
}
cout << endl << endl;
if(tofile)
outfile << endl << endl;
for(i = 0 ; i < intext.length() ; i++) // FOR EACH CHAR IN INTEXT,
{
if(Console::KeyAvailable == true) // ANY KEY CUTS LISTING SHORT
{
Console::ReadKey(true);
break;
}
bool found = false;
for(uint j = 0 ; j < 26 ; j++) // LOOK IT UP IN TABLE
if(intext[i] == letter[j].orig) // IF FOUND, IT'S A LETTER
{ // SHOW ITS TRANSLATION
Console::ForegroundColor = (letter[j].lock ? ConsoleColor::Green : ConsoleColor::Gray);
cout << letter[j].trans;
if(tofile)
outfile << letter[j].trans;
found = true;
break; // BREAK LOOP TO DO NEXT CHAR
}
if(!found) // OUTPUT NON-LETTER AS-IS
{
if(intext[i] == '\t') // expand tabs to spaces
cout << " ";
else
{
Console::ForegroundColor = ConsoleColor::Gray;
cout << intext[i];
}
if(tofile)
outfile << intext[i];
}
}
cout << endl;
if(tofile)
outfile << endl;
} // end showtrans()
//----------------------------------------------------------------------------
// SWAP TRANSLATIONS FOR 2 LETTERS
// BEFORE: ZBRUET AFTER: ZBRUET <- LETTER[].ORIG, NEVER CHANGES
// ZBRUET ETRUZB <- LETTER[].TRANS
// RETURNS true IF OK, false IF SWAP DISALLOWED
int swaplets(char a, char b) // LETTERS WHOSE TRANSLATIONS ARE TO BE SWAPPED
{
a = toupper(a);
b = toupper(b);
int adex, bdex; // adex and bdex are INDEXES OF A AND B IN LETTER[].TRANS
for(adex = 0 ; (adex < 26) && (letter[adex].trans != a) ; adex++); // LOCATE A
for(bdex = 0 ; (bdex < 26) && (letter[bdex].trans != b) ; bdex++); // LOCATE B
if((letter[adex].lock) || (letter[bdex].lock)) // TEST FOR LOCKED
return(false);
char ch = letter[adex].trans; // SWAP THEIR TRANSLATIONS
letter[adex].trans = letter[bdex].trans;
letter[bdex].trans = ch;
return(true);
}
//----------------------------------------------------------------------------
// INITIALLY ASSIGN TRANSLATIONS FOR MOST FREQUENT LETTERS
// This doesn't always result in the assignments I expect, maybe because of the letter locking.
void etaoin()
{
// ASSIGN MOST FREQUENT LETTER TO 'E', TEMPORARILY LOCK IN PLACE,
// 2ND MOST FREQUENT = 'T', ETC.
swaplets(letter[0].orig,'E'); letter[0].lock = true;
swaplets(letter[1].orig,'T'); letter[1].lock = true;
swaplets(letter[2].orig,'A'); letter[2].lock = true;
swaplets(letter[3].orig,'O'); letter[3].lock = true;
swaplets(letter[4].orig,'I'); letter[4].lock = true;
swaplets(letter[5].orig,'N'); letter[5].lock = true;
swaplets(letter[6].orig,'S'); letter[6].lock = true;
swaplets(letter[7].orig,'H'); letter[7].lock = true;
swaplets(letter[8].orig,'R'); letter[8].lock = true;
swaplets(letter[9].orig,'D'); letter[9].lock = true;
swaplets(letter[10].orig,'L'); letter[10].lock = true;
swaplets(letter[11].orig,'U'); letter[11].lock = true;
// SHUFFLE THESE SELDOM-USED TO END OF ARRAY, IF POSSIBLE
swaplets(letter[25].orig,'Q'); letter[25].lock = true;
swaplets(letter[24].orig,'J'); letter[24].lock = true;
swaplets(letter[23].orig,'X'); letter[23].lock = true;
swaplets(letter[22].orig,'Z');
for(int i = 0 ; i < 26 ; i++) // RE-UNLOCK THEM ALL
letter[i].lock = false;
}
//----------------------------------------------------------------------------
// SORT ARRAY IN ORDER OF DECREASING FREQUENCY
int sortlets(struct translate *l, struct translate *r)
{
return(r->freq - l->freq); // a reverse-sort (backwards)
}
//----------------------------------------------------------------------------
// MAIN
int main(array<System::String ^> ^args)
{
Console::Clear();
cout << "Usage: DECIPHER filename.ext. Output, if any, goes to DECIPHED.TXT" << endl;
string s;
if(args->Length < 2)
{
s = "CIPHERED.TXT"; // used during testing, when there are no command line args.
//return(1); // an alternative: quit.
}
else
SystemStringToBasicString(args[1],s);
initable(); // PUT LETTERS IN THE TRANS. TABLE
if(readtext(s.c_str())) // GET CIPHERED TEXT FROM FILE
{
freqtally(); // TALLY LETTER FREQUENCIES
// sort by decreasing frequency
qsort(letter,26,sizeof(letter[0]),(int (*)(const void *,const void *)) sortlets);
if(yesno("Automatically assign ETAOIN... to most frequent letters?"))
etaoin(); // ASSIGN ETAOIN TO MOST FREQ.
uint i, j;
string buf;
char ch;
while(1) // START PROCESSING
{
showtrans(false); // DISPLAY TEXT
Console::ForegroundColor = ConsoleColor::Gray;
cout << "<S>wap, <L>ock, <U>nlock, <D>one, <Q>uit: ";
ch = tolower(getche());
cout << endl;
switch(ch)
{
case 's': // SWAP LETTERS
do
{
cout << "Swap: ";
getline(cin,buf,'\n');
}
while(buf.length() < 2); // C/R = ABORT
if(!swaplets(buf[0],buf[1]))
putchar(7); // A LETTER IS LOCKED - BEEP
cout << endl;
break;
case 'u': // unlock letters
case 'l': // LOCK LETTERS
cout << ((ch == 'l' ? "Lock letter(s): " : "Unlock letter(s): "));
getline(cin,buf,'\n'); // could use my cgets() from net.cpp
if(!buf.length()) // C/R = ABORT
break;
toupper(buf);
for(j = 0 ; j < buf.length() ; j++)
for(i = 0 ; i < 26 ; i++)
if(letter[i].trans == buf[j])
{
letter[i].lock = (ch == 'l' ? true : false);
break;
}
cout << endl;
break;
case 'd': // DONE - WRITE TO DISK & EXIT
showtrans(true);
case 'q': // quit - no write to disk
cout << "Press ESC to quit. ";
if(getch() == 27)
return(0);
cout << endl;
break;
default:
break;
} // END SWITCH
} // END WHILE(1)
} // end if(readtext)
return(0);
} // end main
A sample CIPHERED.TXT file for testing.
BZAE AE GSEB U BOEB BD EOO ZDW BZO VCDLCUI WDCQE.
Input is from stdin, output to stdout, so you can use redirection and pipes. Default in/out is console.
Use the Visual C++ version of my.h and mylib.cpp (see links above), or the GNU g++ my.h and mylib.cpp, as needed.
/* ENCIPHER.CPP 1-26-2010 Microsoft Visual C++ or GNU GCC g++
Copyright (C)1994-1996, 2007, 2010 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
Version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
CREATES SUBSTITUTION CIPHERS BY RANDOMLY REARRANGING THE ALPHABET.
Notes:
--Probably my only remaining example of using qsort(). Don't change to sorted container.
*/
// The stdafx include cannot be inside a condition directive (gives weird error),
// so you must manually uncomment it for Windows.
#include "stdafx.h"
#ifdef WIN32
#include "..\..\mylib\mylib.cpp"
#else
#include "../include/mylib.cpp"
#endif
using namespace std;
//////////////////////////////////////////////////////////////////////////////
struct translate // TRANSLATION TABLE
{
char orig; // LETTER FROM INPUT TEXT
char trans; // LETTER IT'S TRANSLATED TO
int sequence; // USED FOR SHUFFLING
} letter[26];
//----------------------------------------------------------------------------
// SORT ROUTINE FOR SHUFFLING
int ShuffleSort(struct translate *l,struct translate *r)
{
return(l->sequence - r->sequence);
}
//----------------------------------------------------------------------------
// Put translation letters into translation table, assign random number to
// each letter of the alphabet, and sort the array by the random numbers,
// which causes them to be randomly shuffled.
// Then add the original letters, in proper order. This leaves the array
// elements in order of source letters ABC..., with each one tied to
// a random and unique translation letter.
void initable()
{
string source("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
uint i;
for(i = 0 ; i < 26 ; i++) // fill letter with translation letters
{
letter[i].trans = source[i];
letter[i].sequence = random(10000);
}
// mix them up
qsort(letter,26,sizeof(letter[0]),(int (*)(const void *,const void *)) ShuffleSort);
for(i = 0 ; i < 26 ; i++) // now fill in orig in proper order
letter[i].orig = source[i];
}
//----------------------------------------------------------------------------
// MAIN
int main()
{
cerr << "Creates substitution ciphers by randomly rearranging the alphabet." << endl;
cerr << "Usage: encipher [< infile] [> outfile]" << endl;
srand((unsigned)time(NULL));
initable(); // fill translation table, shuffle alphabet
string buf;
while(getline(cin,buf,'\n'))
{
for(uint j = 0 ; j < buf.length() ; j++) // for each letter in file line,
{
char ch = toupper(buf[j]); // to uppercase for testing
for(uint i = 0 ; i < 26 ; i++) // look each letter up,
if(ch == letter[i].orig)
{
ch = letter[i].trans; // and translate it
break;
}
buf[j] = ch; // move back, possibly translated
}
cout << buf << endl;
}
return(0);
}
|
|
|
|
|
|
Copyright ©2010 Steven Whitney. Last modified Thu 10/21/2010 02:08:03 -0700. |
||