Imagine a Cartesian plane and every Cell object represents a point in the plane (the plane will be the maze). When building my maze, I want to figure out whether a Cell object is a vertex (the four corner points) or just a border point (any cell that is on the edge of the maze, vertex points are also border points).
I need to know so that I can add neighboring cells as neighbors of the particular cell (I am creating a graph structure with nodes). Different borders have different requirements for what cells are neighbors (a top-right vertex for example cannot have a neighbor that is y + 1 or x + 1 as it is outside of the maze, whereas a bottom-left vertex cannot have y - 1 or x - 1).
How I tried to implement this was through a slew of if statements which I feel isn't a really good practice. So I wanted to ask whether there is a better way to know what type of coordinate a point is?
Here is how I did it:
private String typeOfBorderCell(Cell cell){
if (!isBorderCell(cell)){
throw new IllegalArgumentException("cell is not a border cell");
}
double x = cell.getCoordinate().getX();
double y = cell.getCoordinate().getY();
// Vertices
if (x == 0 && y == 0){
return "bottom-left";
}
else if (x == 0 && y == height - 1){
return "top-left";
}
else if (x == width - 1 && y == 0){
return "bottom-right";
}
else if (x == width - 1 && y == height - 1){
return "top-right";
}
// Non-Vertices
else if (x == 0 && (y > 0 && y < height - 1)){
return "left";
}
// and so on for the other three non-vertex borders
}
The height/width are the size of the maze, but I had to subtract 1 as the maze coordinates start at origin (0,0) thus a 5x5 maze goes up to a max of 4 for its y and 4 for its x.
Doing this, I would get a total of 8 conditional statements (and the method that uses this method would require a switch statement with 8 cases as well). Is there a more efficient way to do this without a bunch of conditional statements?
I find enums a reasonably elegant alternative to a long set of if
statements. Here's an example (using Java 8):
enum CellType {
OTHER(1, (x, y) -> true),
TOP(2, (x, y) -> y == HEIGHT - 1),
BOTTOM(2, (x, y) -> y == 0),
LEFT(2, (x, y) -> x == 0),
RIGHT(2, (x, y) -> x == WIDTH - 1),
TOP_LEFT(3, TOP, LEFT),
BOTTOM_RIGHT(3, BOTTOM, RIGHT),
TOP_RIGHT(3, TOP, RIGHT),
BOTTOM_LEFT(3, BOTTOM, LEFT);
private static final int HEIGHT = 5;
private static final int WIDTH = 5;
private final int precedence;
private final BiPredicate<Integer, Integer> test;
private CellType(int precedence, BiPredicate<Integer, Integer> test) {
this.precedence = precedence;
this.test = test;
}
private CellType(int precedence, CellType type1, CellType type2) {
this(precedence, type1.test.and(type2.test));
}
public static CellType valueOf(int x, int y) {
assert x >= 0 && x < WIDTH && y >= 0 && y < WIDTH;
return Arrays.stream(values())
.filter(ct -> ct.test.test(x, y))
.max(Comparator.comparingInt(ct -> ct.precedence))
.orElse(OTHER);
}
}
You can use this with code like CellType.valueOf(0, 4)
which will return CellType.TOP_LEFT
.
I prefer this idiom to sets of if statements because it puts the predicates in one place and makes them easy to identify and change.
It also will result in your 'cell type' not being a string which is a good idea if you later want to add logic to it. For example, you can avoid the switch statement you mention in your question by adding the logic to process the cell type to the enum itself. Also, comparing to strings is pretty error prone. You could change the string in one place and end up with hard to detect bugs. If you change an enum you get an immediate syntax error.
Here's a brief explanation of how it works. A BiPredicate
is a functional interface that takes two ints (x and y) and returns a boolean. Each of the CellType
members has a predicate that tests whether a given x and y represent a cell of that type. For the edge cell types, a lambda expression is used to provide the condition. For the vertices, the constructor takes two edge cell types and constructs a new predicate by testing if the cell satisfies both edge conditions. For example, TOP_LEFT
tests if the cell is on both the top and left edges.
The valueOf
method finds all cell types that satisfy a given cell then returns the one with the highest precedence. The precedence ensures that vertices are returned rather than edges. If no cell types match then it returns OTHER
(for non-edge non-vertix).
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments