Friday, January 29, 2016

Kata: Alphabet Cipher in Clojure

I just redid the Alphabet Cipher kata from the fantastic Carine Meier's Living Clojure book.

I had done it some time ago with Eloi Poch and Rafa Gómez (thanks guys I had a great time) at a Clojure Developers Barcelona event.

These are the tests using Midje:

(ns alphabet-cipher.coder-test
(:require
[midje.sweet :refer :all]
[alphabet-cipher.coder :refer :all]))
(facts
"about Alphabet cipher"
(fact
"can encode given a secret keyword"
(encode "v" "m") => "h"
(encode "vigilance"
"meetmeontuesdayeveningatseven") => "hmkbxebpxpmyllyrxiiqtoltfgzzv"
(encode "scones"
"meetmebythetree") => "egsgqwtahuiljgs")
(fact
"can decode an encrypted message given a secret keyword"
(decode "v" "h") => "m"
(decode "vigilance"
"hmkbxebpxpmyllyrxiiqtoltfgzzv") => "meetmeontuesdayeveningatseven"
(decode "scones"
"egsgqwtahuiljgs") => "meetmebythetree")
(fact
"can extract the secret keyword given an encrypted message and the original message"
(decipher "o" "t") => "v"
(decipher "opkyf" "thequ") => "vigil"
(decipher "opkyfipmfmwcvqoklyhxywgeecpvhelzg"
"thequickbrownfoxjumpsoveralazydog") => "vigilance"
(decipher "hcqxqqtqljmlzhwiivgbsapaiwcenmyu"
"packmyboxwithfivedozenliquorjugs") => "scones"))
and this is the final code:

(ns alphabet-cipher.coder)
(declare encode)
(def ^:private alphabet
(map char (range (int \a) (inc (int \z)))))
(defn- rotate-str [str n]
(take (count str) (drop n (cycle str))))
(defn- rotate-alphabet-to-start-with [c]
(rotate-str alphabet (.indexOf alphabet c)))
(defn- encode-char [keyword-c msg-c]
(nth (rotate-alphabet-to-start-with keyword-c)
(.indexOf alphabet msg-c)))
(defn- decode-char [keyword-c c]
(nth alphabet
(.indexOf (rotate-alphabet-to-start-with keyword-c) c)))
(defn- decipher-char [encoded-c msg-c]
(nth alphabet
(.indexOf (rotate-alphabet-to-start-with msg-c) encoded-c)))
(defn- possible-keywords [enlarged-keyword]
(map #(take % enlarged-keyword)
(range 1 (inc (count enlarged-keyword)))))
(defn- find-first [pred coll]
(some #(when (pred %) %) coll))
(defn- decipher-enlarged-keyword [encoded-msg msg]
(apply str (map decipher-char encoded-msg msg)))
(defn encode [keyword msg]
(apply str (map encode-char (cycle keyword) msg)))
(defn decode [keyword encoded-msg]
(apply str (map decode-char (cycle keyword) encoded-msg)))
(defn decipher [encoded-msg msg]
(->> (decipher-enlarged-keyword encoded-msg msg)
possible-keywords
(find-first #(= (encode % msg) encoded-msg))
(apply str)))
I used a mix of TDD and REPL-driven development committing after each green and each refactoring.
What I did was tinkering in the REPL to get rough versions of encode, decode and decipher functions. This kind of tinkering is getting more and more fun, the more I get used to working in the REPL.
Once I had the rough version I just copied it directly on the code, stabilized it with some tests and started to refactor it until it seemed a bit more readable to me.

See all the commits here if you want to follow the process.

You can find all the code on GitHub.