Skip to content

Commit 4be278a

Browse files
committed
Add rail-fence-cipher
1 parent 6de11dc commit 4be278a

9 files changed

Lines changed: 208 additions & 0 deletions

File tree

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,14 @@
826826
"prerequisites": [],
827827
"difficulty": 6
828828
},
829+
{
830+
"slug": "rail-fence-cipher",
831+
"name": "Rail Fence Cipher",
832+
"uuid": "0dfe9492-7349-456a-a4fd-afde1383c393",
833+
"practices": [],
834+
"prerequisites": [],
835+
"difficulty": 6
836+
},
829837
{
830838
"slug": "say",
831839
"name": "Say",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
return {
2+
default = {
3+
ROOT = { '.' }
4+
}
5+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Instructions
2+
3+
Implement encoding and decoding for the rail fence cipher.
4+
5+
The Rail Fence cipher is a form of transposition cipher that gets its name from the way in which it's encoded.
6+
It was already used by the ancient Greeks.
7+
8+
In the Rail Fence cipher, the message is written downwards on successive "rails" of an imaginary fence, then moving up when we get to the bottom (like a zig-zag).
9+
Finally the message is then read off in rows.
10+
11+
For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE", the cipherer writes out:
12+
13+
```text
14+
W . . . E . . . C . . . R . . . L . . . T . . . E
15+
. E . R . D . S . O . E . E . F . E . A . O . C .
16+
. . A . . . I . . . V . . . D . . . E . . . N . .
17+
```
18+
19+
Then reads off:
20+
21+
```text
22+
WECRLTEERDSOEEFEAOCAIVDEN
23+
```
24+
25+
To decrypt a message you take the zig-zag shape and fill the ciphertext along the rows.
26+
27+
```text
28+
? . . . ? . . . ? . . . ? . . . ? . . . ? . . . ?
29+
. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? .
30+
. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . .
31+
```
32+
33+
The first row has seven spots that can be filled with "WECRLTE".
34+
35+
```text
36+
W . . . E . . . C . . . R . . . L . . . T . . . E
37+
. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? .
38+
. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . .
39+
```
40+
41+
Now the 2nd row takes "ERDSOEEFEAOC".
42+
43+
```text
44+
W . . . E . . . C . . . R . . . L . . . T . . . E
45+
. E . R . D . S . O . E . E . F . E . A . O . C .
46+
. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . .
47+
```
48+
49+
Leaving "AIVDEN" for the last row.
50+
51+
```text
52+
W . . . E . . . C . . . R . . . L . . . T . . . E
53+
. E . R . D . S . O . E . E . F . E . A . O . C .
54+
. . A . . . I . . . V . . . D . . . E . . . N . .
55+
```
56+
57+
If you now read along the zig-zag shape you can read the original message.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"glennj"
4+
],
5+
"files": {
6+
"solution": [
7+
"rail_fence_cipher.moon"
8+
],
9+
"test": [
10+
"rail_fence_cipher_spec.moon"
11+
],
12+
"example": [
13+
".meta/example.moon"
14+
]
15+
},
16+
"blurb": "Implement encoding and decoding for the rail fence cipher.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher"
19+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
rail_iter = (n) ->
2+
coroutine.wrap ->
3+
while true
4+
coroutine.yield i for i = 1, n-1
5+
coroutine.yield i for i = n, 2, -1
6+
7+
{
8+
encode: (n, plaintext) ->
9+
rails = [{} for _ = 1, n]
10+
next_rail = rail_iter n
11+
for char in plaintext\gmatch '.'
12+
r = next_rail!
13+
table.insert rails[r], char
14+
15+
table.concat [table.concat rail for rail in *rails]
16+
17+
decode: (n, ciphertext) ->
18+
len = #ciphertext
19+
20+
-- find the length of each rail
21+
cycle_length = 2*(n - 1)
22+
cycles = len // cycle_length
23+
lengths = [cycles * ((i == 1 or i == n) and 1 or 2) for i = 1, n]
24+
-- account for the last partial cycle
25+
next_rail = rail_iter n
26+
for i = 1, len - cycles * cycle_length
27+
lengths[next_rail!] += 1
28+
29+
-- extract the rails from the ciphertext
30+
rails = {}
31+
start = 1
32+
for r = 1, n
33+
chunk = ciphertext\sub start, start + lengths[r] - 1
34+
rails[r] = [c for c in chunk\gmatch '.']
35+
start += lengths[r]
36+
37+
next_rail = rail_iter n
38+
table.concat [table.remove rails[next_rail!], 1 for _ = 1, len]
39+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
module_name: 'RailFenceCipher',
3+
4+
generate_test: (case, level) ->
5+
lines = {
6+
"result = RailFenceCipher.#{case.property} #{case.input.rails}, #{quote case.input.msg}",
7+
"expected = #{quote case.expected}",
8+
"assert.are.equal expected, result"
9+
}
10+
table.concat [indent line, level for line in *lines], '\n'
11+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[46dc5c50-5538-401d-93a5-41102680d068]
13+
description = "encode -> encode with two rails"
14+
15+
[25691697-fbd8-4278-8c38-b84068b7bc29]
16+
description = "encode -> encode with three rails"
17+
18+
[384f0fea-1442-4f1a-a7c4-5cbc2044002c]
19+
description = "encode -> encode with ending in the middle"
20+
21+
[cd525b17-ec34-45ef-8f0e-4f27c24a7127]
22+
description = "decode -> decode with three rails"
23+
24+
[dd7b4a98-1a52-4e5c-9499-cbb117833507]
25+
description = "decode -> decode with five rails"
26+
27+
[93e1ecf4-fac9-45d9-9cd2-591f47d3b8d3]
28+
description = "decode -> decode with six rails"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
encode: (n, plaintext) ->
3+
error 'Implement me'
4+
5+
decode: (n, ciphertext) ->
6+
error 'Implement me'
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
RailFenceCipher = require 'rail_fence_cipher'
2+
3+
describe 'rail-fence-cipher', ->
4+
describe 'encode', ->
5+
it 'encode with two rails', ->
6+
result = RailFenceCipher.encode 2, 'XOXOXOXOXOXOXOXOXO'
7+
expected = 'XXXXXXXXXOOOOOOOOO'
8+
assert.are.equal expected, result
9+
10+
pending 'encode with three rails', ->
11+
result = RailFenceCipher.encode 3, 'WEAREDISCOVEREDFLEEATONCE'
12+
expected = 'WECRLTEERDSOEEFEAOCAIVDEN'
13+
assert.are.equal expected, result
14+
15+
pending 'encode with ending in the middle', ->
16+
result = RailFenceCipher.encode 4, 'EXERCISES'
17+
expected = 'ESXIEECSR'
18+
assert.are.equal expected, result
19+
20+
describe 'decode', ->
21+
pending 'decode with three rails', ->
22+
result = RailFenceCipher.decode 3, 'TEITELHDVLSNHDTISEIIEA'
23+
expected = 'THEDEVILISINTHEDETAILS'
24+
assert.are.equal expected, result
25+
26+
pending 'decode with five rails', ->
27+
result = RailFenceCipher.decode 5, 'EIEXMSMESAORIWSCE'
28+
expected = 'EXERCISMISAWESOME'
29+
assert.are.equal expected, result
30+
31+
pending 'decode with six rails', ->
32+
result = RailFenceCipher.decode 6, '133714114238148966225439541018335470986172518171757571896261'
33+
expected = '112358132134558914423337761098715972584418167651094617711286'
34+
assert.are.equal expected, result

0 commit comments

Comments
 (0)