Skip to content

Commit 7bd7330

Browse files
authored
Add Rotational Cipher Exercise (#635)
1 parent 199cc03 commit 7bd7330

File tree

8 files changed

+272
-0
lines changed

8 files changed

+272
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,14 @@
11951195
"practices": [],
11961196
"prerequisites": [],
11971197
"difficulty": 3
1198+
},
1199+
{
1200+
"slug": "rotational-cipher",
1201+
"name": "Rotational Cipher",
1202+
"uuid": "61add6ab-9eaa-4fbc-b060-8ac44f47fad7",
1203+
"practices": [],
1204+
"prerequisites": [],
1205+
"difficulty": 3
11981206
}
11991207
]
12001208
},
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
~~~~exercism/note
2+
Do not use PHP functions like `str_rot13()` to solve this exercise! Only use basic string and character manipulation functions.
3+
~~~~
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Instructions
2+
3+
Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.
4+
5+
The Caesar cipher is a simple shift cipher that relies on transposing all the letters in the alphabet using an integer key between `0` and `26`.
6+
Using a key of `0` or `26` will always yield the same output due to modular arithmetic.
7+
The letter is shifted for as many values as the value of the key.
8+
9+
The general notation for rotational ciphers is `ROT + <key>`.
10+
The most commonly used rotational cipher is `ROT13`.
11+
12+
A `ROT13` on the Latin alphabet would be as follows:
13+
14+
```text
15+
Plain: abcdefghijklmnopqrstuvwxyz
16+
Cipher: nopqrstuvwxyzabcdefghijklm
17+
```
18+
19+
It is stronger than the Atbash cipher because it has 27 possible keys, and 25 usable keys.
20+
21+
Ciphertext is written out in the same formatting as the input including spaces and punctuation.
22+
23+
## Examples
24+
25+
- ROT5 `omg` gives `trl`
26+
- ROT0 `c` gives `c`
27+
- ROT26 `Cool` gives `Cool`
28+
- ROT13 `The quick brown fox jumps over the lazy dog.` gives `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.`
29+
- ROT13 `Gur dhvpx oebja sbk whzcf bire gur ynml qbt.` gives `The quick brown fox jumps over the lazy dog.`
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"tomasnorre"
4+
],
5+
"files": {
6+
"solution": [
7+
"RotationalCipher.php"
8+
],
9+
"test": [
10+
"RotationalCipherTest.php"
11+
],
12+
"example": [
13+
".meta/example.php"
14+
]
15+
},
16+
"blurb": "Create an implementation of the rotational cipher, also sometimes called the Caesar cipher.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Caesar_cipher"
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class RotationalCipher
6+
{
7+
public function rotate(string $text, int $shift): string
8+
{
9+
return implode('', array_map(static function ($c) use ($shift) {
10+
$isUpper = preg_match('/[A-Z]/', $c);
11+
$isAlpha = preg_match('/[a-z]/i', $c);
12+
$charShift = $isUpper ? ord('A') : ord('a');
13+
14+
if ($isAlpha) {
15+
$shiftedCharCode = (ord($c) - $charShift + $shift) % 26 + $charShift;
16+
return chr($shiftedCharCode);
17+
}
18+
19+
return $c;
20+
}, str_split($text)));
21+
}
22+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
[74e58a38-e484-43f1-9466-877a7515e10f]
13+
description = "rotate a by 0, same output as input"
14+
15+
[7ee352c6-e6b0-4930-b903-d09943ecb8f5]
16+
description = "rotate a by 1"
17+
18+
[edf0a733-4231-4594-a5ee-46a4009ad764]
19+
description = "rotate a by 26, same output as input"
20+
21+
[e3e82cb9-2a5b-403f-9931-e43213879300]
22+
description = "rotate m by 13"
23+
24+
[19f9eb78-e2ad-4da4-8fe3-9291d47c1709]
25+
description = "rotate n by 13 with wrap around alphabet"
26+
27+
[a116aef4-225b-4da9-884f-e8023ca6408a]
28+
description = "rotate capital letters"
29+
30+
[71b541bb-819c-4dc6-a9c3-132ef9bb737b]
31+
description = "rotate spaces"
32+
33+
[ef32601d-e9ef-4b29-b2b5-8971392282e6]
34+
description = "rotate numbers"
35+
36+
[32dd74f6-db2b-41a6-b02c-82eb4f93e549]
37+
description = "rotate punctuation"
38+
39+
[9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9]
40+
description = "rotate all letters"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class RotationalCipher
28+
{
29+
public function rotate(string $text, int $shift): string
30+
{
31+
throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__));
32+
}
33+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class RotationalCipherTest extends PHPUnit\Framework\TestCase
6+
{
7+
private RotationalCipher $rotationalCipher;
8+
9+
public static function setUpBeforeClass(): void
10+
{
11+
require_once 'RotationalCipher.php';
12+
}
13+
14+
protected function setUp(): void
15+
{
16+
$this->rotationalCipher = new RotationalCipher();
17+
}
18+
19+
/**
20+
* uuid: 74e58a38-e484-43f1-9466-877a7515e10f
21+
*/
22+
public function testRotateAByZero(): void
23+
{
24+
$expected = 'a';
25+
$actual = $this->rotationalCipher->rotate('a', 0);
26+
$this->assertEquals($expected, $actual);
27+
}
28+
29+
/**
30+
* uuid: 7ee352c6-e6b0-4930-b903-d09943ecb8f5
31+
*/
32+
public function testRotateAByOne(): void
33+
{
34+
$expected = 'b';
35+
$actual = $this->rotationalCipher->rotate('a', 1);
36+
$this->assertEquals($expected, $actual);
37+
}
38+
39+
/**
40+
* uuid: edf0a733-4231-4594-a5ee-46a4009ad764
41+
*/
42+
public function testRotateABy26(): void
43+
{
44+
$expected = 'a';
45+
$actual = $this->rotationalCipher->rotate('a', 26);
46+
$this->assertEquals($expected, $actual);
47+
}
48+
49+
/**
50+
* uuid: e3e82cb9-2a5b-403f-9931-e43213879300
51+
*/
52+
public function testRotateMBy13(): void
53+
{
54+
$expected = 'z';
55+
$actual = $this->rotationalCipher->rotate('m', 13);
56+
$this->assertEquals($expected, $actual);
57+
}
58+
59+
/**
60+
* uuid: 19f9eb78-e2ad-4da4-8fe3-9291d47c1709
61+
*/
62+
public function testRotateNBy13WithWrapAroundAlphabet(): void
63+
{
64+
$expected = 'a';
65+
$actual = $this->rotationalCipher->rotate('n', 13);
66+
$this->assertEquals($expected, $actual);
67+
}
68+
69+
/**
70+
* uuid: a116aef4-225b-4da9-884f-e8023ca6408a
71+
*/
72+
public function testRotateCapitalLetters(): void
73+
{
74+
$expected = 'TRL';
75+
$actual = $this->rotationalCipher->rotate('OMG', 5);
76+
$this->assertEquals($expected, $actual);
77+
}
78+
79+
/**
80+
* uuid: 71b541bb-819c-4dc6-a9c3-132ef9bb737b
81+
*/
82+
public function testRotateSpaces(): void
83+
{
84+
$expected = 'T R L';
85+
$actual = $this->rotationalCipher->rotate('O M G', 5);
86+
$this->assertEquals($expected, $actual);
87+
}
88+
89+
/**
90+
* uuid: ef32601d-e9ef-4b29-b2b5-8971392282e6
91+
*/
92+
public function testRotateNumbers(): void
93+
{
94+
$expected = 'Xiwxmrk 1 2 3 xiwxmrk';
95+
$actual = $this->rotationalCipher->rotate('Testing 1 2 3 testing', 4);
96+
$this->assertEquals($expected, $actual);
97+
}
98+
99+
/**
100+
* uuid: 32dd74f6-db2b-41a6-b02c-82eb4f93e549
101+
*/
102+
public function testRotatePunctuation(): void
103+
{
104+
$expected = "Gzo'n zvo, Bmviyhv!";
105+
$actual = $this->rotationalCipher->rotate("Let's eat, Grandma!", 21);
106+
$this->assertEquals($expected, $actual);
107+
}
108+
109+
/**
110+
* uuid: 9fb93fe6-42b0-46e6-9ec1-0bf0a062d8c9
111+
*/
112+
public function testRotateAllLetters(): void
113+
{
114+
$expected = 'Gur dhvpx oebja sbk whzcf bire gur ynml qbt.';
115+
$actual = $this->rotationalCipher->rotate('The quick brown fox jumps over the lazy dog.', 13);
116+
$this->assertEquals($expected, $actual);
117+
}
118+
}

0 commit comments

Comments
 (0)