Przeglądaj źródła

Merge pull request #617 from exercism/word-search

word-search: add to track
FridaTveit 9 lat temu
rodzic
commit
6208d8f292

+ 5
- 0
config.json Wyświetl plik

@@ -256,6 +256,11 @@
256 256
       "topics": []
257 257
     },
258 258
     {
259
+      "slug": "word-search",
260
+      "difficulty": 7,
261
+      "topics": []
262
+    },
263
+    {
259 264
       "slug": "simple-linked-list",
260 265
       "difficulty": 7,
261 266
       "topics": []

+ 1
- 0
exercises/settings.gradle Wyświetl plik

@@ -69,4 +69,5 @@ include 'triangle'
69 69
 include 'trinary'
70 70
 include 'twelve-days'
71 71
 include 'word-count'
72
+include 'word-search'
72 73
 include 'wordy'

+ 18
- 0
exercises/word-search/build.gradle Wyświetl plik

@@ -0,0 +1,18 @@
1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+    mavenCentral()
7
+}
8
+
9
+dependencies {
10
+    testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+    testLogging {
15
+        exceptionFormat = 'full'
16
+        events = ["passed", "failed", "skipped"]
17
+    }
18
+}

+ 37
- 0
exercises/word-search/src/example/java/Pair.java Wyświetl plik

@@ -0,0 +1,37 @@
1
+class Pair {
2
+
3
+    private final int x;
4
+
5
+    private final int y;
6
+
7
+    Pair(final int x, final int y) {
8
+        this.y = y;
9
+        this.x = x;
10
+    }
11
+
12
+    int getX() {
13
+        return x;
14
+    }
15
+
16
+    int getY() {
17
+        return y;
18
+    }
19
+
20
+    @Override
21
+    public boolean equals(final Object o) {
22
+        if (this == o) return true;
23
+        if (o == null || getClass() != o.getClass()) return false;
24
+
25
+        Pair pair = (Pair) o;
26
+
27
+        return x == pair.x && y == pair.y;
28
+    }
29
+
30
+    @Override
31
+    public int hashCode() {
32
+        int result = x;
33
+        result = 31 * result + y;
34
+        return result;
35
+    }
36
+
37
+}

+ 29
- 0
exercises/word-search/src/example/java/WordLocation.java Wyświetl plik

@@ -0,0 +1,29 @@
1
+class WordLocation {
2
+
3
+    private final Pair startCoord;
4
+
5
+    private final Pair endCoord;
6
+
7
+    WordLocation(final Pair startCoord, final Pair endCoord) {
8
+        this.startCoord = startCoord;
9
+        this.endCoord = endCoord;
10
+    }
11
+
12
+    @Override
13
+    public boolean equals(final Object o) {
14
+        if (this == o) return true;
15
+        if (o == null || getClass() != o.getClass()) return false;
16
+
17
+        WordLocation that = (WordLocation) o;
18
+
19
+        return startCoord.equals(that.startCoord) && endCoord.equals(that.endCoord);
20
+    }
21
+
22
+    @Override
23
+    public int hashCode() {
24
+        int result = startCoord.hashCode();
25
+        result = 31 * result + endCoord.hashCode();
26
+        return result;
27
+    }
28
+
29
+}

+ 94
- 0
exercises/word-search/src/example/java/WordSearcher.java Wyświetl plik

