diff --git a/exercises/practice/word-search/.meta/example.lua b/exercises/practice/word-search/.meta/example.lua index 4e40b3e0..0fb7c8a7 100644 --- a/exercises/practice/word-search/.meta/example.lua +++ b/exercises/practice/word-search/.meta/example.lua @@ -42,15 +42,23 @@ end local deltas = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 } } -return function(puzzle) +return function(grid) + grid = Grid(grid) + return { - find = function(word) - for _, delta in ipairs(deltas) do - local first, last = find(Grid(puzzle), word, delta[1], delta[2]) - if first then - return first, last + search = function(words) + local result = {} + + for _, word in ipairs(words) do + for _, delta in ipairs(deltas) do + local first, last = find(grid, word, delta[1], delta[2]) + if first and last then + result[word] = { start = first, ['end'] = last } + end end end + + return result end } end diff --git a/exercises/practice/word-search/.meta/spec_generator.lua b/exercises/practice/word-search/.meta/spec_generator.lua new file mode 100644 index 00000000..81959ffc --- /dev/null +++ b/exercises/practice/word-search/.meta/spec_generator.lua @@ -0,0 +1,49 @@ +local function map(t, f) + local result = {} + for i, v in ipairs(t) do + result[i] = f(v) + end + return result +end + +local function stringify(x) + return ("'%s'"):format(x) +end + +local function render_expected(expected) + local key_value_pairs = {} + for k, v in pairs(expected) do + local template = "%s = { start = { %d, %d }, ['end'] = { %d, %d } }" + local key_value_pair = template:format(k, v.start.column, v.start.row, v['end'].column, v['end'].row) + table.insert(key_value_pairs, key_value_pair) + end + table.sort(key_value_pairs) + + if #key_value_pairs == 0 then + return '{}' + else + return '{ --\n' .. table.concat(key_value_pairs, ', --\n') .. '\n}' + end +end + +return { + module_name = 'WordSearch', + + generate_test = function(case) + local template = [[ + local grid = { -- + %s + } + local words = { -- + %s + } + local expected = %s + assert.are.same(expected, WordSearch(grid).search(words))]] + + local grid = table.concat(map(case.input.grid, stringify), ', --\n') + local words = table.concat(map(case.input.wordsToSearchFor, stringify), ', --\n') + local expected = render_expected(case.expected) + + return template:format(grid, words, expected) + end +} diff --git a/exercises/practice/word-search/.meta/tests.toml b/exercises/practice/word-search/.meta/tests.toml index 1790f8a4..3f98113d 100644 --- a/exercises/practice/word-search/.meta/tests.toml +++ b/exercises/practice/word-search/.meta/tests.toml @@ -1,6 +1,13 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [b4057815-0d01-41f0-9119-6a91f54b2a0a] description = "Should accept an initial game grid and a target search word" @@ -61,3 +68,15 @@ description = "Should locate words written top right to bottom left" [695531db-69eb-463f-8bad-8de3bf5ef198] description = "Should fail to locate a word that is not in the puzzle" + +[fda5b937-6774-4a52-8f89-f64ed833b175] +description = "Should fail to locate words that are not on horizontal, vertical, or diagonal lines" + +[5b6198eb-2847-4e2f-8efe-65045df16bd3] +description = "Should not concatenate different lines to find a horizontal word" + +[eba44139-a34f-4a92-98e1-bd5f259e5769] +description = "Should not wrap around horizontally to find a word" + +[cd1f0fa8-76af-4167-b105-935f78364dac] +description = "Should not wrap around vertically to find a word" diff --git a/exercises/practice/word-search/word-search.lua b/exercises/practice/word-search/word-search.lua index a1b2e5ed..3b3f71f8 100644 --- a/exercises/practice/word-search/word-search.lua +++ b/exercises/practice/word-search/word-search.lua @@ -1,2 +1,2 @@ -return function(puzzle) +return function(grid) end diff --git a/exercises/practice/word-search/word-search_spec.lua b/exercises/practice/word-search/word-search_spec.lua index c49291e2..29aad9a2 100644 --- a/exercises/practice/word-search/word-search_spec.lua +++ b/exercises/practice/word-search/word-search_spec.lua @@ -1,83 +1,500 @@ local WordSearch = require('word-search') --- LuaFormatter off describe('word-search', function() - local puzzle = { - 'jefblpepre', - 'camdcimgtc', - 'oivokprjsm', - 'pbwasqroua', - 'rixilelhrs', - 'wolcqlirpc', - 'screeaumgr', - 'alxhpburyi', - 'jalaycalmp', - 'clojurermt' - } + it('should accept an initial game grid and a target search word', function() + local grid = { -- + 'jefblpepre' + } + local words = { -- + 'clojure' + } + local expected = {} + assert.are.same(expected, WordSearch(grid).search(words)) + end) - it('should find horizontal words written left-to-right', function() - local first, last = WordSearch(puzzle).find('clojure') - assert.same({ 1, 10 }, first) - assert.same({ 7, 10 }, last) + it('should locate one word written left to right', function() + local grid = { -- + 'clojurermt' + } + local words = { -- + 'clojure' + } + local expected = { -- + clojure = { start = { 1, 1 }, ['end'] = { 7, 1 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find horizontal words written right-to-left', function() - local first, last = WordSearch(puzzle).find('elixir') - assert.same({ 6, 5 }, first) - assert.same({ 1, 5 }, last) + it('should locate the same word written left to right in a different position', function() + local grid = { -- + 'mtclojurer' + } + local words = { -- + 'clojure' + } + local expected = { -- + clojure = { start = { 3, 1 }, ['end'] = { 9, 1 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find vertical words written top-to-bottom', function() - local first, last = WordSearch(puzzle).find('ecmascript') - assert.same({ 10, 1 }, first) - assert.same({ 10, 10 }, last) + it('should locate a different left to right word', function() + local grid = { -- + 'coffeelplx' + } + local words = { -- + 'coffee' + } + local expected = { -- + coffee = { start = { 1, 1 }, ['end'] = { 6, 1 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find vertical words written bottom-to-top', function() - local first, last = WordSearch(puzzle).find('rust') - assert.same({ 9, 5 }, first) - assert.same({ 9, 2 }, last) + it('should locate that different left to right word in a different position', function() + local grid = { -- + 'xcoffeezlp' + } + local words = { -- + 'coffee' + } + local expected = { -- + coffee = { start = { 2, 1 }, ['end'] = { 7, 1 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find diagonal words written top-left-to-bottom-right', function() - local first, last = WordSearch(puzzle).find('java') - assert.same({ 1, 1 }, first) - assert.same({ 4, 4 }, last) + it('should locate a left to right word in two line grid', function() + local grid = { -- + 'jefblpepre', -- + 'tclojurerm' + } + local words = { -- + 'clojure' + } + local expected = { -- + clojure = { start = { 2, 2 }, ['end'] = { 8, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find diagonal upper written bottom-right-to-top-left', function() - local first, last = WordSearch(puzzle).find('lua') - assert.same({ 8, 9 }, first) - assert.same({ 6, 7 }, last) + it('should locate a left to right word in three line grid', function() + local grid = { -- + 'camdcimgtc', -- + 'jefblpepre', -- + 'clojurermt' + } + local words = { -- + 'clojure' + } + local expected = { -- + clojure = { start = { 1, 3 }, ['end'] = { 7, 3 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find diagonal upper written bottom-left-to-top-right', function() - local first, last = WordSearch(puzzle).find('lisp') - assert.same({ 3, 6 }, first) - assert.same({ 6, 3 }, last) + it('should locate a left to right word in ten line grid', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should find diagonal upper written top-right-to-bottom-left', function() - local first, last = WordSearch(puzzle).find('ruby') - assert.same({ 8, 6 }, first) - assert.same({ 5, 9 }, last) + it('should locate that left to right word in a different position in a ten line grid', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'clojurermt', -- + 'jalaycalmp' + } + local words = { -- + 'clojure' + } + local expected = { -- + clojure = { start = { 1, 9 }, ['end'] = { 7, 9 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate a different left to right word in a ten line grid', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'fortranftw', -- + 'alxhpburyi', -- + 'clojurermt', -- + 'jalaycalmp' + } + local words = { -- + 'fortran' + } + local expected = { -- + fortran = { start = { 1, 7 }, ['end'] = { 7, 7 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should not find words that are not in the puzzle', function() - local first, last = WordSearch(puzzle).find('haskell') - assert.same(nil, first) - assert.same(nil, last) + it('should locate multiple words', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'fortranftw', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'fortran', -- + 'clojure' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + fortran = { start = { 1, 7 }, ['end'] = { 7, 7 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) end) - it('should be able to search differently-sized puzzles', function() - local puzzle = { - 'qwertyuiopz', - 'luamsicrexe', - 'abcdefghijk' + it('should locate a single word written right to left', function() + local grid = { -- + 'rixilelhrs' + } + local words = { -- + 'elixir' + } + local expected = { -- + elixir = { start = { 6, 1 }, ['end'] = { 1, 1 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate multiple words written in different horizontal directions', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'elixir', -- + 'clojure' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate words written top to bottom', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate words written bottom to top', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript', -- + 'rust' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } }, -- + rust = { start = { 9, 5 }, ['end'] = { 9, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate words written top left to bottom right', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript', -- + 'rust', -- + 'java' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } }, -- + java = { start = { 1, 1 }, ['end'] = { 4, 4 } }, -- + rust = { start = { 9, 5 }, ['end'] = { 9, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate words written bottom right to top left', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript', -- + 'rust', -- + 'java', -- + 'lua' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } }, -- + java = { start = { 1, 1 }, ['end'] = { 4, 4 } }, -- + lua = { start = { 8, 9 }, ['end'] = { 6, 7 } }, -- + rust = { start = { 9, 5 }, ['end'] = { 9, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate words written bottom left to top right', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript', -- + 'rust', -- + 'java', -- + 'lua', -- + 'lisp' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } }, -- + java = { start = { 1, 1 }, ['end'] = { 4, 4 } }, -- + lisp = { start = { 3, 6 }, ['end'] = { 6, 3 } }, -- + lua = { start = { 8, 9 }, ['end'] = { 6, 7 } }, -- + rust = { start = { 9, 5 }, ['end'] = { 9, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should locate words written top right to bottom left', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript', -- + 'rust', -- + 'java', -- + 'lua', -- + 'lisp', -- + 'ruby' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } }, -- + java = { start = { 1, 1 }, ['end'] = { 4, 4 } }, -- + lisp = { start = { 3, 6 }, ['end'] = { 6, 3 } }, -- + lua = { start = { 8, 9 }, ['end'] = { 6, 7 } }, -- + ruby = { start = { 8, 6 }, ['end'] = { 5, 9 } }, -- + rust = { start = { 9, 5 }, ['end'] = { 9, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should fail to locate a word that is not in the puzzle', function() + local grid = { -- + 'jefblpepre', -- + 'camdcimgtc', -- + 'oivokprjsm', -- + 'pbwasqroua', -- + 'rixilelhrs', -- + 'wolcqlirpc', -- + 'screeaumgr', -- + 'alxhpburyi', -- + 'jalaycalmp', -- + 'clojurermt' + } + local words = { -- + 'clojure', -- + 'elixir', -- + 'ecmascript', -- + 'rust', -- + 'java', -- + 'lua', -- + 'lisp', -- + 'ruby', -- + 'haskell' + } + local expected = { -- + clojure = { start = { 1, 10 }, ['end'] = { 7, 10 } }, -- + ecmascript = { start = { 10, 1 }, ['end'] = { 10, 10 } }, -- + elixir = { start = { 6, 5 }, ['end'] = { 1, 5 } }, -- + java = { start = { 1, 1 }, ['end'] = { 4, 4 } }, -- + lisp = { start = { 3, 6 }, ['end'] = { 6, 3 } }, -- + lua = { start = { 8, 9 }, ['end'] = { 6, 7 } }, -- + ruby = { start = { 8, 6 }, ['end'] = { 5, 9 } }, -- + rust = { start = { 9, 5 }, ['end'] = { 9, 2 } } + } + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should fail to locate words that are not on horizontal, vertical, or diagonal lines', function() + local grid = { -- + 'abc', -- + 'def' + } + local words = { -- + 'aef', -- + 'ced', -- + 'abf', -- + 'cbd' + } + local expected = {} + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should not concatenate different lines to find a horizontal word', function() + local grid = { -- + 'abceli', -- + 'xirdfg' + } + local words = { -- + 'elixir' + } + local expected = {} + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should not wrap around horizontally to find a word', function() + local grid = { -- + 'silabcdefp' + } + local words = { -- + 'lisp' + } + local expected = {} + assert.are.same(expected, WordSearch(grid).search(words)) + end) + + it('should not wrap around vertically to find a word', function() + local grid = { -- + 's', -- + 'u', -- + 'r', -- + 'a', -- + 'b', -- + 'c', -- + 't' + } + local words = { -- + 'rust' } - local first, last = WordSearch(puzzle).find('exercism') - assert.same({ 11, 2 }, first) - assert.same({ 4, 2 }, last) + local expected = {} + assert.are.same(expected, WordSearch(grid).search(words)) end) end) --- LuaFormatter on