Skip to content

Commit

Permalink
Add crypto-square exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
ageron committed Oct 10, 2024
1 parent 27df90f commit e006925
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,14 @@
"prerequisites": [],
"difficulty": 3
},
{
"slug": "crypto-square",
"name": "Crypto Square",
"uuid": "b3e52a1c-d51f-4a86-8e3c-6bc2c89daa97",
"practices": [],
"prerequisites": [],
"difficulty": 1
},
{
"slug": "diamond",
"name": "Diamond",
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 "
```
27 changes: 27 additions & 0 deletions exercises/practice/crypto-square/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module [ciphertext]

ciphertext : Str -> Result Str _
ciphertext = \text ->
chars =
text
|> Str.toUtf8
|> List.joinMap \char ->
if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') then
[char]
else if char >= 'A' && char <= 'Z' then
[char - 'A' + 'a']
else
[]

length = List.len chars
width = length |> Num.toF64 |> Num.sqrt |> Num.ceiling |> Num.toU64
rows = chars |> List.chunksOf width

List.range { start: At 0, end: Before width }
|> List.map \column ->
rows
|> List.map \row ->
row |> List.get column |> Result.withDefault ' '
|> List.intersperse [' ']
|> List.join
|> Str.fromUtf8
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": [
"ageron"
],
"files": {
"solution": [
"CryptoSquare.roc"
],
"test": [
"crypto-square-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"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"
}
15 changes: 15 additions & 0 deletions exercises/practice/crypto-square/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.canonical_ref() }}
{{ macros.header() }}

import {{ exercise | to_pascal }} exposing [{{ cases[0]["property"] | to_camel }}]

{% for case in cases -%}
# {{ case["description"] }}
expect
text = {{ case["input"]["plaintext"] | to_roc }}
result = {{ case["property"] | to_camel }} text
expected = Ok {{ case["expected"] | to_roc }}
result == expected

{% endfor %}
34 changes: 34 additions & 0 deletions exercises/practice/crypto-square/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via 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"
5 changes: 5 additions & 0 deletions exercises/practice/crypto-square/CryptoSquare.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module [ciphertext]

ciphertext : Str -> Result Str _
ciphertext = \text ->
crash "Please implement the 'ciphertext' function"
68 changes: 68 additions & 0 deletions exercises/practice/crypto-square/crypto-square-test.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# These tests are auto-generated with test data from:
# https://github.com/exercism/problem-specifications/tree/main/exercises/crypto-square/canonical-data.json
# File last updated on 2024-10-10
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

main =
Task.ok {}

import CryptoSquare exposing [ciphertext]

# empty plaintext results in an empty ciphertext
expect
text = ""
result = ciphertext text
expected = Ok ""
result == expected

# normalization results in empty plaintext
expect
text = "... --- ..."
result = ciphertext text
expected = Ok ""
result == expected

# Lowercase
expect
text = "A"
result = ciphertext text
expected = Ok "a"
result == expected

# Remove spaces
expect
text = " b "
result = ciphertext text
expected = Ok "b"
result == expected

# Remove punctuation
expect
text = "@1,%!"
result = ciphertext text
expected = Ok "1"
result == expected

# 9 character plaintext results in 3 chunks of 3 characters
expect
text = "This is fun!"
result = ciphertext text
expected = Ok "tsf hiu isn"
result == expected

# 8 character plaintext results in 3 chunks, the last one with a trailing space
expect
text = "Chill out."
result = ciphertext text
expected = Ok "clu hlt io "
result == expected

# 54 character plaintext results in 7 chunks, the last two with trailing spaces
expect
text = "If man was meant to stay on the ground, god would have given us roots."
result = ciphertext text
expected = Ok "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
result == expected

0 comments on commit e006925

Please sign in to comment.