diff --git a/config.json b/config.json index 9929ecf..0480036 100644 --- a/config.json +++ b/config.json @@ -930,6 +930,14 @@ "prerequisites": [], "difficulty": 7 }, + { + "slug": "word-search", + "name": "Word Search", + "uuid": "0606b8eb-1dbb-4d9c-8c97-a2ed5187347b", + "practices": [], + "prerequisites": [], + "difficulty": 7 + }, { "slug": "alphametics", "name": "Alphametics", diff --git a/exercises/practice/word-search/.busted b/exercises/practice/word-search/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/word-search/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/word-search/.docs/instructions.md b/exercises/practice/word-search/.docs/instructions.md new file mode 100644 index 0000000..e2d08aa --- /dev/null +++ b/exercises/practice/word-search/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +In word search puzzles you get a square of letters and have to find specific words in them. + +For example: + +```text +jefblpepre +camdcimgtc +oivokprjsm +pbwasqroua +rixilelhrs +wolcqlirpc +screeaumgr +alxhpburyi +jalaycalmp +clojurermt +``` + +There are several programming languages hidden in the above square. + +Words can be hidden in all kinds of directions: left-to-right, right-to-left, vertical and diagonal. + +Given a puzzle and a list of words return the location of the first and last letter of each word. diff --git a/exercises/practice/word-search/.meta/config.json b/exercises/practice/word-search/.meta/config.json new file mode 100644 index 0000000..6e40295 --- /dev/null +++ b/exercises/practice/word-search/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "word_search.moon" + ], + "test": [ + "word_search_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Create a program to solve a word search puzzle." +} diff --git a/exercises/practice/word-search/.meta/example.moon b/exercises/practice/word-search/.meta/example.moon new file mode 100644 index 0000000..c1676cc --- /dev/null +++ b/exercises/practice/word-search/.meta/example.moon @@ -0,0 +1,29 @@ +class WordSearch + new: (grid) => + @matrix = [ [c for c in row\gmatch '.'] for row in *grid] + @height = #@matrix + @width = #@matrix[1] + + find: (words) => {word, @findWord word for word in *words} + + findWord: (word) => + for row = 1, @height + for col = 1, @width + if @matrix[row][col] == word\sub 1,1 + first = {row: row, column: col} + for dr = -1, 1 + for dc = -1, 1 + continue if dr == 0 and dc == 0 + last = @findInDirection row, col, dr, dc, word + if last + return {start: first, end: last} + nil + + findInDirection: (row, col, dr, dc, word) => + local rr, cc + for i = 1, #word + rr = row + (i - 1) * dr + cc = col + (i - 1) * dc + return nil if rr < 1 or rr > @height or cc < 1 or cc > @width + return nil if @matrix[rr][cc] != word\sub i,i + return {row: rr, column: cc} diff --git a/exercises/practice/word-search/.meta/spec_generator.moon b/exercises/practice/word-search/.meta/spec_generator.moon new file mode 100644 index 0000000..99311e9 --- /dev/null +++ b/exercises/practice/word-search/.meta/spec_generator.moon @@ -0,0 +1,24 @@ +import indent, string_list, table_dump, is_json_null from require 'spec_helpers' + +{ + module_name: 'WordSearch', + + generate_test: (case, level) -> + expected = case.expected + for word, position in pairs expected + if is_json_null position + expected[word] = nil + + lines = { + "grid = #{string_list case.input.grid, level, inline: 1}", + "words = #{string_list case.input.wordsToSearchFor, level, inline: 1}" + "expected = #{table_dump expected, level}", + "assert.are.same expected, WordSearch(grid)\\find(words)" + } + table.concat [indent line, level for line in *lines], '\n' + + test_helpers: [[ + assert\set_parameter "TableFormatLevel", 3 +]] + +} diff --git a/exercises/practice/word-search/.meta/tests.toml b/exercises/practice/word-search/.meta/tests.toml new file mode 100644 index 0000000..3f98113 --- /dev/null +++ b/exercises/practice/word-search/.meta/tests.toml @@ -0,0 +1,82 @@ +# 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" + +[6b22bcc5-6cbf-4674-931b-d2edbff73132] +description = "Should locate one word written left to right" + +[ff462410-434b-442d-9bc3-3360c75f34a8] +description = "Should locate the same word written left to right in a different position" + +[a02febae-6347-443e-b99c-ab0afb0b8fca] +description = "Should locate a different left to right word" + +[e42e9987-6304-4e13-8232-fa07d5280130] +description = "Should locate that different left to right word in a different position" + +[9bff3cee-49b9-4775-bdfb-d55b43a70b2f] +description = "Should locate a left to right word in two line grid" + +[851a35fb-f499-4ec1-9581-395a87903a22] +description = "Should locate a left to right word in three line grid" + +[2f3dcf84-ba7d-4b75-8b8d-a3672b32c035] +description = "Should locate a left to right word in ten line grid" + +[006d4856-f365-4e84-a18c-7d129ce9eefb] +description = "Should locate that left to right word in a different position in a ten line grid" + +[eff7ac9f-ff11-443e-9747-40850c12ab60] +description = "Should locate a different left to right word in a ten line grid" + +[dea39f86-8c67-4164-8884-13bfc48bd13b] +description = "Should locate multiple words" + +[29e6a6a5-f80c-48a6-8e68-05bbbe187a09] +description = "Should locate a single word written right to left" + +[3cf34428-b43f-48b6-b332-ea0b8836011d] +description = "Should locate multiple words written in different horizontal directions" + +[2c8cd344-a02f-464b-93b6-8bf1bd890003] +description = "Should locate words written top to bottom" + +[9ee1e43d-e59d-4c32-9a5f-6a22d4a1550f] +description = "Should locate words written bottom to top" + +[6a21a676-f59e-4238-8e88-9f81015afae9] +description = "Should locate words written top left to bottom right" + +[c9125189-1861-4b0d-a14e-ba5dab29ca7c] +description = "Should locate words written bottom right to top left" + +[b19e2149-7fc5-41ec-a8a9-9bc6c6c38c40] +description = "Should locate words written bottom left to top right" + +[69e1d994-a6d7-4e24-9b5a-db76751c2ef8] +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.moon b/exercises/practice/word-search/word_search.moon new file mode 100644 index 0000000..fec14a7 --- /dev/null +++ b/exercises/practice/word-search/word_search.moon @@ -0,0 +1,6 @@ +class WordSearch + new: (grid) => + error 'Implement the constructor' + + find: (words) => + error 'Implement the "find" method' diff --git a/exercises/practice/word-search/word_search_spec.moon b/exercises/practice/word-search/word_search_spec.moon new file mode 100644 index 0000000..74e6033 --- /dev/null +++ b/exercises/practice/word-search/word_search_spec.moon @@ -0,0 +1,930 @@ +WordSearch = require 'word_search' + +describe 'word-search:', -> + assert\set_parameter "TableFormatLevel", 3 + + it 'Should accept an initial game grid and a target search word', -> + grid = {'jefblpepre'} + words = {'clojure'} + expected = {} + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate one word written left to right', -> + grid = {'clojurermt'} + words = {'clojure'} + expected = { + clojure: { + start: { + column: 1 + row: 1 + } + end: { + column: 7 + row: 1 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate the same word written left to right in a different position', -> + grid = {'mtclojurer'} + words = {'clojure'} + expected = { + clojure: { + start: { + column: 3 + row: 1 + } + end: { + column: 9 + row: 1 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate a different left to right word', -> + grid = {'coffeelplx'} + words = {'coffee'} + expected = { + coffee: { + start: { + column: 1 + row: 1 + } + end: { + column: 6 + row: 1 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate that different left to right word in a different position', -> + grid = {'xcoffeezlp'} + words = {'coffee'} + expected = { + coffee: { + start: { + column: 2 + row: 1 + } + end: { + column: 7 + row: 1 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate a left to right word in two line grid', -> + grid = { + 'jefblpepre', + 'tclojurerm', + } + words = {'clojure'} + expected = { + clojure: { + start: { + column: 2 + row: 2 + } + end: { + column: 8 + row: 2 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate a left to right word in three line grid', -> + grid = { + 'camdcimgtc', + 'jefblpepre', + 'clojurermt', + } + words = {'clojure'} + expected = { + clojure: { + start: { + column: 1 + row: 3 + } + end: { + column: 7 + row: 3 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate a left to right word in ten line grid', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = {'clojure'} + expected = { + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate that left to right word in a different position in a ten line grid', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'clojurermt', + 'jalaycalmp', + } + words = {'clojure'} + expected = { + clojure: { + start: { + column: 1 + row: 9 + } + end: { + column: 7 + row: 9 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate a different left to right word in a ten line grid', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'fortranftw', + 'alxhpburyi', + 'clojurermt', + 'jalaycalmp', + } + words = {'fortran'} + expected = { + fortran: { + start: { + column: 1 + row: 7 + } + end: { + column: 7 + row: 7 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate multiple words', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'fortranftw', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'fortran', + 'clojure', + } + expected = { + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + fortran: { + start: { + column: 1 + row: 7 + } + end: { + column: 7 + row: 7 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate a single word written right to left', -> + grid = {'rixilelhrs'} + words = {'elixir'} + expected = { + elixir: { + start: { + column: 6 + row: 1 + } + end: { + column: 1 + row: 1 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate multiple words written in different horizontal directions', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'elixir', + 'clojure', + } + expected = { + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate words written top to bottom', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + } + expected = { + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate words written bottom to top', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + } + expected = { + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + rust: { + start: { + column: 9 + row: 5 + } + end: { + column: 9 + row: 2 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate words written top left to bottom right', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + 'java', + } + expected = { + rust: { + start: { + column: 9 + row: 5 + } + end: { + column: 9 + row: 2 + } + } + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + java: { + start: { + column: 1 + row: 1 + } + end: { + column: 4 + row: 4 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate words written bottom right to top left', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + 'java', + 'lua', + } + expected = { + lua: { + start: { + column: 8 + row: 9 + } + end: { + column: 6 + row: 7 + } + } + rust: { + start: { + column: 9 + row: 5 + } + end: { + column: 9 + row: 2 + } + } + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + java: { + start: { + column: 1 + row: 1 + } + end: { + column: 4 + row: 4 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate words written bottom left to top right', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + 'java', + 'lua', + 'lisp', + } + expected = { + lisp: { + start: { + column: 3 + row: 6 + } + end: { + column: 6 + row: 3 + } + } + lua: { + start: { + column: 8 + row: 9 + } + end: { + column: 6 + row: 7 + } + } + rust: { + start: { + column: 9 + row: 5 + } + end: { + column: 9 + row: 2 + } + } + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + java: { + start: { + column: 1 + row: 1 + } + end: { + column: 4 + row: 4 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should locate words written top right to bottom left', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + 'java', + 'lua', + 'lisp', + 'ruby', + } + expected = { + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + lisp: { + start: { + column: 3 + row: 6 + } + end: { + column: 6 + row: 3 + } + } + lua: { + start: { + column: 8 + row: 9 + } + end: { + column: 6 + row: 7 + } + } + rust: { + start: { + column: 9 + row: 5 + } + end: { + column: 9 + row: 2 + } + } + ruby: { + start: { + column: 8 + row: 6 + } + end: { + column: 5 + row: 9 + } + } + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + java: { + start: { + column: 1 + row: 1 + } + end: { + column: 4 + row: 4 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should fail to locate a word that is not in the puzzle', -> + grid = { + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + } + words = { + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + 'java', + 'lua', + 'lisp', + 'ruby', + 'haskell', + } + expected = { + clojure: { + start: { + column: 1 + row: 10 + } + end: { + column: 7 + row: 10 + } + } + rust: { + start: { + column: 9 + row: 5 + } + end: { + column: 9 + row: 2 + } + } + elixir: { + start: { + column: 6 + row: 5 + } + end: { + column: 1 + row: 5 + } + } + lisp: { + start: { + column: 3 + row: 6 + } + end: { + column: 6 + row: 3 + } + } + ruby: { + start: { + column: 8 + row: 6 + } + end: { + column: 5 + row: 9 + } + } + ecmascript: { + start: { + column: 10 + row: 1 + } + end: { + column: 10 + row: 10 + } + } + lua: { + start: { + column: 8 + row: 9 + } + end: { + column: 6 + row: 7 + } + } + java: { + start: { + column: 1 + row: 1 + } + end: { + column: 4 + row: 4 + } + } + } + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should fail to locate words that are not on horizontal, vertical, or diagonal lines', -> + grid = { + 'abc', + 'def', + } + words = { + 'aef', + 'ced', + 'abf', + 'cbd', + } + expected = {} + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should not concatenate different lines to find a horizontal word', -> + grid = { + 'abceli', + 'xirdfg', + } + words = {'elixir'} + expected = {} + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should not wrap around horizontally to find a word', -> + grid = {'silabcdefp'} + words = {'lisp'} + expected = {} + assert.are.same expected, WordSearch(grid)\find(words) + + pending 'Should not wrap around vertically to find a word', -> + grid = { + 's', + 'u', + 'r', + 'a', + 'b', + 'c', + 't', + } + words = {'rust'} + expected = {} + assert.are.same expected, WordSearch(grid)\find(words)