/*******************************************************************************
* Copyright (c) 2015
*
* 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 jsettlers.algorithms.path.dijkstra;
import java.io.Serializable;
import java.util.EnumSet;
import java.util.Set;
import jsettlers.algorithms.path.IPathCalculatable;
import jsettlers.algorithms.path.InvalidStartPositionException;
import jsettlers.algorithms.path.Path;
import jsettlers.algorithms.path.astar.AbstractAStar;
import jsettlers.common.map.shapes.MapCircle;
import jsettlers.common.material.ESearchType;
import jsettlers.common.position.ShortPoint2D;
/**
* this class implements a strict dijkstra algorithm
*
* @author Andreas Eberle
*
*/
public final class DijkstraAlgorithm {
private static final byte[] directionIncreaseX = { -1, 0, 1, 1, 0, -1 };
private static final byte[] directionIncreaseY = { 0, 1, 1, 0, -1, -1 };
private static final float MAX_RADIUS_MULTIPLIER = 1f / MapCircle.Y_SCALE;
private final IDijkstraPathMap map;
private final short height, width;
private final AbstractAStar aStar;
public DijkstraAlgorithm(IDijkstraPathMap map, AbstractAStar aStar, short width, short height) {
this.map = map;
this.aStar = aStar;
this.width = width;
this.height = height;
}
public final Path find(final IPathCalculatable requester, final short cX, final short cY, final short minRadius, final short maxRadius,
final ESearchType type) {
if (!isInBounds(cX, cY)) {
throw new InvalidStartPositionException("dijkstra center position is not in bounds!", cX, cY);
}
// check center position (special case for minRadius <= 0
if (minRadius <= 0) {
map.setDijkstraSearched(cX, cY);
if (map.fitsSearchType(cX, cY, type, requester)) {
Path path = findPathTo(requester, cX, cY);
if (path != null)
return path;
}
}
for (short radius = minRadius; radius < maxRadius; radius++) {
short x = cX, y = (short) (cY - radius);
for (byte direction = 0; direction < 6; direction++) {
byte dx = directionIncreaseX[direction];
byte dy = directionIncreaseY[direction];
for (short length = 0; length < radius; length++) {
x += dx;
y += dy;
if (isInBounds(x, y)) {
map.setDijkstraSearched(x, y);
if (map.fitsSearchType(x, y, type, requester)) {
Path path = findPathTo(requester, x, y);
if (path != null)
return path;
}
}
}
}
}
return null;
}
private final Path findPathTo(IPathCalculatable requester, short tx, short ty) {
ShortPoint2D pos = requester.getPos();
return aStar.findPath(requester, pos.x, pos.y, tx, ty);
}
private final boolean isInBounds(short x, short y) {
return 0 <= x && x < width && 0 <= y && y < height;
}
public final static class DijkstraContinuableRequest implements Serializable {
private static final long serialVersionUID = -1350601280043056439L;
final short minRadius;
final short maxRadius;
final IPathCalculatable requester;
final short cX;
final short cY;
Set<ESearchType> searchTypes;
short radius;
public DijkstraContinuableRequest(final IPathCalculatable requester, short cX, short cY, short minRadius, short maxRadius) {
this.requester = requester;
this.cX = cX;
this.cY = cY;
this.minRadius = minRadius;
this.maxRadius = maxRadius;
this.searchTypes = EnumSet.noneOf(ESearchType.class);
this.radius = 0;
}
final short getRadiusSteps() {
return 6;
}
void setRadius(short radius) {
this.radius = (short) (radius - this.minRadius + 1);
}
public void setSearchTypes(Set<ESearchType> searchTypes) {
if (!this.searchTypes.equals(searchTypes)) {
this.searchTypes = searchTypes;
radius = 0;
}
}
public void reset() {
radius = 0;
}
}
public final Path find(DijkstraContinuableRequest request) {
if (!isInBounds(request.cX, request.cY)) {
throw new InvalidStartPositionException("dijkstra center position is not in bounds!", request.cX, request.cY);
}
MapCircle circle = new MapCircle(request.cX, request.cY, request.maxRadius * MAX_RADIUS_MULTIPLIER);
short radiusSteps = request.getRadiusSteps();
short radius = 1;
for (short deltaRadius = 0; deltaRadius < radiusSteps; deltaRadius++) {
radius = (short) ((deltaRadius + request.radius) % request.maxRadius + request.minRadius);
short x = request.cX, y = (short) (request.cY - radius);
for (byte direction = 0; direction < 6; direction++) {
byte dx = directionIncreaseX[direction];
byte dy = directionIncreaseY[direction];
for (short length = 0; length < radius; length++) {
x += dx;
y += dy;
if (circle.contains(x, y) && isInBounds(x, y)) {
map.setDijkstraSearched(x, y);
if (map.fitsSearchType(x, y, request.searchTypes, request.requester)) {
Path path = findPathTo(request.requester, x, y);
if (path != null) {
request.setRadius(radius);
return path;
}
}
}
}
}
}
request.setRadius(radius);
return null;
}
}