diff --git a/config.json b/config.json index f50b61dc..e08040d4 100644 --- a/config.json +++ b/config.json @@ -736,6 +736,13 @@ ], "prerequisites": [], "difficulty": 4 + },{ + "slug": "rail-fence-cipher", + "name": "Rail Fence Cipher", + "uuid": "33012310-6494-47ff-a29f-6cc168fe12b4", + "practices": [], + "prerequisites": [], + "difficulty": 4 }, { "slug": "all-your-base", diff --git a/exercises/practice/rail-fence-cipher/.docs/instructions.md b/exercises/practice/rail-fence-cipher/.docs/instructions.md new file mode 100644 index 00000000..e311de6c --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.docs/instructions.md @@ -0,0 +1,57 @@ +# Instructions + +Implement encoding and decoding for the rail fence cipher. + +The Rail Fence cipher is a form of transposition cipher that gets its name from the way in which it's encoded. +It was already used by the ancient Greeks. + +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). +Finally the message is then read off in rows. + +For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE", the cipherer writes out: + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +Then reads off: + +```text +WECRLTEERDSOEEFEAOCAIVDEN +``` + +To decrypt a message you take the zig-zag shape and fill the ciphertext along the rows. + +```text +? . . . ? . . . ? . . . ? . . . ? . . . ? . . . ? +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +The first row has seven spots that can be filled with "WECRLTE". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Now the 2nd row takes "ERDSOEEFEAOC". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Leaving "AIVDEN" for the last row. + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +If you now read along the zig-zag shape you can read the original message. diff --git a/exercises/practice/rail-fence-cipher/.meta/RailFenceCipher.example.ps1 b/exercises/practice/rail-fence-cipher/.meta/RailFenceCipher.example.ps1 new file mode 100644 index 00000000..7962771e --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/RailFenceCipher.example.ps1 @@ -0,0 +1,101 @@ +#Adapt from the awesome solution: +#https://exercism.org/tracks/python/exercises/rail-fence-cipher/solutions/tcarobruce +Function Invoke-Encode() { + <# + .SYNOPSIS + Implement encoding for the rail fence cipher. + + .DESCRIPTION + 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). + Finally the message is then read off in rows. + + .PARAMETER Message + The message to be encoded. + + .PARAMETER Rails + The number of rails to be use to construct the rail fence. + + .EXAMPLE + Invoke-Encode -Message "EXERCISM" -Rails 2 + Returns: "EECSXRIM" + #> + [CmdletBinding()] + Param( + [string]$Message, + [int]$Rails + ) + $pattern = RailPattern -Text $Message -Number $Rails + $zipped = Zipper -Values $Message.ToCharArray() -Indexes $pattern + + $encoded = $zipped | Sort-Object -Property Index -Stable | ForEach-Object {$_.Value} + -join $encoded +} + +Function Invoke-Decode() { + <# + .SYNOPSIS + Implement decoding for the rail fence cipher. + + .DESCRIPTION + + .PARAMETER CipherText + The ciphertext to be decoded. + + .PARAMETER Rails + The number of rails to be use to construct the rail fence. + + .EXAMPLE + Invoke-Decode -Ciphertext "EECSXRIM" -Rails 2 + Returns: "EXERCISM" + #> + [CmdletBinding()] + Param( + [string]$Ciphertext, + [int]$Rails + ) + $pattern = RailPattern -Text $Ciphertext -Number $Rails + # sorted index based on pattern + $indexes = (Zipper -Values (0..($Ciphertext.Length-1)) -Indexes $pattern) | Sort-Object -Property Index -Stable | ForEach-Object {$_.Value} + + $decode = @($null) * $Ciphertext.Length + for ($i = 0; $i -lt $Ciphertext.Length; $i++) { + $decode[$indexes[$i]] = $Ciphertext[$i] + } + -join $decode +} + +function RailPattern() { + <# + .DESCRIPTION + Helper function to get the pattern of the rail fence. This will match the full length of the input text. + For example: 3 rails will yield: 0,1,2,1,0... + #> + param ( + [string]$Text, + [int]$Number + ) + $pattern = 0..($Number-1) + ($Number-2)..1 + + #get the pattern to match the text + $fullPattern = $pattern * [math]::Ceiling($Text.Length/$pattern.Count) + $fullPattern[0..($Text.Length-1)] +} + +function Zipper() { + <# + .DESCRIPTION + Helper function act as a zipper for two collections. + Return one collection with custom objects + #> + param ( + [object[]]$Values, + [object[]]$Indexes + ) + $zipped = 0..($Values.Count-1) | ForEach-Object { + [PSCustomObject]@{ + Value = $Values[$_] + Index = $Indexes[$_] + } + } + $zipped +} \ No newline at end of file diff --git a/exercises/practice/rail-fence-cipher/.meta/config.json b/exercises/practice/rail-fence-cipher/.meta/config.json new file mode 100644 index 00000000..e8c6a910 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "glaxxie" + ], + "files": { + "solution": [ + "RailFenceCipher.ps1" + ], + "test": [ + "RailFenceCipher.tests.ps1" + ], + "example": [ + ".meta/RailFenceCipher.example.ps1" + ] + }, + "blurb": "Implement encoding and decoding for the rail fence cipher.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher" +} diff --git a/exercises/practice/rail-fence-cipher/.meta/tests.toml b/exercises/practice/rail-fence-cipher/.meta/tests.toml new file mode 100644 index 00000000..dfc5e16b --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/tests.toml @@ -0,0 +1,28 @@ +# 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. + +[46dc5c50-5538-401d-93a5-41102680d068] +description = "encode -> encode with two rails" + +[25691697-fbd8-4278-8c38-b84068b7bc29] +description = "encode -> encode with three rails" + +[384f0fea-1442-4f1a-a7c4-5cbc2044002c] +description = "encode -> encode with ending in the middle" + +[cd525b17-ec34-45ef-8f0e-4f27c24a7127] +description = "decode -> decode with three rails" + +[dd7b4a98-1a52-4e5c-9499-cbb117833507] +description = "decode -> decode with five rails" + +[93e1ecf4-fac9-45d9-9cd2-591f47d3b8d3] +description = "decode -> decode with six rails" diff --git a/exercises/practice/rail-fence-cipher/RailFenceCipher.ps1 b/exercises/practice/rail-fence-cipher/RailFenceCipher.ps1 new file mode 100644 index 00000000..846d2142 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/RailFenceCipher.ps1 @@ -0,0 +1,53 @@ +Function Invoke-Encode() { + <# + .SYNOPSIS + Implement encoding for the rail fence cipher. + + .DESCRIPTION + 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). + Finally the message is then read off in rows. + + .PARAMETER Message + The message to be encoded. + + .PARAMETER Rails + The number of rails to be use to construct the rail fence. + + .EXAMPLE + Invoke-Encode -Message "EXERCISM" -Rails 2 + Returns: "EECSXRIM" + #> + [CmdletBinding()] + Param( + [string]$Message, + [int]$Rails + ) + + Throw "Please implement this function" +} + +Function Invoke-Decode() { + <# + .SYNOPSIS + Implement decoding for the rail fence cipher. + + .DESCRIPTION + + .PARAMETER CipherText + The ciphertext to be decoded. + + .PARAMETER Rails + The number of rails to be use to construct the rail fence. + + .EXAMPLE + Invoke-Decode -Ciphertext "EECSXRIM" -Rails 2 + Returns: "EXERCISM" + #> + [CmdletBinding()] + Param( + [string]$Ciphertext, + [int]$Rails + ) + + Throw "Please implement this function" +} \ No newline at end of file diff --git a/exercises/practice/rail-fence-cipher/RailFenceCipher.tests.ps1 b/exercises/practice/rail-fence-cipher/RailFenceCipher.tests.ps1 new file mode 100644 index 00000000..df2c980e --- /dev/null +++ b/exercises/practice/rail-fence-cipher/RailFenceCipher.tests.ps1 @@ -0,0 +1,51 @@ +BeforeAll { + . "./RailFenceCipher.ps1" +} + +Describe "RailFenceCipher test cases" { + Context "encoding" { + It "encode with two rails" { + $got = Invoke-Encode -Message "XOXOXOXOXOXOXOXOXO" -Rails 2 + $want = "XXXXXXXXXOOOOOOOOO" + + $got | Should -BeExactly $want + } + + It "encode with three rails" { + $got = Invoke-Encode -Message "WEAREDISCOVEREDFLEEATONCE" -Rails 3 + $want = "WECRLTEERDSOEEFEAOCAIVDEN" + + $got | Should -BeExactly $want + } + + It "encode with ending in the middle" { + $got = Invoke-Encode -Message "EXERCISES" -Rails 4 + $want = "ESXIEECSR" + + $got | Should -BeExactly $want + } + } + + Context "decoding" { + It "decode with three rails" { + $got = Invoke-Decode -Ciphertext "TEITELHDVLSNHDTISEIIEA" -Rails 3 + $want = "THEDEVILISINTHEDETAILS" + + $got | Should -BeExactly $want + } + + It "decode with five rails" { + $got = Invoke-Decode -Ciphertext "EIEXMSMESAORIWSCE" -Rails 5 + $want = "EXERCISMISAWESOME" + + $got | Should -BeExactly $want + } + + It "decode with six rails" { + $got = Invoke-Decode -Ciphertext "133714114238148966225439541018335470986172518171757571896261" -Rails 6 + $want = "112358132134558914423337761098715972584418167651094617711286" + + $got | Should -BeExactly $want + } + } +}