diff --git a/config.json b/config.json index 091feb8..504987c 100644 --- a/config.json +++ b/config.json @@ -474,6 +474,14 @@ "prerequisites": [], "difficulty": 2 }, + { + "slug": "atbash-cipher", + "name": "Atbash Cipher", + "uuid": "d92ec5ae-e9b1-4089-a633-c885050d679c", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, { "slug": "protein-translation", "name": "Protein Translation", diff --git a/exercises/practice/atbash-cipher/.docs/instructions.md b/exercises/practice/atbash-cipher/.docs/instructions.md new file mode 100644 index 0000000..21ca2ce --- /dev/null +++ b/exercises/practice/atbash-cipher/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East. + +The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards. +The first letter is replaced with the last letter, the second with the second-last, and so on. + +An Atbash cipher for the Latin alphabet would be as follows: + +```text +Plain: abcdefghijklmnopqrstuvwxyz +Cipher: zyxwvutsrqponmlkjihgfedcba +``` + +It is a very weak cipher because it only has one possible key, and it is a simple mono-alphabetic substitution cipher. +However, this may not have been an issue in the cipher's time. + +Ciphertext is written out in groups of fixed length, the traditional group size being 5 letters, leaving numbers unchanged, and punctuation is excluded. +This is to make it harder to guess things based on word boundaries. +All text will be encoded as lowercase letters. + +## Examples + +- Encoding `test` gives `gvhg` +- Encoding `x123 yes` gives `c123b vh` +- Decoding `gvhg` gives `test` +- Decoding `gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt` gives `thequickbrownfoxjumpsoverthelazydog` diff --git a/exercises/practice/atbash-cipher/.meta/config.json b/exercises/practice/atbash-cipher/.meta/config.json new file mode 100644 index 0000000..8082084 --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "atbash_cipher.bal" + ], + "test": [ + "tests/atbash_cipher_test.bal" + ], + "example": [ + ".meta/reference/atbash_cipher.bal" + ] + }, + "blurb": "Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Atbash" +} diff --git a/exercises/practice/atbash-cipher/.meta/reference/atbash_cipher.bal b/exercises/practice/atbash-cipher/.meta/reference/atbash_cipher.bal new file mode 100644 index 0000000..28fb8f4 --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/reference/atbash_cipher.bal @@ -0,0 +1,46 @@ +import ballerina/lang.regexp; + +public function encode(string phrase) returns string { + string result = ""; + int chunk_size = 0; + foreach string:Char chr in phrase { + if isAlphanumeric(chr) { + if chunk_size == 5 { + result += " "; + chunk_size = 0; + } + + result += encodeChar(chr); + chunk_size += 1; + } + } + + return result; +} + +public function decode(string phrase) returns string { + string result = ""; + + foreach string:Char chr in phrase { + if chr != " " { + result += encodeChar(chr); + } + } + + return result; +} + +function encodeChar(string:Char chr) returns string { + string lowered = chr.toLowerAscii(); + int codePoint = lowered.getCodePoint(0); + if codePoint >= 97 && codePoint <= 122 { + codePoint = 97 + 122 - codePoint; + } + + return checkpanic string:fromCodePointInt(codePoint); +} + +function isAlphanumeric(string:Char chr) returns boolean { + string:RegExp r = re `[a-zA-Z0-9]`; + return r.find(chr) is regexp:Span; +} diff --git a/exercises/practice/atbash-cipher/.meta/tests.toml b/exercises/practice/atbash-cipher/.meta/tests.toml new file mode 100644 index 0000000..c082d07 --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[2f47ebe1-eab9-4d6b-b3c6-627562a31c77] +description = "encode -> encode yes" + +[b4ffe781-ea81-4b74-b268-cc58ba21c739] +description = "encode -> encode no" + +[10e48927-24ab-4c4d-9d3f-3067724ace00] +description = "encode -> encode OMG" + +[d59b8bc3-509a-4a9a-834c-6f501b98750b] +description = "encode -> encode spaces" + +[31d44b11-81b7-4a94-8b43-4af6a2449429] +description = "encode -> encode mindblowingly" + +[d503361a-1433-48c0-aae0-d41b5baa33ff] +description = "encode -> encode numbers" + +[79c8a2d5-0772-42d4-b41b-531d0b5da926] +description = "encode -> encode deep thought" + +[9ca13d23-d32a-4967-a1fd-6100b8742bab] +description = "encode -> encode all the letters" + +[bb50e087-7fdf-48e7-9223-284fe7e69851] +description = "decode -> decode exercism" + +[ac021097-cd5d-4717-8907-b0814b9e292c] +description = "decode -> decode a sentence" + +[18729de3-de74-49b8-b68c-025eaf77f851] +description = "decode -> decode numbers" + +[0f30325f-f53b-415d-ad3e-a7a4f63de034] +description = "decode -> decode all the letters" + +[39640287-30c6-4c8c-9bac-9d613d1a5674] +description = "decode -> decode with too many spaces" + +[b34edf13-34c0-49b5-aa21-0768928000d5] +description = "decode -> decode with no spaces" diff --git a/exercises/practice/atbash-cipher/Ballerina.toml b/exercises/practice/atbash-cipher/Ballerina.toml new file mode 100644 index 0000000..c3ae01f --- /dev/null +++ b/exercises/practice/atbash-cipher/Ballerina.toml @@ -0,0 +1,5 @@ +[package] +org = "ballerina_exercism" +name = "atbash_cipher" +version = "0.1.0" +distribution = "2201.5.0" diff --git a/exercises/practice/atbash-cipher/Dependencies.toml b/exercises/practice/atbash-cipher/Dependencies.toml new file mode 100644 index 0000000..e4f8244 --- /dev/null +++ b/exercises/practice/atbash-cipher/Dependencies.toml @@ -0,0 +1,49 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.5.0" + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "lang.regexp", moduleName = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "test" +version = "0.0.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "test", moduleName = "test"} +] + +[[package]] +org = "ballerina_exercism" +name = "atbash_cipher" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "test"} +] +modules = [ + {org = "ballerina_exercism", packageName = "atbash_cipher", moduleName = "atbash_cipher"} +] + diff --git a/exercises/practice/atbash-cipher/atbash_cipher.bal b/exercises/practice/atbash-cipher/atbash_cipher.bal new file mode 100644 index 0000000..ce4d1f9 --- /dev/null +++ b/exercises/practice/atbash-cipher/atbash_cipher.bal @@ -0,0 +1,7 @@ +public function encode(string phrase) returns string { + // TODO: implement this function +} + +public function decode(string phrase) returns string { + // TODO: implement this function +} diff --git a/exercises/practice/atbash-cipher/tests/atbash_cipher_test.bal b/exercises/practice/atbash-cipher/tests/atbash_cipher_test.bal new file mode 100644 index 0000000..cf374b5 --- /dev/null +++ b/exercises/practice/atbash-cipher/tests/atbash_cipher_test.bal @@ -0,0 +1,126 @@ +import ballerina/test; + +@test:Config +function encodes_yes() { + string phrase = "yes"; + string expected = "bvh"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function encodes_no() { + string phrase = "no"; + string expected = "ml"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function encodes_OMG() { + string phrase = "OMG"; + string expected = "lnt"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function encodes_spaces() { + string phrase = "O M G"; + string expected = "lnt"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function encodes_mindblowingly() { + string phrase = "mindblowingly"; + string expected = "nrmwy oldrm tob"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function encodes_numbers() { + string phrase = "Testing, 1 2 3, testing."; + string expected = "gvhgr mt123 gvhgr mt"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} + +function encodes_deep_thought() { + string phrase = "Truth is fiction."; + string expected = "gifgs rhurx grlm"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function encodes_all_the_letters() { + string phrase = "The quick brown fox jumps over the lazy dog."; + string expected = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt"; + test:assertEquals(encode(phrase), expected); +} + +@test:Config { + enable: false +} +function decodes_exercism() { + string phrase = "vcvix rhn"; + string expected = "exercism"; + test:assertEquals(decode(phrase), expected); +} + +@test:Config { + enable: false +} +function decodes_a_sentence() { + string phrase = "zmlyh gzxov rhlug vmzhg vkkrm thglm v"; + string expected = "anobstacleisoftenasteppingstone"; + test:assertEquals(decode(phrase), expected); +} + +@test:Config { + enable: false +} +function decodes_numbers() { + string phrase = "gvhgr mt123 gvhgr mt"; + string expected = "testing123testing"; + test:assertEquals(decode(phrase), expected); +} + +@test:Config { + enable: false +} +function decodes_all_the_letters() { + string phrase = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt"; + string expected = "thequickbrownfoxjumpsoverthelazydog"; + test:assertEquals(decode(phrase), expected); +} + +@test:Config { + enable: false +} +function decodes_with_too_many_spaces() { + string phrase = "vc vix r hn"; + string expected = "exercism"; + test:assertEquals(decode(phrase), expected); +} + +@test:Config { + enable: false +} +function decodes_with_no_spaces() { + string phrase = "zmlyhgzxovrhlugvmzhgvkkrmthglmv"; + string expected = "anobstacleisoftenasteppingstone"; + test:assertEquals(decode(phrase), expected); +}