aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpacien2018-01-14 18:13:22 +0100
committerpacien2018-01-14 18:13:22 +0100
commit1f161aae93903dc8747f5a16c4d86cfccea6698b (patch)
treea507242ddab761f78b02e0dabf9ad556f4a9333a
parenta494bba689e1a95dd17287287c6394cb7a0cb7ef (diff)
downloadwallj-1f161aae93903dc8747f5a16c4d86cfccea6698b.tar.gz
Implement board validator
Signed-off-by: pacien <pacien.trangirard@pacien.net>
-rw-r--r--src/docs/user.md3
-rw-r--r--src/main/java/fr/umlv/java/wallj/board/BoardParser.java1
-rw-r--r--src/main/java/fr/umlv/java/wallj/board/BoardValidator.java142
-rw-r--r--src/test/java/fr/umlv/java/wallj/board/BoardValidatorTest.java35
-rw-r--r--src/test/resources/maps/bigInvalid.txt10
-rw-r--r--src/test/resources/maps/bigValid.txt10
6 files changed, 198 insertions, 3 deletions
diff --git a/src/docs/user.md b/src/docs/user.md
index bb24c53..735daf5 100644
--- a/src/docs/user.md
+++ b/src/docs/user.md
@@ -1,7 +1,7 @@
1--- 1---
2title: "BSc IN S5 / OOP with Java / Wall-J / User manual" 2title: "BSc IN S5 / OOP with Java / Wall-J / User manual"
3author: [Pacien TRAN-GIRARD, Adam NAILI] 3author: [Pacien TRAN-GIRARD, Adam NAILI]
4date: 2018-01-03 4date: 2018-01-14
5... 5...
6 6
7# Preamble 7# Preamble
@@ -108,6 +108,7 @@ A world is defined as valid if its blocks fulfill the following criteria:
108* The interior space formed by bounding blocks must be unique and simple. 108* The interior space formed by bounding blocks must be unique and simple.
109* Reachable blocks are either adjacent or belong to the interior space. 109* Reachable blocks are either adjacent or belong to the interior space.
110* The world must contain at least one trash can and one garbage block. 110* The world must contain at least one trash can and one garbage block.
111* The world must have enough free tiles to contain all droppable bombs.
111 112
112Only valid worlds can be loaded into the game. 113Only valid worlds can be loaded into the game.
113 114
diff --git a/src/main/java/fr/umlv/java/wallj/board/BoardParser.java b/src/main/java/fr/umlv/java/wallj/board/BoardParser.java
index c6d0cd8..001bdc8 100644
--- a/src/main/java/fr/umlv/java/wallj/board/BoardParser.java
+++ b/src/main/java/fr/umlv/java/wallj/board/BoardParser.java
@@ -59,7 +59,6 @@ public final class BoardParser {
59 public static Board parse(Path filePath) throws IOException { 59 public static Board parse(Path filePath) throws IOException {
60 return buildBoard(Files.lines(filePath) 60 return buildBoard(Files.lines(filePath)
61 .filter(s -> !s.isEmpty()) 61 .filter(s -> !s.isEmpty())
62 .map(String::trim)
63 .map(BoardParser::parseLine) 62 .map(BoardParser::parseLine)
64 .collect(Collectors.toList())); 63 .collect(Collectors.toList()));
65 } 64 }
diff --git a/src/main/java/fr/umlv/java/wallj/board/BoardValidator.java b/src/main/java/fr/umlv/java/wallj/board/BoardValidator.java
index 0e51bc6..e238955 100644
--- a/src/main/java/fr/umlv/java/wallj/board/BoardValidator.java
+++ b/src/main/java/fr/umlv/java/wallj/board/BoardValidator.java
@@ -1,5 +1,145 @@
1package fr.umlv.java.wallj.board; 1package fr.umlv.java.wallj.board;
2 2
3import fr.umlv.java.wallj.model.BlockType;
4
5import java.util.*;
6import java.util.function.Predicate;
7import java.util.stream.IntStream;
8
9/**
10 * Board validity checker.
11 *
12 * @author Pacien TRAN-GIRARD
13 */
3public class BoardValidator { 14public class BoardValidator {
4 //TODO 15
16 /**
17 * A validation exception, witness of validation error(s).
18 */
19 public static class ValidationException extends Exception {
20 ValidationException(String msg) {
21 super(msg);
22 }
23
24 ValidationException() {
25 super();
26 }
27 }
28
29 /**
30 * Constraint predicates.
31 */
32 public static final class Constraint {
33
34 public static final int NB_DROPPABLE_BOMBS = 3;
35
36 private static boolean inBoard(TileVec2 dim, TileVec2 v) {
37 return v.getRow() >= 0 && v.getRow() < dim.getRow() &&
38 v.getCol() >= 0 && v.getRow() < dim.getCol();
39 }
40
41 /**
42 * Checks that the supplied board is well-bounded.
43 */
44 public static boolean isBounded(Board b) {
45 TileVec2 dim = b.getDim();
46
47 boolean hBounded = IntStream.range(0, dim.getCol())
48 .allMatch(col -> b.getBlockTypeAt(TileVec2.of(col, 0)).isBounding() &&
49 b.getBlockTypeAt(TileVec2.of(col, dim.getRow() - 1)).isBounding());
50
51 boolean vBounded = IntStream.range(0, dim.getRow())
52 .allMatch(row -> b.getBlockTypeAt(TileVec2.of(0, row)).isBounding() &&
53 b.getBlockTypeAt(TileVec2.of(dim.getCol() - 1, row)).isBounding());
54
55 return hBounded && vBounded;
56 }
57
58 /**
59 * Checks that the supplied board is hollow, i.e. has a unique and simple interior.
60 */
61 public static boolean isHollow(Board b) {
62 TileVec2 dim = b.getDim();
63 Optional<TileVec2> start = b.stream().filter(e -> e.getValue().isTraversable()).findAny().map(Map.Entry::getKey);
64 if (!start.isPresent()) return false;
65
66 Set<TileVec2> reachable = new HashSet<>();
67 Queue<TileVec2> toVisit = new ArrayDeque<>();
68 reachable.add(start.get());
69 toVisit.add(start.get());
70
71 TileVec2 tile;
72 while ((tile = toVisit.poll()) != null)
73 tile.neighbors().stream()
74 .filter(neighbor -> inBoard(dim, neighbor))
75 .filter(neighbor -> b.getBlockTypeAt(neighbor) != null && b.getBlockTypeAt(neighbor).isTraversable())
76 .filter(neighbor -> !reachable.contains(neighbor))
77 .forEach(neighbor -> {
78 reachable.add(neighbor);
79 toVisit.add(neighbor);
80 });
81
82 return reachable.size() == b.stream().filter(e -> e.getValue().isTraversable()).count();
83 }
84
85 /**
86 * Checks that every block marked as reachable has at least one traversable neighbor.
87 */
88 public static boolean hasActualReachableBlocks(Board b) {
89 TileVec2 dim = b.getDim();
90 return b.stream()
91 .filter(blockEntry -> blockEntry.getValue().mustBeReachable())
92 .allMatch(blockEntry -> blockEntry.getKey().neighbors().stream()
93 .filter(neighbor -> inBoard(dim, neighbor))
94 .anyMatch(neighbor -> b.getBlockTypeAt(neighbor).isTraversable()));
95 }
96
97 /**
98 * Checks that the supplied board has all the mandatory blocks.
99 */
100 public static boolean hasMandatoryBlocks(Board b) {
101 return b.stream().anyMatch(block -> block.getValue() == BlockType.TRASH) &&
102 b.stream().anyMatch(block -> block.getValue() == BlockType.GARBAGE) &&
103 b.stream().filter(block -> block.getValue() == BlockType.FREE).count() >= NB_DROPPABLE_BOMBS;
104 }
105
106 private Constraint() {
107 // static class
108 }
109
110 }
111
112 private final Board board;
113 private final ValidationException errors = new ValidationException();
114
115 /**
116 * @param board the board to validate
117 */
118 public BoardValidator(Board board) {
119 this.board = board;
120 }
121
122 /**
123 * Tests the board against a given validator, using the supplied error message if the validation fails.
124 *
125 * @param validator a validity test
126 * @param msg a failure message
127 * @return the board validator
128 */
129 public BoardValidator validate(Predicate<Board> validator, String msg) {
130 if (!validator.test(board)) errors.addSuppressed(new ValidationException(msg));
131 return this;
132 }
133
134 /**
135 * @return the validated board
136 * @throws ValidationException in case of failure
137 */
138 public Board get() throws ValidationException {
139 if (errors.getSuppressed().length > 0)
140 throw errors;
141 else
142 return board;
143 }
144
5} 145}
diff --git a/src/test/java/fr/umlv/java/wallj/board/BoardValidatorTest.java b/src/test/java/fr/umlv/java/wallj/board/BoardValidatorTest.java
new file mode 100644
index 0000000..7be657d
--- /dev/null
+++ b/src/test/java/fr/umlv/java/wallj/board/BoardValidatorTest.java
@@ -0,0 +1,35 @@
1package fr.umlv.java.wallj.board;
2
3import org.junit.jupiter.api.Assertions;
4import org.junit.jupiter.api.Test;
5
6import java.io.IOException;
7import java.net.URISyntaxException;
8import java.nio.file.Path;
9import java.nio.file.Paths;
10
11/**
12 * @author Pacien TRAN-GIRARD
13 */
14final class BoardValidatorTest {
15
16 private Path getResourcePath(String str) throws URISyntaxException {
17 return Paths.get(getClass().getResource(str).toURI());
18 }
19
20 @Test
21 void testConstraints() throws URISyntaxException, IOExceptio