CIT 591 Assignment 3: Playfair Cipher
Fall 2009, David Matuszek

# Purposes of this assignment:

• To give you more practice with lists (and lists of lists)
• To get you started using `unittest` and doing TDD (Test Driven Development)
• To get you started doing string processing
• To get you started with pair programming.

# General idea of the assignment:

You are probably familiar with the idea of "secret codes," such as the cryptograms published in some newspapers. A cipher is a more secure way of making secret messages. In this assignment you will write a program to encipher and decipher secret messages according to the Playfair cipher. (For more about the Playfair cipher, see the Wikipedia article.)

## The general algorithms

Here's how to construct a Playfair cipher.

1. Choose a keyword or key phrase, for example, `university`.
2. Discard all non-letters (whitespace, punctuation, digits), and lowercase any captial letters.
3. If the letter` j `occurs in the keyword, change it to` i`. (So `university` is not changed.)
4. If a letter occurs more than once in the keyword, keep only the first occurrence. (So `university` becomes `universty`.)
5. Put the letters of the keyword into a 5 by 5 array (implement this as a list of lists).
The array you are working with The list representation of the array
 `u` `n` `i` `v` `e` `r` `s` `t` `y` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `
``````[['u', 'n', 'i', 'v', 'e'],
['r', 's', 't', 'y', ' '],
[' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ']]``````
6. Fill in the rest of the letters of the alphabet, in order, omitting` j`.
The array you are working with The list representation of the array
 `u` `n` `i` `v` `e` `r` `s` `t` `y` `a` `b` `c` `d` `f` `g` `h` `k` `l` `m` `o` `p` `q` `w` `x` `z`
``````[['u', 'n', 'i', 'v', 'e'],
['r', 's', 't', 'y', 'a'],
['b', 'c', 'd', 'f', 'g'],
['h', 'k', 'l', 'm', 'o'],
['p', 'q', 'w', 'x', 'z']]``````

Here's how to encode a message, using the array:

1. Make all the letters lowercase, and throw away all blanks and punctuation marks.
2. Change any letter `j` to `i`.
3. Take the next two letters of the message. We can't let them be both the same, so:
1. If only one letter remains, use` x `as the second letter.
2. If the two letters are the same, use` x `instead of the second letter. (The second letter will be the first letter of the next group of two.)
3. If the two letters are both` x`, skip one of them (and use the next letter along as the second letter).
4. If the two letters are in the same column, encode each one with the letter below it, wrapping around if necessarly. (That is, if the letter is in the bottom row, move to the top row.) For example, `id` become `tl`, while `up` becomes `ru`.
5. If the two letters are in the same row, encode each one with the letter to its right, wrapping around if necessarly. (That is, if the letter is in the rightmost column, move to the leftmost column.) For example, `st` become `ty`, while `lo` becomes `mh`.
6. If the two letters are in different rows and different columns, they form a rectangle. Replace each letter with the letter in the same row but in the other corner of the rectangle. For example, `sm` becomes `yk`.
7. Print out the result as a string of letters, with no blanks or other punctuation.

Example:

``````  Programming in Python is fun!  # message to be enciphered
programminginpythonisfun       # lowercased, no j's, no non-letters
pr og ra mx mi ng in py th on is fu nx   # letters two at a time
ub zo sr xv lv ec vi xr rl ke nt bv vq   # encoded two at a time
ubzosrxvlvecvixrrlkentbvvq     # enciphered message``````

Here's how to decode a message, using the array:

• Take the letters two at a time (there will be an even number of letters), and for each pair of letters,
• If they are in the same row, replace each letter with the letter to its left, wrapping around if necessary.
• If they are in the same column, replace each letter with the letter above it, wrapping around if necessary.
• If the two letters are in different rows and different columns, they form a rectangle. Replace each letter with the letter in the same row but in the other corner of the rectangle.

Example:

``````  ubzosrxvlvecvixrrlkentbvvq     # enciphered message
ub zo sr xv lv ec vi xr rl ke nt bv vq
pr og ra mx mi ng in py th on is fu nx
programxminginpythonisfunx     # the best we can do at deciphering it``````

This is as far as you can get in decoding the message. The message is readable, with a little effort on the human's part.

Using Test-Driven Design, create a program named `playfair.py`. It should have at least these two methods:

`def encode(plainTextMessage, secretPhrase)`
Encodes a plainTextMessage, using the secretPhrase. Both the message and the secret phrase may contain capital letters, the letter J, and non-alphabetic characters.
`def decode(encodedMessage, secretPhrase)`
Decodes an encodedMessage, using the secretPhrase. The secret phrase may contain capital letters, the letter J, and non-alphabetic characters, but the encoded message will consist of an even number of letters only, with each pair of letters ready to be decoded.

Every method you write should have a unit test. I have two "top level" tests for you; these will be the last tests you will be able to pass, since they depend on everything else working perfectly.

``````import playfair
import unittest

class TestPlayfair(unittest.TestCase):

def testEncode(self):
message1 = 'Test message ONE.'
message2 = 'Boo, Exxon Oil!'

secretPhrase = 'abc'
self.assertEquals('udtupbxcqckbpocz',
playfair.encode(message1, secretPhrase))
self.assertEquals('dmpdynopfo',
playfair.encode(message2, secretPhrase))

secretPhrase = "Amazingly Few Discotheques Provide Jukeboxes!"
self.assertEquals('pgwqnlokwzlgpeon',
playfair.encode(message1, secretPhrase))
self.assertEquals('xcponpepmf',
playfair.encode(message2, secretPhrase))

def testDecode(self):
message1 = 'Test message ONE.'
message2 = 'Boo, Exxon Oil!'

secretPhrase = 'abc'
self.assertEquals('testmesxsageonex',
playfair.decode('udtupbxcqckbpocz', secretPhrase))
self.assertEquals('booexonoil',
playfair.decode('dmpdynopfo', secretPhrase))

secretPhrase = "Amazingly Few Discotheques Provide Jukeboxes!"
self.assertEquals('testmesxsageonex',
playfair.decode('pgwqnlokwzlgpeon', secretPhrase))
self.assertEquals('booexonoil',
playfair.decode('xcponpepmf', secretPhrase))

unittest.main()```
```

# Due date:

Before 6 AM, Friday October 2, 2009, via Blackboard.