diff --git a/config.json b/config.json index 1b99f98..4f3db86 100644 --- a/config.json +++ b/config.json @@ -218,6 +218,14 @@ "prerequisites": [], "difficulty": 8 }, + { + "slug": "atbash-cipher", + "name": "Atbash Cipher", + "uuid": "171c0743-9e22-4152-af26-831ca097ea04", + "practices": [], + "prerequisites": [], + "difficulty": 8 + }, { "slug": "collatz-conjecture", "name": "Collatz Conjecture", diff --git a/exercises/practice/atbash-cipher/.docs/instructions.md b/exercises/practice/atbash-cipher/.docs/instructions.md new file mode 100644 index 0000000..1e7627b --- /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..8e3739c --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "jimmytty" + ], + "files": { + "solution": [ + "atbash-cipher.sql" + ], + "test": [ + "atbash-cipher_test.sql" + ], + "example": [ + ".meta/example.sql" + ] + }, + "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/example.sql b/exercises/practice/atbash-cipher/.meta/example.sql new file mode 100644 index 0000000..407dbb9 --- /dev/null +++ b/exercises/practice/atbash-cipher/.meta/example.sql @@ -0,0 +1,57 @@ +UPDATE "atbash-cipher" + SET result = ( + WITH RECURSIVE rcte (i, c, string) AS ( + VALUES(1, NULL, ( + WITH RECURSIVE encode (phrase, c) AS ( + VALUES(LOWER(phrase), NULL) + UNION ALL + SELECT SUBSTR(phrase, 2), SUBSTR(phrase, 1, 1) + FROM encode + WHERE phrase <> '' + ) + SELECT GROUP_CONCAT(c, '') AS result + FROM ( + SELECT CASE + WHEN (UNICODE(c) BETWEEN UNICODE('a') AND UNICODE('z')) + THEN CHAR(UNICODE('z') - (UNICODE(c) - UNICODE('a'))) + WHEN (UNICODE(c) BETWEEN UNICODE('0') AND UNICODE('9')) + THEN c + END AS c + FROM encode + ) + WHERE c NOT NULL + )) + UNION ALL + SELECT i + 1, + SUBSTR(string, 1, 1) || IIF(i % 5 = 0, ' ', ''), + SUBSTR(string, 2) + FROM rcte + WHERE string <> '' + ) + SELECT TRIM(GROUP_CONCAT(c, '')) FROM rcte + ) + WHERE property = 'encode' + ; + +UPDATE "atbash-cipher" + SET result = ( + WITH RECURSIVE decode (phrase, c) AS ( + VALUES(phrase, NULL) + UNION ALL + SELECT SUBSTR(phrase, 2), SUBSTR(phrase, 1, 1) + FROM decode + WHERE phrase <> '' + ) + SELECT GROUP_CONCAT(c, '') AS result + FROM ( + SELECT CASE + WHEN (UNICODE(c) BETWEEN UNICODE('a') AND UNICODE('z')) + THEN CHAR(UNICODE('z') - UNICODE(c) + UNICODE('a')) + WHEN (UNICODE(c) BETWEEN UNICODE('0') AND UNICODE('9')) THEN c + END AS c + FROM decode + ) + WHERE c NOT NULL + ) + WHERE property = 'decode' + ; 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/atbash-cipher.sql b/exercises/practice/atbash-cipher/atbash-cipher.sql new file mode 100644 index 0000000..f7dbd82 --- /dev/null +++ b/exercises/practice/atbash-cipher/atbash-cipher.sql @@ -0,0 +1,7 @@ +-- Schema: CREATE TABLE "atbash-cipher" ( +-- property TEXT NOT NULL, +-- phrase TEXT NOT NULL, +-- result TEXT +-- ); +-- Task: update the atbash-cipher table and set the result based on +-- property and phrase diff --git a/exercises/practice/atbash-cipher/atbash-cipher_test.sql b/exercises/practice/atbash-cipher/atbash-cipher_test.sql new file mode 100644 index 0000000..e06f7e1 --- /dev/null +++ b/exercises/practice/atbash-cipher/atbash-cipher_test.sql @@ -0,0 +1,43 @@ +-- Create database: +.read ./create_fixture.sql + +-- Read user student solution and save any output as markdown in user_output.md: +.mode markdown +.output user_output.md +.read ./atbash-cipher.sql +.output + +-- Create a clean testing environment: +.read ./create_test_table.sql + +-- Comparison of user input and the tests updates the status for each test: +UPDATE tests +SET status = 'pass' +FROM (SELECT property, phrase, result FROM "atbash-cipher") AS actual +WHERE (actual.property, actual.phrase, actual.result) = (tests.property, tests.phrase, tests.expected); + +-- Update message for failed tests to give helpful information: +UPDATE tests +SET message = ( + 'Result for "' + || JSON_OBJECT( + 'property', tests.property, + 'phrase', tests.phrase + ) + || '"' + || ' is <' || COALESCE(actual.result, 'NULL') + || '> but should be <' || tests.expected || '>' +) +FROM (SELECT property, phrase, result FROM "atbash-cipher") AS actual +WHERE (actual.property, actual.phrase) = (tests.property, tests.phrase) AND tests.status = 'fail'; + +-- Save results to ./output.json (needed by the online test-runner) +.mode json +.once './output.json' +SELECT description, status, message, output, test_code, task_id +FROM tests; + +-- Display test results in readable form for the student: +.mode table +SELECT description, status, message +FROM tests; diff --git a/exercises/practice/atbash-cipher/create_fixture.sql b/exercises/practice/atbash-cipher/create_fixture.sql new file mode 100644 index 0000000..c34df3b --- /dev/null +++ b/exercises/practice/atbash-cipher/create_fixture.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS "atbash-cipher"; +CREATE TABLE "atbash-cipher" ( + property TEXT NOT NULL, + phrase TEXT NOT NULL, + result TEXT +); + +.mode csv +.import ./data.csv "atbash-cipher" diff --git a/exercises/practice/atbash-cipher/create_test_table.sql b/exercises/practice/atbash-cipher/create_test_table.sql new file mode 100644 index 0000000..f91f0f0 --- /dev/null +++ b/exercises/practice/atbash-cipher/create_test_table.sql @@ -0,0 +1,33 @@ +DROP TABLE IF EXISTS tests; +CREATE TABLE IF NOT EXISTS tests ( + -- uuid and description are taken from the test.toml file + uuid TEXT PRIMARY KEY, + description TEXT NOT NULL, + -- The following section is needed by the online test-runner + status TEXT DEFAULT 'fail', + message TEXT, + output TEXT, + test_code TEXT, + task_id INTEGER DEFAULT NULL, + -- Here are columns for the actual tests + property TEXT NOT NULL, + phrase TEXT NOT NULL, + expected TEXT NOT NULL +); + +INSERT INTO tests (uuid, description, property, phrase, expected) + VALUES + ('2f47ebe1-eab9-4d6b-b3c6-627562a31c77','encode yes','encode','yes','bvh'), + ('b4ffe781-ea81-4b74-b268-cc58ba21c739','encode no','encode','no','ml'), + ('10e48927-24ab-4c4d-9d3f-3067724ace00','encode OMG','encode','OMG','lnt'), + ('d59b8bc3-509a-4a9a-834c-6f501b98750b','encode spaces','encode','O M G','lnt'), + ('31d44b11-81b7-4a94-8b43-4af6a2449429','encode mindblowingly','encode','mindblowingly','nrmwy oldrm tob'), + ('d503361a-1433-48c0-aae0-d41b5baa33ff','encode numbers','encode','Testing,1 2 3, testing.','gvhgr mt123 gvhgr mt'), + ('79c8a2d5-0772-42d4-b41b-531d0b5da926','encode deep thought','encode','Truth is fiction.','gifgs rhurx grlm'), + ('9ca13d23-d32a-4967-a1fd-6100b8742bab','encode all the letters','encode','The quick brown fox jumps over the lazy dog.','gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt'), + ('bb50e087-7fdf-48e7-9223-284fe7e69851','decode exercism','decode','vcvix rhn','exercism'), + ('ac021097-cd5d-4717-8907-b0814b9e292c','decode a sentence','decode','zmlyh gzxov rhlug vmzhg vkkrm thglm v','anobstacleisoftenasteppingstone'), + ('18729de3-de74-49b8-b68c-025eaf77f851','decode numbers','decode','gvhgr mt123 gvhgr mt','testing123testing'), + ('0f30325f-f53b-415d-ad3e-a7a4f63de034','decode all the letters','decode','gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt','thequickbrownfoxjumpsoverthelazydog'), + ('39640287-30c6-4c8c-9bac-9d613d1a5674','decode with too many spaces','decode','vc vix r hn','exercism'), + ('b34edf13-34c0-49b5-aa21-0768928000d5','decode with no spaces','decode','zmlyhgzxovrhlugvmzhgvkkrmthglmv','anobstacleisoftenasteppingstone'); diff --git a/exercises/practice/atbash-cipher/data.csv b/exercises/practice/atbash-cipher/data.csv new file mode 100644 index 0000000..8a816c7 --- /dev/null +++ b/exercises/practice/atbash-cipher/data.csv @@ -0,0 +1,14 @@ +"encode","yes","" +"encode","no","" +"encode","OMG","" +"encode","O M G","" +"encode","mindblowingly","" +"encode","Testing,1 2 3, testing.","" +"encode","Truth is fiction.","" +"encode","The quick brown fox jumps over the lazy dog.","" +"decode","vcvix rhn","" +"decode","zmlyh gzxov rhlug vmzhg vkkrm thglm v","" +"decode","gvhgr mt123 gvhgr mt","" +"decode","gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt","" +"decode","vc vix r hn","" +"decode","zmlyhgzxovrhlugvmzhgvkkrmthglmv",""