Skip to content

Commit

Permalink
Add crypto-square exercise (#75)
Browse files Browse the repository at this point in the history
* Add crypto-square exercise

Implement tests and example solution
Generate README
Update config
  • Loading branch information
jonmcalder authored Mar 10, 2018
1 parent 7b715e3 commit 4927956
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 0 deletions.
13 changes: 13 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,19 @@
"unlocked_by": "sum-of-multiples",
"uuid": "2f7b585f-f82f-4dca-9588-2df3250184e6"
},
{
"uuid": "82c43534-86ff-4b5a-9519-1501a15721bf",
"slug": "crypto-square",
"core": false,
"unlocked_by": "rotational-cipher",
"difficulty": 4,
"topics": [
"strings",
"text_formatting",
"regular_expressions",
"transforming"
]
},
{
"uuid": "7cad82d6-0db6-d680-4133-81d850d6e7a925454a0",
"slug": "diamond",
Expand Down
84 changes: 84 additions & 0 deletions exercises/crypto-square/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Crypto Square

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 downcased.

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

> If man was meant to stay on the ground, god would have given us roots.
is normalized to:

> ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots
The plaintext should be organized in to a rectangle. The size of the
rectangle (`r x c`) should be decided by the length of the message,
such that `c >= r` and `c - r <= 1`, where `c` is the number of columns
and `r` is the number of rows.

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

```plain
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:

```plain
imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau
```

Output the encoded text in chunks. Phrases that fill perfect squares
`(r X r)` should be output in `r`-length chunks separated by spaces.
Imperfect squares will have `n` empty spaces. Those spaces should be distributed evenly across the last `n` rows.

```plain
imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau
```

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

```plain
imtgdvs
fearwer
mayoogo
anouuio
ntnnlvt
wttddes
aohghn
sseoau
```

## Installation
See [this guide](https://github.com/exercism/xr/blob/master/docs/INSTALLATION.md) for instructions on how to setup your local R environment.

## How to implement your solution
In each problem folder, there is a file named `<exercise_name>.R` containing a function that returns a `NULL` value. Place your implementation inside the body of the function.

## How to run tests
Inside of RStudio, simply execute the `test_<exercise_name>.R` script. This can be conveniently done with [testthat's `auto_test` function](https://www.rdocumentation.org/packages/testthat/topics/auto_test). Because exercism code and tests are in the same folder, use this same path for both `code_path` and `test_path` parameters. On the command-line, you can also run `Rscript test_<exercise_name>.R`.

## Source

J Dalbey's Programming Practice problems [http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html](http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html)

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
15 changes: 15 additions & 0 deletions exercises/crypto-square/crypto-square.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
normalized_plaintext <- function(input) {

}

plaintext_segments <- function(input) {

}

encoded <- function(input) {

}

ciphertext <- function(input) {

}
47 changes: 47 additions & 0 deletions exercises/crypto-square/example.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
library(magrittr)

normalized_plaintext <- function(input) {

gsub("[^[:alnum:]]", "", input) %>%
tolower()

}

map_plaintext_to_matrix <- function(input) {

txt <- normalized_plaintext(input)
width <- max(1, nchar(txt) %>% sqrt() %>% ceiling())
ext_len <- ceiling(nchar(txt) / width) * width
txt <- sprintf(paste0("%-", ext_len, "s"), txt)

strsplit(txt, "") %>%
unlist() %>%
matrix(ncol = width, byrow = TRUE)

}

plaintext_segments <- function(input) {

if (!nzchar(input)) return("")
map_plaintext_to_matrix(input) %>%
apply(MARGIN = 1, paste, collapse = "") %>%
trimws()

}

encoded <- function(input) {

map_plaintext_to_matrix(input) %>%
apply(MARGIN = 2, paste, collapse = "") %>%
trimws() %>%
paste(collapse = "")

}

ciphertext <- function(input) {

map_plaintext_to_matrix(input) %>%
apply(MARGIN = 2, paste, collapse = "") %>%
paste(collapse = " ")

}
65 changes: 65 additions & 0 deletions exercises/crypto-square/test_crypto-square.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
source("./crypto-square.R")
library(testthat)

test_that("Lowercase", {
expect_equal(normalized_plaintext("Hello"), "hello")
})

test_that("Remove spaces", {
expect_equal(normalized_plaintext("Hi there"), "hithere")
})

test_that("Remove punctuation", {
expect_equal(normalized_plaintext("@1, 2%, 3 Go!"), "123go")
})

test_that("empty plaintext results in an empty rectangle", {
expect_equal(plaintext_segments(""), "")
})

test_that("4 character plaintext results in an 2x2 rectangle", {
expect_equal(plaintext_segments("Ab Cd"), c("ab", "cd"))
})

test_that("9 character plaintext results in an 3x3 rectangle", {
expect_equal(plaintext_segments("This is fun!"), c("thi", "sis", "fun"))
})

test_that("54 character plaintext results in an 8x7 rectangle", {
expect_equal(plaintext_segments(
"If man was meant to stay on the ground, god would have given us roots."),
c("ifmanwas",
"meanttos",
"tayonthe",
"groundgo",
"dwouldha",
"vegivenu",
"sroots"))
})

test_that("empty plaintext results in an empty encode", {
expect_equal(encoded(""), "")
})

test_that("Non-empty plaintext results in the combined plaintext segments", {
expect_equal(encoded(
"If man was meant to stay on the ground, god would have given us roots."),
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau")
})

test_that("empty plaintext results in an empty ciphertext", {
expect_equal(ciphertext(""), "")
})

test_that("9 character plaintext results in 3 chunks of 3 characters", {
expect_equal(ciphertext("This is fun!"), "tsf hiu isn")
})

test_that("54 character plaintext results in 7 chunks, the last two padded with
spaces", {
expect_equal(ciphertext(
"If man was meant to stay on the ground, god would have given us roots."),
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ")
})

message("All tests passed for exercise: crypto-square")

0 comments on commit 4927956

Please sign in to comment.