The world has its fair share of serial killers. Some have never been caught and are probably running in the wild freely with their real identities still unknown. Such as the case of a serial killer using the moniker "Zodiac" who murdered at least seven people between December 1968 and October 1969. He taunted local police and newspapers with handwritten letters containing threats, claims of further undiscovered victims, and coded messages. The supposed killer claimed that the solutions would reveal an identity when they were all solved. Some never were, leaving the authorities and many code crackers worldwide puzzled for years, and the killer was never caught.
More than 50 years after the so-called Zodiac Killer first began terrorizing the streets of Northern California, one of the killer's mysterious encoded messages was finally deciphered on December 5, 2020. It is a 340-symbol encrypted message — known as Z-340 — sent to the San Francisco Chronicle in November 1969. The international team of amateur code crackers that succeeded in doing so consisted of David Oranchak (a software developer from Virginia, USA), Sam Blake (a mathematician from Australia) and Jarl Van Eycke (a warehouse operator from Belgium). Unfortunately, the message does not contain enough information to unmask the Zodiac Killer.
To encode Z-340, the Zodiac Killer used a classic trick: replace each letter with a symbol. This was easy to break. But the insight that eventually led to cracking the code was that the letters should not be read from left to right and from top to bottom. Still, the Zodiac Killer started writing at the top left corner, but then put each subsequent symbol one row down and two columns to the right. Beyond the rightmost column he continued at the leftmost column, and past the bottom row he continued at the top row.
For example, let's take the message:
I've no plans to call on you, Clarice. The world is more interesting with you in it.
This message consists of 84 characters that are put in a grid with 4 rows and 21 columns in the following way:
If we replace these positions with their corresponding characters from the message, the cipher looks like:
We will call this the Zodiac cipher with vertical offset 1 and horizontal offset 2, even though we omit replacing characters with symbols. So, in textual format, the 4-line Zodiac cipher of our example message becomes:
Iatsou e ,ephhlm o'ri r yin.wcii rle yorvintliono iastCea snwoneecgodnut Ttl .l
Your task:
Write a function read_ciphertext that takes the location (str) of a text file with the Zodiac cipher of a message (a number of lines that each contain the same number of characters). The function must return a list containing the consecutive lines (str) of the cipher.
Write a function decode that takes the location (str) of a text file with the Zodiac cipher of a message (a number of lines that each contain the same number of characters). The function also has a second optional parameter vertical that may take the vertical offset that was used to encode the message (int; default value: 1). The function also has a third optional parameter horizontal that may take the horizontal offset that was used to encode the message (int; default value: 2). The function must return the original message (str).
Write a function encode that takes two arguments: i) a message (str) and ii) a number $$n \in \mathbb{N}_0$$ (int). The function also has a third optional parameter vertical that may take the vertical offset that must to be used to encode the message (int; default value: 1). The function also has a fourth optional parameter horizontal that may take the horizontal offset that must be used to encode the message (int; default value: 2). The function must return a string (str) with the $$n$$ lines of the Zodiac cipher of the given message. The function may assume that the length of the given message is a multiple of $$n$$ and that the cipher fills all cells of the grid, without the need to check this explicitly.
In the following interactive session we assume the text files ciphertext.01.txt1 and ciphertext.02.txt2 to be located in the current directory.
To enable using the following interactive session as a doctest, we have escaped all backslashes (in newline representations) as double backslashes.
Testcases that print a result may be corrupted by editors that automatically remove white space characters at the end of lines.
>>> message = "I've no plans to call on you, Clarice. The world is more interesting with you in it."
>>> read_ciphertext('ciphertext.01.txt3')
['Iatsou e ,ephhlm', " o'ri r yin.wcii rle ", ' yorvintliono iastCea', 'snwoneecgodnut Ttl .l']
>>> decode('ciphertext.01.txt4')
"I've no plans to call on you, Clarice. The world is more interesting with you in it."
>>> encode(message, 4)
"Iatsou e ,ephhlm\\n o'ri r yin.wcii rle \\n yorvintliono iastCea\\nsnwoneecgodnut Ttl .l"
>>> print(encode(message, 4))
Iatsou e ,ephhlm
o'ri r yin.wcii rle
yorvintliono iastCea
snwoneecgodnut Ttl .l
>>> read_ciphertext('ciphertext.02.txt5')
['Ioni TC seiwe r ooihsrreual', ' d lnv tseantn teoococp.yi', ",la'ugnihlo nirmwiy t tel."]
>>> decode('ciphertext.02.txt6', vertical=2, horizontal=3)
"I've no plans to call on you, Clarice. The world is more interesting with you in it."
>>> encode(message, 3, vertical=2, horizontal=3)
"Ioni TC seiwe r ooihsrreual\\n d lnv tseantn teoococp.yi\\n,la'ugnihlo nirmwiy t tel."
>>> print(encode(message, 3, vertical=2, horizontal=3))
Ioni TC seiwe r ooihsrreual
d lnv tseantn teoococp.yi
,la'ugnihlo nirmwiy t tel.