diff --git a/bin/generate-spec b/bin/generate-spec index 14bd569..909e544 100755 --- a/bin/generate-spec +++ b/bin/generate-spec @@ -85,7 +85,10 @@ local function process(node) %s end)]] - return template:format(node.description:lower(), spec_generator.generate_test(node)) + return template:format( + node.description:lower():gsub('\'', '\\\''), + spec_generator.generate_test(node) + ) end end diff --git a/config.json b/config.json index debfea1..47177aa 100644 --- a/config.json +++ b/config.json @@ -986,6 +986,14 @@ "prerequisites": [], "difficulty": 4 }, + { + "slug": "dominoes", + "name": "Dominoes", + "uuid": "0ea6977c-5032-40ab-bd67-be23e052c69f", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "affine-cipher", "name": "Affine Cipher", diff --git a/exercises/practice/dominoes/.busted b/exercises/practice/dominoes/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/dominoes/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/dominoes/.docs/instructions.md b/exercises/practice/dominoes/.docs/instructions.md new file mode 100644 index 0000000..1ced9f6 --- /dev/null +++ b/exercises/practice/dominoes/.docs/instructions.md @@ -0,0 +1,13 @@ +# Instructions + +Make a chain of dominoes. + +Compute a way to order a given set of dominoes in such a way that they form a correct domino chain (the dots on one half of a stone match the dots on the neighboring half of an adjacent stone) and that dots on the halves of the stones which don't have a neighbor (the first and last stone) match each other. + +For example given the stones `[2|1]`, `[2|3]` and `[1|3]` you should compute something +like `[1|2] [2|3] [3|1]` or `[3|2] [2|1] [1|3]` or `[1|3] [3|2] [2|1]` etc, where the first and last numbers are the same. + +For stones `[1|2]`, `[4|1]` and `[2|3]` the resulting chain is not valid: `[4|1] [1|2] [2|3]`'s first and last numbers are not the same. +4 != 3 + +Some test cases may use duplicate stones in a chain solution, assume that multiple Domino sets are being used. diff --git a/exercises/practice/dominoes/.meta/config.json b/exercises/practice/dominoes/.meta/config.json new file mode 100644 index 0000000..0d1fcdc --- /dev/null +++ b/exercises/practice/dominoes/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "ryanplusplus" + ], + "files": { + "solution": [ + "dominoes.lua" + ], + "test": [ + "dominoes_spec.lua" + ], + "example": [ + ".meta/example.lua" + ] + }, + "blurb": "Make a chain of dominoes." +} diff --git a/exercises/practice/dominoes/.meta/example.lua b/exercises/practice/dominoes/.meta/example.lua new file mode 100644 index 0000000..f3dd012 --- /dev/null +++ b/exercises/practice/dominoes/.meta/example.lua @@ -0,0 +1,39 @@ +local function reversed(domino) + return { domino[2], domino[1] } +end + +local function without_domino(dominoes, target) + local without = {} + for i = 1, #dominoes do + if i ~= target then + table.insert(without, dominoes[i]) + end + end + return without +end + +local function can_chain(dominoes) + if #dominoes == 0 then + return true + end + + local function recur(left, dominoes, right) + if #dominoes == 0 then + return left == right + end + + for i, domino in ipairs(dominoes) do + for _, domino in ipairs({ domino, reversed(domino) }) do + if domino[1] == left and recur(domino[2], without_domino(dominoes, i), right) then + return true + end + end + end + + return false + end + + return recur(dominoes[1][1], without_domino(dominoes, 1), dominoes[1][2]) +end + +return { can_chain = can_chain } diff --git a/exercises/practice/dominoes/.meta/spec_generator.lua b/exercises/practice/dominoes/.meta/spec_generator.lua new file mode 100644 index 0000000..f9e0360 --- /dev/null +++ b/exercises/practice/dominoes/.meta/spec_generator.lua @@ -0,0 +1,26 @@ +local map = function(t, f) + local mapped = {} + for i, v in ipairs(t) do + mapped[i] = f(v) + end + return mapped +end + +local function render_dominoes(dominoes) + return table.concat(map(dominoes, function(domino) + return ('{ %s }'):format(table.concat(domino, ', ')) + end), ', ') +end + +return { + module_name = 'dominoes', + + generate_test = function(case) + local template = [[ + local input = { + %s + } + assert.is_%s(dominoes.can_chain(input))]] + return template:format(render_dominoes(case.input.dominoes), case.expected) + end +} diff --git a/exercises/practice/dominoes/.meta/tests.toml b/exercises/practice/dominoes/.meta/tests.toml new file mode 100644 index 0000000..08c8e08 --- /dev/null +++ b/exercises/practice/dominoes/.meta/tests.toml @@ -0,0 +1,49 @@ +# 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. + +[31a673f2-5e54-49fe-bd79-1c1dae476c9c] +description = "empty input = empty output" + +[4f99b933-367b-404b-8c6d-36d5923ee476] +description = "singleton input = singleton output" + +[91122d10-5ec7-47cb-b759-033756375869] +description = "singleton that can't be chained" + +[be8bc26b-fd3d-440b-8e9f-d698a0623be3] +description = "three elements" + +[99e615c6-c059-401c-9e87-ad7af11fea5c] +description = "can reverse dominoes" + +[51f0c291-5d43-40c5-b316-0429069528c9] +description = "can't be chained" + +[9a75e078-a025-4c23-8c3a-238553657f39] +description = "disconnected - simple" + +[0da0c7fe-d492-445d-b9ef-1f111f07a301] +description = "disconnected - double loop" + +[b6087ff0-f555-4ea0-a71c-f9d707c5994a] +description = "disconnected - single isolated" + +[2174fbdc-8b48-4bac-9914-8090d06ef978] +description = "need backtrack" + +[167bb480-dfd1-4318-a20d-4f90adb4a09f] +description = "separate loops" + +[cd061538-6046-45a7-ace9-6708fe8f6504] +description = "nine elements" + +[44704c7c-3adb-4d98-bd30-f45527cf8b49] +description = "separate three-domino loops" diff --git a/exercises/practice/dominoes/dominoes.lua b/exercises/practice/dominoes/dominoes.lua new file mode 100644 index 0000000..f1e585d --- /dev/null +++ b/exercises/practice/dominoes/dominoes.lua @@ -0,0 +1,5 @@ +local function can_chain(dominoes) + +end + +return { can_chain = can_chain } diff --git a/exercises/practice/dominoes/dominoes_spec.lua b/exercises/practice/dominoes/dominoes_spec.lua new file mode 100644 index 0000000..2c337ae --- /dev/null +++ b/exercises/practice/dominoes/dominoes_spec.lua @@ -0,0 +1,68 @@ +local dominoes = require('dominoes') + +describe('dominoes', function() + it('empty input = empty output', function() + local input = {} + assert.is_true(dominoes.can_chain(input)) + end) + + it('singleton input = singleton output', function() + local input = { { 1, 1 } } + assert.is_true(dominoes.can_chain(input)) + end) + + it('singleton that can\'t be chained', function() + local input = { { 1, 2 } } + assert.is_false(dominoes.can_chain(input)) + end) + + it('three elements', function() + local input = { { 1, 2 }, { 3, 1 }, { 2, 3 } } + assert.is_true(dominoes.can_chain(input)) + end) + + it('can reverse dominoes', function() + local input = { { 1, 2 }, { 1, 3 }, { 2, 3 } } + assert.is_true(dominoes.can_chain(input)) + end) + + it('can\'t be chained', function() + local input = { { 1, 2 }, { 4, 1 }, { 2, 3 } } + assert.is_false(dominoes.can_chain(input)) + end) + + it('disconnected - simple', function() + local input = { { 1, 1 }, { 2, 2 } } + assert.is_false(dominoes.can_chain(input)) + end) + + it('disconnected - double loop', function() + local input = { { 1, 2 }, { 2, 1 }, { 3, 4 }, { 4, 3 } } + assert.is_false(dominoes.can_chain(input)) + end) + + it('disconnected - single isolated', function() + local input = { { 1, 2 }, { 2, 3 }, { 3, 1 }, { 4, 4 } } + assert.is_false(dominoes.can_chain(input)) + end) + + it('need backtrack', function() + local input = { { 1, 2 }, { 2, 3 }, { 3, 1 }, { 2, 4 }, { 2, 4 } } + assert.is_true(dominoes.can_chain(input)) + end) + + it('separate loops', function() + local input = { { 1, 2 }, { 2, 3 }, { 3, 1 }, { 1, 1 }, { 2, 2 }, { 3, 3 } } + assert.is_true(dominoes.can_chain(input)) + end) + + it('nine elements', function() + local input = { { 1, 2 }, { 5, 3 }, { 3, 1 }, { 1, 2 }, { 2, 4 }, { 1, 6 }, { 2, 3 }, { 3, 4 }, { 5, 6 } } + assert.is_true(dominoes.can_chain(input)) + end) + + it('separate three-domino loops', function() + local input = { { 1, 2 }, { 2, 3 }, { 3, 1 }, { 4, 5 }, { 5, 6 }, { 6, 4 } } + assert.is_false(dominoes.can_chain(input)) + end) +end)