aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/fr
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/fr')
-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
2 files changed, 141 insertions, 2 deletions
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}