Skip to content
This repository has been archived by the owner on May 30, 2023. It is now read-only.

Commit

Permalink
Add crypto-square exercise (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikSchierboom authored May 6, 2023
1 parent b890690 commit f11c044
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,16 @@
"difficulty": 5,
"topics": null
},
{
"slug": "crypto-square",
"name": "Crypto Square",
"uuid": "6206d9b0-454e-4cf5-9c8e-dade7485e909",
"practices": [],
"prerequisites": [
"strings"
],
"difficulty": 4
},
{
"slug": "complex-numbers",
"name": "Complex Numbers",
Expand Down
71 changes: 71 additions & 0 deletions exercises/practice/crypto-square/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Instructions

Implement the classic method for composing secret messages called a square code.

Given an English text, output the encoded version of that text.

First, the input is normalized: the spaces and punctuation are removed from the English text and the message is down-cased.

Then, the normalized characters are broken into rows.
These rows can be regarded as forming a rectangle when printed with intervening newlines.

For example, the sentence

```text
"If man was meant to stay on the ground, god would have given us roots."
```

is normalized to:

```text
"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
```

The plaintext should be organized into a rectangle as square as possible.
The size of the rectangle should be decided by the length of the message.

If `c` is the number of columns and `r` is the number of rows, then for the rectangle `r` x `c` find the smallest possible integer `c` such that:

- `r * c >= length of message`,
- and `c >= r`,
- and `c - r <= 1`.

Our normalized text is 54 characters long, dictating a rectangle with `c = 8` and `r = 7`:

```text
"ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots "
```

The coded message is obtained by reading down the columns going left to right.

The message above is coded as:

```text
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
```

Output the encoded text in chunks that fill perfect rectangles `(r X c)`, with `c` chunks of `r` length, separated by spaces.
For phrases that are `n` characters short of the perfect rectangle, pad each of the last `n` chunks with a single trailing space.

```text
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
```

Notice that were we to stack these, we could visually decode the ciphertext back in to the original message:

```text
"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "
```
19 changes: 19 additions & 0 deletions exercises/practice/crypto-square/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"ErikSchierboom"
],
"files": {
"solution": [
"src/crypto_square.cljs"
],
"test": [
"test/crypto_square_test.cljs"
],
"example": [
".meta/src/example.cljs"
]
},
"blurb": "Implement the classic method for composing secret messages called a square code.",
"source": "J Dalbey's Programming Practice problems",
"source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html"
}
24 changes: 24 additions & 0 deletions exercises/practice/crypto-square/.meta/src/example.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
(ns crypto-square
(:require [clojure.string :as str]))

