The Big Book of Small EK9 Programs
Adapted from Al Sweigart's The Big Book of Small Python Projects: 81 Easy Practice Programs (No Starch Press, 2021; copyright © 2021 Al Sweigart), which he makes freely available under a Creative Commons Attribution-NonCommercial-ShareAlike licence at inventwithpython.com/bigbookpython. Changes were made: each program was ported from Python to EK9 and remodelled in EK9's idioms.
Project 61 — ROT13 Cipher
This is project 61 of the book, ported to EK9. Its chapter is still being written; until then, this page shows the port itself: a run of it, the whole program, and how it keeps to the original.
A sample run
The player's answers are piped in:
Hello, World!
Uryyb, Jbeyq!
QUIT
Run it with:
ek9 rot13cipher.ek9 < input.txt
It prints:
ROT13 Cipher, by Al Sweigart al@inventwithpython.com
Enter a message to encrypt/decrypt (or QUIT):
The translated message is:
Uryyb, Jbeyq!
Enter a message to encrypt/decrypt (or QUIT):
The translated message is:
Hello, World!
Enter a message to encrypt/decrypt (or QUIT):
The program
#!ek9 defines module bigbook.rot13cipher defines constant UpperLetters <- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LowerLetters <- "abcdefghijklmnopqrstuvwxyz" Shift <- 13 defines class //THE input idiom of this book, copied unchanged from lib/LineInput.ek9 (the reasons are //there): read with next(), and every input loop also stops when ended() is true. LineInput stdin as Stdin: Stdin() inputEnded as Boolean: false next() <- rtn as String: stdin.next() if not rtn? inputEnded: true ended() as pure <- rtn as Boolean: Boolean(inputEnded) override operator ? as pure <- rtn as Boolean: true //THE INPUT AND OUTPUT: everything the player sees or types goes through here, worded by Say. Console stdout as Stdout: Stdout() input as LineInput: LineInput() say as Say: Say("en") Console() -> words as Say say: words showIntroduction() stdout.println(say.introduction()) stdout.println("") //The message to translate; unset when the player types QUIT or the input ends. nextMessage() <- rtn as String: String() stdout.println(say.askMessage()) if message <- input.next() if message.upperCase() <> say.quit() rtn: message //The translation is computed, not worded: it is shown as the cipher made it. The blank //line after it is layout. showTranslated() -> translated as String stdout.println(say.translated()) stdout.println(translated) stdout.println("") ended() as pure <- rtn as Boolean: input.ended() override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the cipher, with no input or output. //A letter moves 13 places round its own alphabet, so case is kept. indexOf is unset for a //character that is not in the alphabet, and the guard lets it through unchanged. rotated() as pure -> character as Character <- rtn as String: $character letters <- character.isUpperCase() <- UpperLetters : LowerLetters if position <- letters.indexOf(character) shifted <- (position + Shift) rem length letters rtn: letters.subString(shifted, shifted + 1) //13 is half of 26, so the same rotation both encrypts and decrypts. rot13() as pure -> message as String <- rtn as String: cat message | map with rotated | collect as String defines function //THE language of the words, copied unchanged from lib/Language.ek9: BIGBOOK_LANG when it //is set (fr, zh, ...), otherwise en. language() <- rtn as String: EnvVars().get("BIGBOOK_LANG") ?: "en" //THE WORDS: every word the player sees or types. defines text for "en" Say introduction() "ROT13 Cipher, by Al Sweigart al@inventwithpython.com" askMessage() "Enter a message to encrypt/decrypt (or QUIT):" quit() "QUIT" translated() "The translated message is:" defines program //ROT13 Cipher (Big Book project 61). Translates each message until the player types QUIT. //The game decides; the Console asks and tells, in the player's words. Rot13Cipher() //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) console.showIntroduction() running <- true while running and not console.ended() if message <- console.nextMessage() console.showTranslated(rot13(message)) else running: false
About this port
- Faithful to the book: an interactive loop, "Enter a message to encrypt/decrypt (or QUIT):" then "The translated message is:", until the user types QUIT. Letters rotate 13 places with their case kept; everything else passes through.
- Omitted: the optional
pyperclip"(Copied to clipboard.)" step. - Idioms:
rotatedlooks a character up withString.indexOf, which is UNSET for a character that is not in the alphabet, so the guardif position <- letters.indexOf(character)is the letter test. No-1sentinel and no hand-built indexDict.rot13is one stream,cat message | map with rotated | collect as String, and both functions areas pure: the cipher has no console in it, and the Console does the I/O.- The rotation takes its modulus from
length letters, not from a second constant 26. - QUIT and the end of input both end the loop:
if message? and ...translates only a line that was actually read.