diff --git a/exercises/practice/word-search/.meta/config.json b/exercises/practice/word-search/.meta/config.json index b280c95e0d..d915c69783 100644 --- a/exercises/practice/word-search/.meta/config.json +++ b/exercises/practice/word-search/.meta/config.json @@ -5,6 +5,7 @@ "contributors": [ "hyuko21", "ivanvotti", + "jagdish-15", "msomji", "rchavarria", "SleeplessByte" diff --git a/exercises/practice/word-search/.meta/proof.ci.js b/exercises/practice/word-search/.meta/proof.ci.js index c6e5d5540a..63fb510148 100644 --- a/exercises/practice/word-search/.meta/proof.ci.js +++ b/exercises/practice/word-search/.meta/proof.ci.js @@ -26,13 +26,11 @@ function searchHorizontally({ word, grid }) { } rowIndex += 1; } - return false; + return undefined; } function flipCoordinates(coords) { - if (!coords) { - return undefined; - } + if (!coords) return undefined; return { start: coords.start.reverse(), end: coords.end.reverse(), @@ -41,110 +39,85 @@ function flipCoordinates(coords) { function flipGrid(grid) { return [...grid[0]] - .map((col, c) => grid.map((row, r) => grid[r][c])) + .map((_, c) => grid.map((row) => row[c])) .map((row) => row.join('')); } -function diagonalFind(r, c, word, grid, rIncrement, outOfRange, buildCoords) { +function diagonalFind(r, c, word, grid, rIncrement, cIncrement) { let currentRow = r; let currentColumn = c; let foundLetters = ''; const startR = r + 1; const startC = c + 1; - let result; - word.split('').forEach((letter) => { + + for (const letter of word) { + // Bounds check if ( - !outOfRange( - currentRow, - currentColumn, - word.length, - grid[currentRow].length, - foundLetters.length, - ) + currentRow < 0 || + currentRow >= grid.length || + currentColumn < 0 || + currentColumn >= grid[currentRow].length ) { - const currLetterInGrid = grid[currentRow].charAt(currentColumn); - currentColumn += 1; - if (currLetterInGrid === letter) { - foundLetters += currLetterInGrid; - if (foundLetters === word) { - result = buildCoords(startR, startC, currentRow, currentColumn); - } - currentRow += rIncrement; - } + return undefined; } - }); - return result; -} - -function findAWordDiagonallyTopDown(r, c, word, grid) { - function outOfRange(row, column, words, columns, letters) { - return ( - row > columns - words + letters || column > columns - words + letters - ); - } - - function buildCoords(startR, startC, row, column) { - return { - start: [startR, startC], - end: [row + 1, column], - }; - } - - return diagonalFind(r, c, word, grid, 1, outOfRange, buildCoords); -} -function findAWordDiagonallyBottomUp(r, c, word, grid) { - function outOfRange(row, column, words, columns, letters) { - return row < words - letters - 1 || column > columns - words + letters; - } + const currLetterInGrid = grid[currentRow].charAt(currentColumn); + if (currLetterInGrid === letter) { + foundLetters += currLetterInGrid; + if (foundLetters === word) { + return { + start: [startR, startC], + end: [currentRow + 1, currentColumn + 1], + }; + } + } else { + return undefined; + } - function buildCoords(startR, startC, row, column) { - return { - start: [startR, startC], - end: [row + 1, column], - }; + currentRow += rIncrement; + currentColumn += cIncrement; } - return diagonalFind(r, c, word, grid, -1, outOfRange, buildCoords); -} - -function formatCoordinates(coords, isReversed) { - return { - true: { - start: coords.end, - end: coords.start, - }, - false: coords, - }[isReversed]; + return undefined; } -function searchDiagonally({ word, grid, isReversed = false, fromTop = true }) { +function searchDiagonally({ word, grid, fromTop = true, reversed = false }) { const rIncrement = fromTop ? 1 : -1; const startRow = fromTop ? 0 : grid.length - 1; - const endRow = fromTop ? (r) => r < grid.length : (r) => r > 0; - const findDirection = fromTop - ? findAWordDiagonallyTopDown - : findAWordDiagonallyBottomUp; + const endRow = fromTop ? (r) => r < grid.length : (r) => r >= 0; for (let r = startRow; endRow(r); r += rIncrement) { for (let c = 0; c < grid[r].length; c += 1) { - const possibleCoords = findDirection(r, c, word, grid); - if (possibleCoords) { - return formatCoordinates(possibleCoords, isReversed); + const dirs = [ + [1, 1], // top-left to bottom-right + [1, -1], // top-right to bottom-left + [-1, 1], // bottom-left to top-right + [-1, -1], // bottom-right to top-left + ]; + + for (const [dr, dc] of dirs) { + const possible = diagonalFind(r, c, word, grid, dr, dc); + if (possible) { + if (reversed) { + return { start: possible.end, end: possible.start }; + } + return possible; + } } } } - if (!isReversed) { - // now find the reversed version + // Try reversed word + if (!reversed) { const reversedWord = [...word].reverse().join(''); return searchDiagonally({ word: reversedWord, grid, - isReversed: true, fromTop, + reversed: true, }); } + return undefined; } @@ -152,7 +125,7 @@ function findWordInAnyDirection(word, grid) { return ( searchHorizontally({ word, grid }) || flipCoordinates(searchHorizontally({ word, grid: flipGrid(grid) })) || - searchDiagonally({ word, grid }) || + searchDiagonally({ word, grid, fromTop: true }) || searchDiagonally({ word, grid, fromTop: false }) ); } diff --git a/exercises/practice/word-search/.meta/tests.toml b/exercises/practice/word-search/.meta/tests.toml index 68c3b60631..3f98113d7e 100644 --- a/exercises/practice/word-search/.meta/tests.toml +++ b/exercises/practice/word-search/.meta/tests.toml @@ -68,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.spec.js b/exercises/practice/word-search/word-search.spec.js index 3a558a4a83..aaa5f77795 100644 --- a/exercises/practice/word-search/word-search.spec.js +++ b/exercises/practice/word-search/word-search.spec.js @@ -1,7 +1,7 @@ import { describe, expect, test, xtest } from '@jest/globals'; import WordSearch from './word-search'; -describe('single line grids', () => { +describe('Word Search', () => { test('Should accept an initial game grid', () => { const grid = ['jefblpepre']; const wordSearch = new WordSearch(grid); @@ -9,14 +9,14 @@ describe('single line grids', () => { expect(wordSearch instanceof WordSearch).toEqual(true); }); - xtest('can accept a target search word', () => { + xtest('Can accept a target search word', () => { const grid = ['jefblpepre']; const wordSearch = new WordSearch(grid); expect(wordSearch.find(['glasnost'])).toEqual({ glasnost: undefined }); }); - xtest('should locate a word written left to right', () => { + xtest('Should locate a word written left to right', () => { const grid = ['clojurermt']; const expectedResults = { clojure: { @@ -29,7 +29,7 @@ describe('single line grids', () => { expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a left to right word in a different position', () => { + xtest('Can locate a left to right word in a different position', () => { const grid = ['mtclojurer']; const expectedResults = { clojure: { @@ -37,12 +37,13 @@ describe('single line grids', () => { end: [1, 9], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a different left to right word', () => { + xtest('Can locate a different left to right word', () => { const grid = ['coffeelplx']; const expectedResults = { coffee: { @@ -50,11 +51,13 @@ describe('single line grids', () => { end: [1, 6], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['coffee'])).toEqual(expectedResults); }); - xtest('can locate that different left to right word in a different position', () => { + + xtest('Can locate that different left to right word in a different position', () => { const grid = ['xcoffeezlp']; const expectedResults = { coffee: { @@ -62,14 +65,13 @@ describe('single line grids', () => { end: [1, 7], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['coffee'])).toEqual(expectedResults); }); -}); -describe('multi line grids', () => { - xtest('can locate a left to right word in a two line grid', () => { + xtest('Can locate a left to right word in a two line grid', () => { const grid = ['jefblpepre', 'clojurermt']; const expectedResults = { @@ -83,7 +85,8 @@ describe('multi line grids', () => { expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a left to right word in a different position in a two line grid', () => { + + xtest('Can locate a left to right word in a different position in a two line grid', () => { const grid = ['jefblpepre', 'tclojurerm']; const expectedResults = { clojure: { @@ -91,11 +94,13 @@ describe('multi line grids', () => { end: [2, 8], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a left to right word in a three line grid', () => { + + xtest('Can locate a left to right word in a three line grid', () => { const grid = ['camdcimgtc', 'jefblpepre', 'clojurermt']; const expectedResults = { clojure: { @@ -103,12 +108,13 @@ describe('multi line grids', () => { end: [3, 7], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a left to right word in a ten line grid', () => { + xtest('Can locate a left to right word in a ten line grid', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -128,12 +134,13 @@ describe('multi line grids', () => { end: [10, 7], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a left to right word in a different position in a ten line grid', () => { + xtest('Can locate a left to right word in a different position in a ten line grid', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -153,11 +160,13 @@ describe('multi line grids', () => { end: [9, 7], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['clojure'])).toEqual(expectedResults); }); - xtest('can locate a different left to right word in a ten line grid', () => { + + xtest('Can locate a different left to right word in a ten line grid', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -170,20 +179,20 @@ describe('multi line grids', () => { 'clojurermt', 'jalaycalmp', ]; + const expectedResults = { scree: { start: [7, 1], end: [7, 5], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['scree'])).toEqual(expectedResults); }); -}); -describe('can find multiple words', () => { - xtest('can find two words written left to right', () => { + xtest('Can find two words written left to right', () => { const grid = [ 'aefblpepre', 'camdcimgtc', @@ -197,6 +206,7 @@ describe('can find multiple words', () => { 'clojurermt', 'xjavamtzlp', ]; + const expectedResults = { clojure: { start: [10, 1], @@ -207,14 +217,13 @@ describe('can find multiple words', () => { end: [11, 5], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['java', 'clojure'])).toEqual(expectedResults); }); -}); -describe('different directions', () => { - xtest('should locate a single word written right to left', () => { + xtest('Should locate a single word written right to left', () => { const grid = ['rixilelhrs']; const expectedResults = { elixir: { @@ -222,11 +231,13 @@ describe('different directions', () => { end: [1, 1], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['elixir'])).toEqual(expectedResults); }); - xtest('should locate multiple words written in different horizontal directions', () => { + + xtest('Should locate multiple words written in different horizontal directions', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -239,6 +250,7 @@ describe('different directions', () => { 'jalaycalmp', 'clojurermt', ]; + const expectedResults = { clojure: { start: [10, 1], @@ -249,14 +261,13 @@ describe('different directions', () => { end: [5, 1], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['elixir', 'clojure'])).toEqual(expectedResults); }); -}); -describe('vertical directions', () => { - xtest('should locate words written top to bottom', () => { + xtest('Should locate words written top to bottom', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -269,6 +280,7 @@ describe('vertical directions', () => { 'jalaycalmp', 'clojurermt', ]; + const expectedResults = { clojure: { start: [10, 1], @@ -283,13 +295,15 @@ describe('vertical directions', () => { end: [10, 10], }, }; + const wordSearch = new WordSearch(grid); expect(wordSearch.find(['elixir', 'clojure', 'ecmascript'])).toEqual( expectedResults, ); }); - xtest('should locate words written bottom to top', () => { + + xtest('Should locate words written bottom to top', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -302,6 +316,7 @@ describe('vertical directions', () => { 'jalaycalmp', 'clojurermt', ]; + const expectedResults = { clojure: { start: [10, 1], @@ -320,13 +335,15 @@ describe('vertical directions', () => { end: [2, 9], }, }; + const wordSearch = new WordSearch(grid); expect( wordSearch.find(['elixir', 'clojure', 'ecmascript', 'rust']), ).toEqual(expectedResults); }); - xtest('should locate words written top left to bottom right', () => { + + xtest('Should locate words written top left to bottom right', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -339,6 +356,7 @@ describe('vertical directions', () => { 'jalaycalmp', 'clojurermt', ]; + const expectedResults = { clojure: { start: [10, 1], @@ -361,13 +379,70 @@ describe('vertical directions', () => { end: [4, 4], }, }; + const wordSearch = new WordSearch(grid); expect( wordSearch.find(['clojure', 'elixir', 'ecmascript', 'rust', 'java']), ).toEqual(expectedResults); }); - xtest('should locate words written bottom right to top left', () => { + + xtest('Should locate words written bottom right to top left', () => { + const grid = [ + 'jefblpepre', + 'camdcimgtc', + 'oivokprjsm', + 'pbwasqroua', + 'rixilelhrs', + 'wolcqlirpc', + 'screeaumgr', + 'alxhpburyi', + 'jalaycalmp', + 'clojurermt', + ]; + + const expectedResults = { + clojure: { + start: [10, 1], + end: [10, 7], + }, + elixir: { + start: [5, 6], + end: [5, 1], + }, + ecmascript: { + start: [1, 10], + end: [10, 10], + }, + rust: { + start: [5, 9], + end: [2, 9], + }, + java: { + start: [1, 1], + end: [4, 4], + }, + lua: { + start: [9, 8], + end: [7, 6], + }, + }; + + const wordSearch = new WordSearch(grid); + + expect( + wordSearch.find([ + 'clojure', + 'elixir', + 'ecmascript', + 'rust', + 'java', + 'lua', + ]), + ).toEqual(expectedResults); + }); + + xtest('Should locate words written bottom left to top right', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -406,7 +481,12 @@ describe('vertical directions', () => { start: [9, 8], end: [7, 6], }, + lisp: { + start: [6, 3], + end: [3, 6], + }, }; + const wordSearch = new WordSearch(grid); expect( @@ -417,10 +497,12 @@ describe('vertical directions', () => { 'rust', 'java', 'lua', + 'lisp', ]), ).toEqual(expectedResults); }); - xtest('should locate words written bottom left to top right', () => { + + xtest('Should locate words written top right to bottom left', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -433,6 +515,7 @@ describe('vertical directions', () => { 'jalaycalmp', 'clojurermt', ]; + const expectedResults = { clojure: { start: [10, 1], @@ -462,6 +545,10 @@ describe('vertical directions', () => { start: [6, 3], end: [3, 6], }, + ruby: { + start: [6, 8], + end: [9, 5], + }, }; const wordSearch = new WordSearch(grid); @@ -475,10 +562,12 @@ describe('vertical directions', () => { 'java', 'lua', 'lisp', + 'ruby', ]), ).toEqual(expectedResults); }); - xtest('should locate words written top right to bottom left', () => { + + xtest('Should fail to locate a word that is not in the puzzle', () => { const grid = [ 'jefblpepre', 'camdcimgtc', @@ -525,7 +614,9 @@ describe('vertical directions', () => { start: [6, 8], end: [9, 5], }, + haskell: undefined, }; + const wordSearch = new WordSearch(grid); expect( @@ -538,31 +629,61 @@ describe('vertical directions', () => { 'lua', 'lisp', 'ruby', + 'haskell', ]), ).toEqual(expectedResults); }); - describe("word doesn't exist", () => { - xtest('should fail to locate a word that is not in the puzzle', () => { - const grid = [ - 'jefblpepre', - 'camdcimgtc', - 'oivokprjsm', - 'pbwasqroua', - 'rixilelhrs', - 'wolcqlirpc', - 'screeaumgr', - 'alxhpburyi', - 'jalaycalmp', - 'clojurermt', - ]; - - const expectedResults = { - fail: undefined, - }; - const wordSearch = new WordSearch(grid); - - expect(wordSearch.find(['fail'])).toEqual(expectedResults); - }); + xtest('Should fail to locate words that are not on horizontal, vertical, or diagonal lines', () => { + const grid = ['abc', 'def']; + + const expectedResults = { + aef: undefined, + ced: undefined, + abf: undefined, + cbd: undefined, + }; + + const wordSearch = new WordSearch(grid); + + expect(wordSearch.find(['aef', 'ced', 'abf', 'cbd'])).toEqual( + expectedResults, + ); + }); + + xtest('Should not concatenate different lines to find a horizontal word', () => { + const grid = ['abceli', 'xirdfg']; + + const expectedResults = { + elixir: undefined, + }; + + const wordSearch = new WordSearch(grid); + + expect(wordSearch.find(['elixir'])).toEqual(expectedResults); + }); + + xtest('Should not wrap around horizontally to find a word', () => { + const grid = ['silabcdefp']; + + const expectedResults = { + lisp: undefined, + }; + + const wordSearch = new WordSearch(grid); + + expect(wordSearch.find(['lisp'])).toEqual(expectedResults); + }); + + xtest('Should not wrap around vertically to find a word', () => { + const grid = ['s', 'u', 'r', 'a', 'b', 'c', 't']; + + const expectedResults = { + rust: undefined, + }; + + const wordSearch = new WordSearch(grid); + + expect(wordSearch.find(['rust'])).toEqual(expectedResults); }); });