/*
* The MIT License (MIT)
*
* FXGL - JavaFX Game Library
*
* Copyright (c) 2015-2017 AlmasB (almaslvl@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.almasb.fxgl.ai.pathfinding;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* A* grid containing A* nodes.
*
* @author Almas Baimagambetov (AlmasB) (almaslvl@gmail.com)
*/
public class AStarGrid {
private AStarLogic logic = new AStarLogic();
private AStarNode[][] grid;
/**
* Constructs A* grid with A* nodes with given width and height.
* All nodes are initially {@link NodeState#WALKABLE}
*
* @param width grid width
* @param height grid height
*/
public AStarGrid(int width, int height) {
if (width < 1 || height < 1)
throw new IllegalArgumentException("width and height cannot < 1");
grid = new AStarNode[width][height];
for (int y = 0; y < grid[0].length; y++) {
for (int x = 0; x < grid.length; x++) {
grid[x][y] = new AStarNode(x, y, NodeState.WALKABLE);
}
}
}
/**
* @return grid width
*/
public final int getWidth() {
return grid.length;
}
/**
* @return grid height
*/
public final int getHeight() {
return grid[0].length;
}
/**
*
* @param x x coord
* @param y y coord
* @return true IFF the point is within the grid
*/
public final boolean isWithin(int x, int y) {
return x >= 0 && x < getWidth()
&& y >= 0 && y < getHeight();
}
/**
* Convenience method to set state of all nodes to given state.
*
* @param state node state
*/
public final void setStateForAllNodes(NodeState state) {
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
getNode(x, y).setState(state);
}
}
}
/**
* Set state of the node at x, y.
*
* @param x the x coordinate
* @param y the y coordinate
* @param state the state
*/
public final void setNodeState(int x, int y, NodeState state) {
getNode(x, y).setState(state);
}
/**
* Returns state of the node at a, y.
*
* @param x the x coordinate
* @param y the y coordinate
* @return the state
*/
public final NodeState getNodeState(int x, int y) {
return getNode(x, y).getState();
}
/**
* Returns a list of A* nodes from start to target.
* The list will include target.
* Return an empty list if the path doesn't exist.
*
* @param startX start node x
* @param startY start node y
* @param targetX target node x
* @param targetY target node y
* @return the path
*/
public final List<AStarNode> getPath(int startX, int startY, int targetX, int targetY) {
return logic.getPath(grid, getNode(startX, startY), getNode(targetX, targetY));
}
/**
* Returns a node at x, y. There is no bounds checking.
*
* @param x the x coordinate
* @param y the y coordinate
* @return A* node at x, y
*/
public final AStarNode getNode(int x, int y) {
return grid[x][y];
}
/**
* @return a random node from the grid
*/
public final AStarNode getRandomNode() {
int x = (int) (Math.random() * getWidth());
int y = (int) (Math.random() * getHeight());
return getNode(x, y);
}
/**
* @param predicate filter condition
* @return a random node that passes the filter or {@link Optional#empty()}
* if no such node exists
*/
public final Optional<AStarNode> getRandomNode(Predicate<AStarNode> predicate) {
List<AStarNode> filtered = getNodes().stream()
.filter(predicate)
.collect(Collectors.toList());
if (filtered.isEmpty())
return Optional.empty();
int index = (int) (Math.random() * filtered.size());
return Optional.of(filtered.get(index));
}
/**
* @return underlying grid of nodes
*/
public final AStarNode[][] getGrid() {
return grid;
}
/**
* @return all grid nodes
*/
public final List<AStarNode> getNodes() {
List<AStarNode> nodes = new ArrayList<>();
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
nodes.add(getNode(x, y));
}
}
return nodes;
}
}