(defn normalize-plaintext [plaintext]
(str/lower-case (str/replace plaintext #"[^\w]" "")))

(defn square-size [plaintext]
(int (Math/ceil (Math/sqrt (count (normalize-plaintext plaintext))))))

(defn plaintext-segments [plaintext]
(let [normalized-plaintext (normalize-plaintext plaintext)
size (square-size normalized-plaintext)]
(map #(apply str %) (partition size size nil normalized-plaintext))))

(defn- padded-segments [plaintext pad]
(let [segments (plaintext-segments plaintext)
r (count (first segments))]
(map #(concat % (take (- r (count %)) (repeat pad))) segments)))

(defn ciphertext [plaintext]
(apply str (apply mapcat vector (padded-segments plaintext nil))))

(defn normalize-ciphertext [plaintext]
(str/join " " (map #(apply str %) (apply map vector (padded-segments plaintext \ )))))
24 changes: 24 additions & 0 deletions exercises/practice/crypto-square/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This is an auto-generated file. Regular comments will be removed when this
# file is regenerated. Regenerating will not touch any manually added keys,
# so comments can be added in a "comment" key.

[407c3837-9aa7-4111-ab63-ec54b58e8e9f]
description = "empty plaintext results in an empty ciphertext"

[64131d65-6fd9-4f58-bdd8-4a2370fb481d]
description = "Lowercase"

[63a4b0ed-1e3c-41ea-a999-f6f26ba447d6]
description = "Remove spaces"

[1b5348a1-7893-44c1-8197-42d48d18756c]
description = "Remove punctuation"

[8574a1d3-4a08-4cec-a7c7-de93a164f41a]
description = "9 character plaintext results in 3 chunks of 3 characters"

[a65d3fa1-9e09-43f9-bcec-7a672aec3eae]
description = "8 character plaintext results in 3 chunks, the last one with a trailing space"

[fbcb0c6d-4c39-4a31-83f6-c473baa6af80]
description = "54 character plaintext results in 7 chunks, the last two with trailing spaces"
10 changes: 10 additions & 0 deletions exercises/practice/crypto-square/deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{:deps
{org.clojure/clojure {:mvn/version "1.10.1"}
org.clojure/clojurescript {:mvn/version "1.10.773"}}

:aliases
{:test
{:extra-paths ["test"]
:extra-deps
{olical/cljs-test-runner {:mvn/version "3.8.0"}}
:main-opts ["-m" "cljs-test-runner.main"]}}}
21 changes: 21 additions & 0 deletions exercises/practice/crypto-square/src/crypto_square.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(ns crypto-square)

(defn normalize-plaintext [] ;; <- arglist goes here
;; your code goes here
)

(defn square-size [] ;; <- arglist goes here
;; your code goes here
)

(defn plaintext-segments [] ;; <- arglist goes here
;; your code goes here
)

(defn ciphertext [] ;; <- arglist goes here
;; your code goes here
)

(defn normalize-ciphertext [] ;; <- arglist goes here
;; your code goes here
)
48 changes: 48 additions & 0 deletions exercises/practice/crypto-square/test/crypto_square_test.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
(ns crypto-square-test
(:require [clojure.test :refer [deftest is]]
crypto-square))

(deftest normalize-splunk
(is (= "splunk" (crypto-square/normalize-plaintext "s#!@$%plunk"))))
(deftest normalize-with-punctuation
(is (= "123go" (crypto-square/normalize-plaintext "1, 2, 3 GO!"))))

(deftest square-2
(is (= 2 (crypto-square/square-size "1234"))))
(deftest square-3
(is (= 3 (crypto-square/square-size "123456789"))))
(deftest square-4
(is (= 4 (crypto-square/square-size "123456789abc"))))

(deftest segments
(is (= ["neverv", "exthin", "eheart", "withid", "lewoes"]
(crypto-square/plaintext-segments "Never vex thine heart with idle woes."))))
(deftest segments-2
(is (= ["zomg", "zomb", "ies"]
(crypto-square/plaintext-segments "ZOMG! ZOMBIES!!!"))))

(deftest cipher-1
(is (= "tasneyinicdsmiohooelntuillibsuuml"
(crypto-square/ciphertext "Time is an illusion. Lunchtime doubly so."))))
(deftest cipher-2
(is (= "wneiaweoreneawssciliprerlneoidktcms"
(crypto-square/ciphertext "We all know interspecies romance is weird."))))
(deftest cipher-3
(is (= "vrel aepe mset paoo irpo"
(crypto-square/normalize-ciphertext "Vampires are people too!"))))
(deftest cipher-4
(is (= "msemo aanin dnin ndla etlt shui "
(crypto-square/normalize-ciphertext "Madness, and then illumination."))))
(deftest cipher-5
(is (= (str "ageihdsednsh lsagtoonaepe lannswnccair hrditeaetnrh "
"ueethdnatoio mbqyewdnotto aouayicdwhod nranatosaef "
"bnldrhnhrrb efirersodir irnieecusno nedgnailoat ")
(let [plaintext (str "All human beings are born free "
"and equal in dignity and rights. "
"They are endowed with reason and conscience "
"and should act towards one another "
"in a spirit of brotherhood.")]
(crypto-square/normalize-ciphertext plaintext)))))
(deftest cipher-6
(is (= "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
(crypto-square/normalize-ciphertext "If man was meant to stay on the ground, god would have given us roots."))))

0 comments on commit f11c044

Please sign in to comment.