Skip to content

Commit

Permalink
feat: Add Crypto Square (closes #137) (#138)
Browse files Browse the repository at this point in the history
  • Loading branch information
keiravillekode authored Jul 14, 2023
1 parent f22a298 commit 0bdc4f7
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,14 @@
"prerequisites": [],
"difficulty": 1
},
{
"slug": "crypto-square",
"name": "Crypto Square",
"uuid": "39431693-b130-4e1a-a4c1-781eb0226e39",
"practices": [],
"prerequisites": [],
"difficulty": 4
},
{
"uuid": "dc522fc7-94e5-4527-ac6e-e713ac4a5ec2",
"slug": "isogram",
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": [
"keiravillekode"
],
"files": {
"solution": [
"crypto-square.v"
],
"test": [
"run_test.v"
],
"example": [
".meta/example.v"
]
},
"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"
}
47 changes: 47 additions & 0 deletions exercises/practice/crypto-square/.meta/example.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module main

fn columns(n int) int {
mut c := 0
for c * c < n {
c += 1
}
return c
}

fn rows(n int, c int) int {
mut r := c - 1
if r * c < n {
r += 1
}
assert r * c >= n
return r
}

fn ciphertext(plaintext string) string {
mut normalized := []u8{cap: plaintext.len}
for ch in plaintext.to_lower() {
if ch.is_alnum() {
normalized << ch
}
}

n := normalized.len
if n <= 1 {
return normalized.bytestr()
}
c := columns(n)
r := rows(n, c)

mut buffer := []u8{len: (r + 1) * c - 1, init: u8(` `)}
mut index := 0
for i in 0 .. r {
for j in 0 .. c {
buffer[j * (r + 1) + i] = normalized[index]
index += 1
if index == n {
break
}
}
}
return buffer.bytestr()
}
27 changes: 27 additions & 0 deletions exercises/practice/crypto-square/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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"

[aad04a25-b8bb-4304-888b-581bea8e0040]
description = "normalization results in empty plaintext"

[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"
4 changes: 4 additions & 0 deletions exercises/practice/crypto-square/crypto-square.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module main

fn ciphertext(plaintext string) string {
}
49 changes: 49 additions & 0 deletions exercises/practice/crypto-square/run_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module main

fn test_empty_plaintext_results_in_an_empty_ciphertext() {
phrase := ''
expect := ''
assert ciphertext(phrase) == expect
}

fn test_normalization_results_in_empty_plaintext() {
phrase := '... --- ...'
expect := ''
assert ciphertext(phrase) == expect
}

fn test_lowercase() {
phrase := 'A'
expect := 'a'
assert ciphertext(phrase) == expect
}

fn test_remove_spaces() {
phrase := ' b '
expect := 'b'
assert ciphertext(phrase) == expect
}

fn test_remove_punctuation() {
phrase := '@1,%!'
expect := '1'
assert ciphertext(phrase) == expect
}

fn test_9_character_plaintext_results_in_3_chunks_of_3_characters() {
phrase := 'This is fun!'
expect := 'tsf hiu isn'
assert ciphertext(phrase) == expect
}

fn test_8_character_plaintext_results_in_3_chunks_the_last_one_with_a_trailing_space() {
phrase := 'Chill out.'
expect := 'clu hlt io '
assert ciphertext(phrase) == expect
}

fn test_54_character_plaintext_results_in_7_chunks_the_last_two_with_trailing_spaces() {
phrase := 'If man was meant to stay on the ground, god would have given us roots.'
expect := 'imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau '
assert ciphertext(phrase) == expect
}

0 comments on commit 0bdc4f7

Please sign in to comment.