@@ -0,0 +1,94 @@
1
+import java.util.*;
2
+import java.util.function.Function;
3
+import java.util.stream.Collectors;
4
+
5
+class WordSearcher {
6
+
7
+    private static final List<Pair> DIRECTIONS = Arrays.asList(
8
+            new Pair( 1,  0),  // N
9
+            new Pair( 1,  1),  // NE
10
+            new Pair( 0,  1),  // E
11
+            new Pair(-1,  1),  // SE
12
+            new Pair(-1,  0),  // S
13
+            new Pair(-1, -1),  // SW
14
+            new Pair( 0, -1),  // W
15
+            new Pair( 1, -1)); // NW
16
+
17
+    /*
18
+     * Search for multiple words in the given grid.
19
+     */
20
+    Map<String, Optional<WordLocation>> search(final Set<String> words, final char[][] grid) {
21
+        return words
22
+                .stream()
23
+                .collect(Collectors.toMap(Function.identity(), s -> search(s, grid)));
24
+    }
25
+
26
+    /*
27
+     * Search for a single word in the given grid.
28
+     */
29
+    private Optional<WordLocation> search(final CharSequence word, final char[][] grid) {
30
+        final int nRows = grid.length;
31
+        final int nCols = grid[0].length;
32
+
33
+        // 1-indexed
34
+        for (int c = 1; c <= nCols; c++) {
35
+            for (int r = 1; r <= nRows; r++) {
36
+                final Optional<WordLocation> wordLocation = search(word, grid, new Pair(c, r));
37
+                if (wordLocation.isPresent()) return wordLocation;
38
+            }
39
+        }
40
+
41
+        return Optional.empty();
42
+    }
43
+
44
+    /*
45
+     * Search for a single word starting at a given coordinate within the given grid.
46
+     */
47
+    private Optional<WordLocation> search(final CharSequence word, final char[][] grid, final Pair startCoord) {
48
+        if (grid[startCoord.getY() - 1][startCoord.getX() - 1] != word.charAt(0)) return Optional.empty();
49
+
50
+        for (final Pair direction : DIRECTIONS) {
51
+            final Optional<WordLocation> wordLocation = check(word, grid, startCoord, direction);
52
+            if (wordLocation.isPresent()) return wordLocation;
53
+        }
54
+
55
+        return Optional.empty();
56
+    }
57
+
58
+    /*
59
+     * Check whether a single word starts at a given coordinate and is aligned in a given direction within the given
60
+     * grid.
61
+     */
62
+    private Optional<WordLocation> check(
63
+            final CharSequence word,
64
+            final char[][] grid,
65
+            final Pair startCoord,
66
+            final Pair direction) {
67
+
68
+        final int nRows = grid.length;
69
+        final int nCols = grid[0].length;
70
+
71
+        Pair nextCoord = startCoord;
72
+
73
+        for (int charIndex = 1; charIndex < word.length(); charIndex++) {
74
+            nextCoord = new Pair(
75
+                    startCoord.getX() + charIndex * direction.getX(),
76
+                    startCoord.getY() + charIndex * direction.getY());
77
+
78
+            if (nextCoord.getY() < 1 ||
79
+                    nextCoord.getY() > nRows ||
80
+                    nextCoord.getX() < 1 ||
81
+                    nextCoord.getX() > nCols) {
82
+
83
+                return Optional.empty();
84
+            }
85
+
86
+            if (grid[nextCoord.getY() - 1][nextCoord.getX() - 1] != word.charAt(charIndex)) {
87
+                return Optional.empty();
88
+            }
89
+        }
90
+
91
+        return Optional.of(new WordLocation(startCoord, nextCoord));
92
+    }
93
+
94
+}

+ 37
- 0
exercises/word-search/src/main/java/Pair.java Wyświetl plik

@@ -0,0 +1,37 @@
1
+class Pair {
2
+
3
+    private final int x;
4
+
5
+    private final int y;
6
+
7
+    Pair(final int x, final int y) {
8
+        this.y = y;
9
+        this.x = x;
10
+    }
11
+
12
+    int getX() {
13
+        return x;
14
+    }
15
+
16
+    int getY() {
17
+        return y;
18
+    }
19
+
20
+    @Override
21
+    public boolean equals(final Object o) {
22
+        if (this == o) return true;
23
+        if (o == null || getClass() != o.getClass()) return false;
24
+
25
+        Pair pair = (Pair) o;
26
+
27
+        return x == pair.x && y == pair.y;
28
+    }
29
+
30
+    @Override
31
+    public int hashCode() {
32
+        int result = x;
33
+        result = 31 * result + y;
34
+        return result;
35
+    }
36
+
37
+}

+ 29
- 0
exercises/word-search/src/main/java/WordLocation.java Wyświetl plik

@@ -0,0 +1,29 @@
1
+class WordLocation {
2
+
3
+    private final Pair startCoord;
4
+
5
+    private final Pair endCoord;
6
+
7
+    WordLocation(final Pair startCoord, final Pair endCoord) {
8
+        this.startCoord = startCoord;
9
+        this.endCoord = endCoord;
10
+    }
11
+
12
+    @Override
13
+    public boolean equals(final Object o) {
14
+        if (this == o) return true;
15
+        if (o == null || getClass() != o.getClass()) return false;
16
+
17
+        WordLocation that = (WordLocation) o;
18
+
19
+        return startCoord.equals(that.startCoord) && endCoord.equals(that.endCoord);
20
+    }
21
+
22
+    @Override
23
+    public int hashCode() {
24
+        int result = startCoord.hashCode();
25
+        result = 31 * result + endCoord.hashCode();
26
+        return result;
27
+    }
28
+
29
+}

+ 278
- 0
exercises/word-search/src/test/java/WordSearcherTest.java Wyświetl plik

