Przeglądaj źródła

Merge pull request #174 from stkent/minesweeper

minesweeper: add to track
Matthew Morgan 9 lat temu
rodzic
commit
afc3bd254f

+ 7
- 1
config.json Wyświetl plik

@@ -45,7 +45,8 @@
45 45
     "beer-song",
46 46
     "difference-of-squares",
47 47
     "largest-series-product",
48
-    "queen-attack"
48
+    "queen-attack",
49
+    "minesweeper"
49 50
   ],
50 51
   "exercises": [
51 52
     {
@@ -257,6 +258,11 @@
257 258
       "slug": "queen-attack",
258 259
       "difficulty": 1,
259 260
       "topics": []
261
+    },
262
+    {
263
+      "slug": "minesweeper",
264
+      "difficulty": 1,
265
+      "topics": []
260 266
     }
261 267
   ],
262 268
   "deprecated": [

+ 17
- 0
exercises/minesweeper/build.gradle Wyświetl plik

@@ -0,0 +1,17 @@
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
+test {
13
+  testLogging {
14
+    exceptionFormat = 'full'
15
+    events = ["passed", "failed", "skipped"]
16
+  }
17
+}

+ 111
- 0
exercises/minesweeper/src/example/java/MinesweeperBoard.java Wyświetl plik

@@ -0,0 +1,111 @@
1
+import java.util.ArrayList;
2
+import java.util.List;
3
+import java.util.Set;
4
+import java.util.stream.Collectors;
5
+
6
+final class MinesweeperBoard {
7
+
8
+    private static final char MINE_CHAR = '*';
9
+
10
+    private static final char SPACE_CHAR = ' ';
11
+
12
+    private final List<String> rawRepresentation;
13
+
14
+    private final int numberOfRows;
15
+
16
+    private final int numberOfColumns;
17
+
18
+    MinesweeperBoard(final List<String> rawRepresentation) {
19
+        validateInputBoard(rawRepresentation);
20
+        this.rawRepresentation = rawRepresentation;
21
+        this.numberOfRows = rawRepresentation.size();
22
+        this.numberOfColumns = rawRepresentation.isEmpty() ? 0 : rawRepresentation.get(0).length();
23
+    }
24
+
25
+    List<String> getAnnotatedRepresentation() throws IllegalArgumentException {
26
+        final List<String> result = new ArrayList<>();
27
+
28
+        for (int rowNumber = 0; rowNumber < numberOfRows; rowNumber++) {
29
+            result.add(getAnnotatedRow(rowNumber));
30
+        }
31
+
32
+        return result;
33
+    }
34
+
35
+    private String getAnnotatedRow(final int rowNumber) {
36
+        String result = "";
37
+
38
+        for (int columnNumber = 0; columnNumber < numberOfColumns; columnNumber++) {
39
+            result += getCellAnnotation(rowNumber, columnNumber);
40
+        }
41
+
42
+        return result;
43
+    }
44
+
45
+    private char getCellAnnotation(final int rowNumber, final int columnNumber) {
46
+        // If (rowNumber, columnNumber) is a mine, we're done.
47
+        if (rawRepresentation.get(rowNumber).charAt(columnNumber) == MINE_CHAR) {
48
+            return MINE_CHAR;
49
+        }
50
+
51
+        final int mineCount = computeMineCountAround(rowNumber, columnNumber);
52
+
53
+        // If computed count is positive, add it to the annotated row. Otherwise, add a blank space.
54
+        return mineCount > 0 ? Character.forDigit(mineCount, 10) : SPACE_CHAR;
55
+    }
56
+
57
+    private int computeMineCountAround(final int rowNumber, final int columnNumber) {
58
+        int result = 0;
59
+
60
+        // Compute row and column ranges to inspect (respecting board edges).
61
+        final int minRowToInspect = Math.max(rowNumber - 1, 0);
62
+        final int maxRowToInspect = Math.min(rowNumber + 1, numberOfRows - 1);
63
+        final int minColToInspect = Math.max(columnNumber - 1, 0);
64
+        final int maxColToInspect = Math.min(columnNumber + 1, numberOfColumns - 1);
65
+
66
+        // Count mines in the cells surrounding (row, col).
67
+        for (int rowToInspect = minRowToInspect; rowToInspect <= maxRowToInspect; rowToInspect++) {
68
+            for (int colToInspect = minColToInspect; colToInspect <= maxColToInspect; colToInspect++) {
69
+                if (rawRepresentation.get(rowToInspect).charAt(colToInspect) == MINE_CHAR) {
70
+                    result += 1;
71
+                }
72
+            }
73
+        }
74
+
75
+        return result;
76
+    }
77
+
78
+    private void validateInputBoard(final List<String> inputBoard) throws IllegalArgumentException {
79
+        validateInputBoardIsNotNull(inputBoard);
80
+
81
+        if (inputBoard.isEmpty()) {
82
+            return;
83
+        }
84
+
85
+        validateInputBoardCharacters(inputBoard);
86
+        validateInputBoardColumnCounts(inputBoard);
87
+    }
88
+
89
+    private void validateInputBoardIsNotNull(final List<String> inputBoard) throws IllegalArgumentException {
90
+        if (inputBoard == null) {
91
+            throw new IllegalArgumentException("Input board may not be null.");
92
+        }
93
+    }
94
+
95
+    private void validateInputBoardCharacters(final List<String> inputBoard) throws IllegalArgumentException {
96
+        final String allBoardCharacters = String.join("", inputBoard);
97
+
98
+        if (!allBoardCharacters.matches("^[ *]*$")) {
99
+            throw new IllegalArgumentException("Input board can only contain the characters ' ' and '*'.");
100
+        }
101
+    }
102
+
103
+    private void validateInputBoardColumnCounts(final List<String> inputBoard) throws IllegalArgumentException {
104
+        final Set<Integer> setOfColumnCounts = inputBoard.stream().map(String::length).collect(Collectors.toSet());
105
+
106
+        if (setOfColumnCounts.size() > 1) {
107
+            throw new IllegalArgumentException("Input board rows must all have the same number of columns.");
108
+        }
109
+    }
110
+
111
+}

+ 5
- 0
exercises/minesweeper/src/main/java/MinesweeperBoard.java Wyświetl plik

@@ -0,0 +1,5 @@
1
+public final class MinesweeperBoard {
2
+
3
+
4
+
5
+}

+ 295
- 0
exercises/minesweeper/src/test/java/MinesweeperBoardTest.java Wyświetl plik

@@ -0,0 +1,295 @@
1
+import org.junit.Ignore;
2
+import org.junit.Rule;
3
+import org.junit.Test;
4
+import org.junit.rules.ExpectedException;
5
+
6
+import java.util.Arrays;
7
+import java.util.Collections;
8
+import java.util.List;
9
+
10
+import static org.junit.Assert.assertEquals;
11
+
12
+public final class MinesweeperBoardTest {
13
+
14
+    /*
15
+     * See https://github.com/junit-team/junit4/wiki/Rules for information on JUnit Rules in general and
16
+     * ExpectedExceptions in particular.
17
+     */
18
+    @Rule
19
+    public ExpectedException expectedException = ExpectedException.none();
20
+
21
+    @Test
22
+    public void testInputBoardWithNoRowsAndNoColumns() {
23
+        final List<String> inputBoard = Collections.emptyList();
24
+        final List<String> expectedAnnotatedRepresentation = Collections.emptyList();
25
+        final List<String> actualAnnotatedRepresentation
26
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
27
+
28
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
29
+    }
30
+
31
+    @Ignore
32
+    @Test
33
+    public void testInputBoardWithOneRowAndNoColumns() {
34
+        final List<String> inputBoard = Collections.singletonList("");
35
+        final List<String> expectedAnnotatedRepresentation = Collections.singletonList("");
36
+        final List<String> actualAnnotatedRepresentation
37
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
38
+
39
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
40
+    }
41
+
42
+    @Ignore
43
+    @Test
44
+    public void testInputBoardWithNoMines() {
45
+        final List<String> inputBoard = Arrays.asList(
46
+                "   ",
47
+                "   ",
48
+                "   "
49
+        );
50
+
51
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
52
+                "   ",
53
+                "   ",
54
+                "   "
55
+        );
56
+
57
+        final List<String> actualAnnotatedRepresentation
58
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
59
+
60
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
61
+    }
62
+
63
+    @Ignore
64
+    @Test
65
+    public void testInputBoardWithOnlyMines() {
66
+        final List<String> inputBoard = Arrays.asList(
67
+                "***",
68
+                "***",
69
+                "***"
70
+        );
71
+
72
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
73
+                "***",
74
+                "***",
75
+                "***"
76
+        );
77
+
78
+        final List<String> actualAnnotatedRepresentation
79
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
80
+
81
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
82
+    }
83
+
84
+    @Ignore
85
+    @Test
86
+    public void testInputBoardWithSingleMineAtCenter() {
87
+        final List<String> inputBoard = Arrays.asList(
88
+                "   ",
89
+                " * ",
90
+                "   "
91
+        );
92
+
93
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
94
+                "111",
95
+                "1*1",
96
+                "111"
97
+        );
98
+
99
+        final List<String> actualAnnotatedRepresentation
100
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
101
+
102
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
103
+    }
104
+
105
+    @Ignore
106
+    @Test
107
+    public void testInputBoardWithMinesAroundPerimeter() {
108
+        final List<String> inputBoard = Arrays.asList(
109
+                "***",
110
+                "* *",
111
+                "***"
112
+        );
113
+
114
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
115
+                "***",
116
+                "*8*",
117
+                "***"
118
+        );
119
+
120
+        final List<String> actualAnnotatedRepresentation
121
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
122
+
123
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
124
+    }
125
+
126
+    @Ignore
127
+    @Test
128
+    public void testInputBoardWithSingleRowAndTwoMines() {
129
+        final List<String> inputBoard = Collections.singletonList(
130
+                " * * "
131
+        );
132
+
133
+        final List<String> expectedAnnotatedRepresentation = Collections.singletonList(
134
+                "1*2*1"
135
+        );
136
+
137
+        final List<String> actualAnnotatedRepresentation
138
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
139
+
140
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
141
+    }
142
+
143
+    @Ignore
144
+    @Test
145
+    public void testInputBoardWithSingleRowAndTwoMinesAtEdges() {
146
+        final List<String> inputBoard = Collections.singletonList(
147
+                "*   *"
148
+        );
149
+
150
+        final List<String> expectedAnnotatedRepresentation = Collections.singletonList(
151
+                "*1 1*"
152
+        );
153
+
154
+        final List<String> actualAnnotatedRepresentation
155
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
156
+
157
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
158
+    }
159
+
160
+    @Ignore
161
+    @Test
162
+    public void testInputBoardWithSingleColumnAndTwoMines() {
163
+        final List<String> inputBoard = Arrays.asList(
164
+                " ",
165
+                "*",
166
+                " ",
167
+                "*",
168
+                " "
169
+        );
170
+
171
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
172
+                "1",
173
+                "*",
174
+                "2",
175
+                "*",
176
+                "1"
177
+        );
178
+
179
+        final List<String> actualAnnotatedRepresentation
180
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
181
+
182
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
183
+    }
184
+
185
+    @Ignore
186
+    @Test
187
+    public void testInputBoardWithSingleColumnAndTwoMinesAtEdges() {
188
+        final List<String> inputBoard = Arrays.asList(
189
+                "*",
190
+                " ",
191
+                " ",
192
+                " ",
193
+                "*"
194
+        );
195
+
196
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
197
+                "*",
198
+                "1",
199
+                " ",
200
+                "1",
201
+                "*"
202
+        );
203
+
204
+        final List<String> actualAnnotatedRepresentation
205
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
206
+
207
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
208
+    }
209
+
210
+    @Ignore
211
+    @Test
212
+    public void testInputBoardWithMinesInCross() {
213
+        final List<String> inputBoard = Arrays.asList(
214
+                "  *  ",
215
+                "  *  ",
216
+                "*****",
217
+                "  *  ",
218
+                "  *  "
219
+        );
220
+
221
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
222
+                " 2*2 ",
223
+                "25*52",
224
+                "*****",
225
+                "25*52",
226
+                " 2*2 "
227
+        );
228
+
229
+        final List<String> actualAnnotatedRepresentation
230
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
231
+
232
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
233
+    }
234
+
235
+    @Ignore
236
+    @Test
237
+    public void testLargeInputBoard() {
238
+        final List<String> inputBoard = Arrays.asList(
239
+                " *  * ",
240
+                "  *   ",
241
+                "    * ",
242
+                "   * *",
243
+                " *  * ",
244
+                "      "
245
+        );
246
+
247
+        final List<String> expectedAnnotatedRepresentation = Arrays.asList(
248
+                "1*22*1",
249
+                "12*322",
250
+                " 123*2",
251
+                "112*4*",
252
+                "1*22*2",
253
+                "111111"
254
+        );
255
+
256
+        final List<String> actualAnnotatedRepresentation
257
+                = new MinesweeperBoard(inputBoard).getAnnotatedRepresentation();
258
+
259
+        assertEquals(expectedAnnotatedRepresentation, actualAnnotatedRepresentation);
260
+    }
261
+
262
+    @Ignore
263
+    @Test
264
+    public void testNullInputBoardIsRejected() {
265
+        expectedException.expect(IllegalArgumentException.class);
266
+        expectedException.expectMessage("Input board may not be null.");
267
+
268
+        new MinesweeperBoard(null);
269
+    }
270
+
271
+    @Ignore
272
+    @Test
273
+    public void testInputBoardWithInvalidSymbolsIsRejected() {
274
+        expectedException.expect(IllegalArgumentException.class);
275
+        expectedException.expectMessage("Input board can only contain the characters ' ' and '*'.");
276
+
277
+        new MinesweeperBoard(Collections.singletonList(" * & "));
278
+    }
279
+
280
+    @Ignore
281
+    @Test
282
+    public void testInputBoardWithInconsistentRowLengthsIsRejected() {
283
+        expectedException.expect(IllegalArgumentException.class);
284
+        expectedException.expectMessage("Input board rows must all have the same number of columns.");
285
+
286
+        new MinesweeperBoard(Arrays.asList(
287
+                "*",
288
+                "**",
289
+                "* *",
290
+                "*  *",
291
+                "*   *"
292
+        ));
293
+    }
294
+
295
+}

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

@@ -17,6 +17,7 @@ include 'hello-world'
17 17
 include 'largest-series-product'
18 18
 include 'linked-list'
19 19
 include 'luhn'
20
+include 'minesweeper'
20 21
 include 'meetup'
21 22
 include 'nth-prime'
22 23
 include 'nucleotide-count'