@@ -0,0 +1,278 @@
1
+import org.junit.Before;
2
+import org.junit.Test;
3
+
4
+import java.util.*;
5
+
6
+import static org.junit.Assert.assertEquals;
7
+
8
+/*
9
+ * version: 1.0.0
10
+ */
11
+public class WordSearcherTest {
12
+
13
+    private WordSearcher wordSearcher;
14
+
15
+    @Before
16
+    public void setUp() {
17
+        wordSearcher = new WordSearcher();
18
+    }
19
+
20
+    @Test
21
+    public void testLocatesWordsWrittenLeftToRight() {
22
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
23
+        expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10))));
24
+
25
+        Set<String> searchWords = expectedLocations.keySet();
26
+
27
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
28
+                searchWords,
29
+                new char[][]{
30
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
31
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
32
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
33
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
34
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
35
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
36
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
37
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
38
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
39
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}
40
+                }
41
+        );
42
+
43
+        assertEquals(expectedLocations, actualLocations);
44
+    }
45
+
46
+    @Test
47
+    public void testLocatesWordsWrittenRightToLeft() {
48
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
49
+        expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10))));
50
+        expectedLocations.put("elixir",  Optional.of(new WordLocation(new Pair(6,  5), new Pair(1,  5))));
51
+
52
+        Set<String> searchWords = expectedLocations.keySet();
53
+
54
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
55
+                searchWords,
56
+                new char[][]{
57
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
58
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
59
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
60
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
61
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
62
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
63
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
64
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
65
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
66
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}
67
+                }
68
+        );
69
+
70
+        assertEquals(expectedLocations, actualLocations);
71
+    }
72
+
73
+    @Test
74
+    public void testLocatesWordsWrittenTopToBottom() {
75
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
76
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
77
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
78
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
79
+
80
+        Set<String> searchWords = expectedLocations.keySet();
81
+
82
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
83
+                searchWords,
84
+                new char[][]{
85
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
86
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
87
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
88
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
89
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
90
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
91
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
92
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
93
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
94
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}
95
+                }
96
+        );
97
+
98
+        assertEquals(expectedLocations, actualLocations);
99
+    }
100
+
101
+    @Test
102
+    public void testLocatesWordsWrittenBottomToTop() {
103
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
104
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
105
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
106
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
107
+        expectedLocations.put("rust",        Optional.of(new WordLocation(new Pair( 9,  5), new Pair( 9,  2))));
108
+
109
+        Set<String> searchWords = expectedLocations.keySet();
110
+
111
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
112
+                searchWords,
113
+                new char[][]{
114
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
115
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
116
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
117
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
118
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
119
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
120
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
121
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
122
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
123
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}});
124
+
125
+        assertEquals(expectedLocations, actualLocations);
126
+    }
127
+
128
+    @Test
129
+    public void testLocatesWordsWrittenTopLeftToBottomRight() {
130
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
131
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
132
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
133
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
134
+        expectedLocations.put("rust",        Optional.of(new WordLocation(new Pair( 9,  5), new Pair( 9,  2))));
135
+        expectedLocations.put("java",        Optional.of(new WordLocation(new Pair( 1,  1), new Pair( 4,  4))));
136
+
137
+        Set<String> searchWords = expectedLocations.keySet();
138
+
139
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
140
+                searchWords,
141
+                new char[][]{
142
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
143
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
144
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
145
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
146
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
147
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
148
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
149
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
150
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
151
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}});
152
+
153
+        assertEquals(expectedLocations, actualLocations);
154
+    }
155
+
156
+    @Test
157
+    public void testLocatesWordsWrittenBottomRightToTopLeft() {
158
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
159
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
160
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
161
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
162
+        expectedLocations.put("rust",        Optional.of(new WordLocation(new Pair( 9,  5), new Pair( 9,  2))));
163
+        expectedLocations.put("java",        Optional.of(new WordLocation(new Pair( 1,  1), new Pair( 4,  4))));
164
+        expectedLocations.put("lua",         Optional.of(new WordLocation(new Pair( 8,  9), new Pair( 6,  7))));
165
+
166
+        Set<String> searchWords = expectedLocations.keySet();
167
+
168
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
169
+                searchWords,
170
+                new char[][]{
171
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
172
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
173
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
174
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
175
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
176
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
177
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
178
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
179
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
180
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}});
181
+
182
+        assertEquals(expectedLocations, actualLocations);
183
+    }
184
+
185
+    @Test
186
+    public void testLocatesWordsWrittenBottomLeftToTopRight() {
187
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
188
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
189
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
190
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
191
+        expectedLocations.put("rust",        Optional.of(new WordLocation(new Pair( 9,  5), new Pair( 9,  2))));
192
+        expectedLocations.put("java",        Optional.of(new WordLocation(new Pair( 1,  1), new Pair( 4,  4))));
193
+        expectedLocations.put("lua",         Optional.of(new WordLocation(new Pair( 8,  9), new Pair( 6,  7))));
194
+        expectedLocations.put("lisp",        Optional.of(new WordLocation(new Pair( 3,  6), new Pair( 6,  3))));
195
+
196
+        Set<String> searchWords = expectedLocations.keySet();
197
+
198
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
199
+                searchWords,
200
+                new char[][]{
201
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
202
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
203
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
204
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
205
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
206
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
207
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
208
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
209
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
210
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}});
211
+
212
+        assertEquals(expectedLocations, actualLocations);
213
+    }
214
+
215
+    @Test
216
+    public void testLocatesWordsWrittenTopRightToBottomLeft() {
217
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
218
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
219
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
220
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
221
+        expectedLocations.put("rust",        Optional.of(new WordLocation(new Pair( 9,  5), new Pair( 9,  2))));
222
+        expectedLocations.put("java",        Optional.of(new WordLocation(new Pair( 1,  1), new Pair( 4,  4))));
223
+        expectedLocations.put("lua",         Optional.of(new WordLocation(new Pair( 8,  9), new Pair( 6,  7))));
224
+        expectedLocations.put("lisp",        Optional.of(new WordLocation(new Pair( 3,  6), new Pair( 6,  3))));
225
+        expectedLocations.put("ruby",        Optional.of(new WordLocation(new Pair( 8,  6), new Pair( 5,  9))));
226
+
227
+        Set<String> searchWords = expectedLocations.keySet();
228
+
229
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
230
+                searchWords,
231
+                new char[][]{
232
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
233
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
234
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
235
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
236
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
237
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
238
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
239
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
240
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
241
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}});
242
+
243
+        assertEquals(expectedLocations, actualLocations);
244
+    }
245
+
246
+    @Test
247
+    public void testFailsToLocateAWordsThatIsNotInThePuzzle() {
248
+        Map<String, Optional<WordLocation>> expectedLocations = new HashMap<>();
249
+        expectedLocations.put("clojure",     Optional.of(new WordLocation(new Pair( 1, 10), new Pair( 7, 10))));
250
+        expectedLocations.put("elixir",      Optional.of(new WordLocation(new Pair( 6,  5), new Pair( 1,  5))));
251
+        expectedLocations.put("ecmascript",  Optional.of(new WordLocation(new Pair(10,  1), new Pair(10, 10))));
252
+        expectedLocations.put("rust",        Optional.of(new WordLocation(new Pair( 9,  5), new Pair( 9,  2))));
253
+        expectedLocations.put("java",        Optional.of(new WordLocation(new Pair( 1,  1), new Pair( 4,  4))));
254
+        expectedLocations.put("lua",         Optional.of(new WordLocation(new Pair( 8,  9), new Pair( 6,  7))));
255
+        expectedLocations.put("lisp",        Optional.of(new WordLocation(new Pair( 3,  6), new Pair( 6,  3))));
256
+        expectedLocations.put("ruby",        Optional.of(new WordLocation(new Pair( 8,  6), new Pair( 5,  9))));
257
+        expectedLocations.put("haskell",     Optional.empty());
258
+
259
+        Set<String> searchWords = expectedLocations.keySet();
260
+
261
+        Map<String, Optional<WordLocation>> actualLocations = wordSearcher.search(
262
+                searchWords,
263
+                new char[][]{
264
+                        {'j', 'e', 'f', 'b', 'l', 'p', 'e', 'p', 'r', 'e'},
265
+                        {'c', 'a', 'm', 'd', 'c', 'i', 'm', 'g', 't', 'c'},
266
+                        {'o', 'i', 'v', 'o', 'k', 'p', 'r', 'j', 's', 'm'},
267
+                        {'p', 'b', 'w', 'a', 's', 'q', 'r', 'o', 'u', 'a'},
268
+                        {'r', 'i', 'x', 'i', 'l', 'e', 'l', 'h', 'r', 's'},
269
+                        {'w', 'o', 'l', 'c', 'q', 'l', 'i', 'r', 'p', 'c'},
270
+                        {'s', 'c', 'r', 'e', 'e', 'a', 'u', 'm', 'g', 'r'},
271
+                        {'a', 'l', 'x', 'h', 'p', 'b', 'u', 'r', 'y', 'i'},
272
+                        {'j', 'a', 'l', 'a', 'y', 'c', 'a', 'l', 'm', 'p'},
273
+                        {'c', 'l', 'o', 'j', 'u', 'r', 'e', 'r', 'm', 't'}});
274
+
275
+        assertEquals(expectedLocations, actualLocations);
276
+    }
277
+
278
+}