Package com.pointcliki.dizgruntled.utils

Source Code of com.pointcliki.dizgruntled.utils.PathFinder$PossibleTile

package com.pointcliki.dizgruntled.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TreeSet;

import com.pointcliki.dizgruntled.LevelScene;
import com.pointcliki.dizgruntled.grunt.GruntState;
import com.pointcliki.dizgruntled.logic.Grunt;
import com.pointcliki.dizgruntled.map.Map;
import com.pointcliki.grid.GridCoordinate;

public class PathFinder {

  protected LevelScene fScene;
  protected Map fMap;
 
  public PathFinder(LevelScene scene) {
    fScene = scene;
    fMap = fScene.mapManager().map();
  }
 
  public List<GridCoordinate> calculate(GridCoordinate xy, GridCoordinate finish, int maxIterations) {
    PriorityQueue<PossibleTile> tiles = new PriorityQueue<PossibleTile>();
    HashMap<String, PossibleTile> searched = new HashMap<String, PossibleTile>();
   
    PossibleTile start = new PossibleTile(xy, null, 0, xy.distance(finish));
    PossibleTile end = null;
   
    tiles.add(start);
    searched.put(xy.toString(), start);
   
    while (maxIterations > 0 && !tiles.isEmpty()) {
      maxIterations--;
     
      PossibleTile poll = tiles.poll();
      // Check for end
      if (poll.xy.equals(finish)) {
        end = poll;
        break;
      }
      // Check for passable
      if (!passable(poll.xy)) continue;
      // Add neighbours
      ArrayList<PossibleTile> poss = new ArrayList<PossibleTile>(8);
      poss.add(new PossibleTile(poll.xy.add(GridCoordinate.NORTH), poll, poll.pathWeight + 1, poll.xy.add(GridCoordinate.NORTH).distance(finish)));
      poss.add(new PossibleTile(poll.xy.add(GridCoordinate.EAST), poll, poll.pathWeight + 1, poll.xy.add(GridCoordinate.EAST).distance(finish)));
      poss.add(new PossibleTile(poll.xy.add(GridCoordinate.SOUTH), poll, poll.pathWeight + 1, poll.xy.add(GridCoordinate.SOUTH).distance(finish)));
      poss.add(new PossibleTile(poll.xy.add(GridCoordinate.WEST), poll, poll.pathWeight + 1, poll.xy.add(GridCoordinate.WEST).distance(finish)));
     
      // Get traits for locals
      TreeSet<String> traitsNorth = fMap.traits(poll.xy.add(GridCoordinate.NORTH));
      TreeSet<String> traitsEast = fMap.traits(poll.xy.add(GridCoordinate.EAST));
      TreeSet<String> traitsSouth = fMap.traits(poll.xy.add(GridCoordinate.SOUTH));
      TreeSet<String> traitsWest = fMap.traits(poll.xy.add(GridCoordinate.WEST));
     
      if (!traitsNorth.contains("solid") && !traitsEast.contains("solid"))
        poss.add(new PossibleTile(poll.xy.add(GridCoordinate.NORTH_EAST), poll, poll.pathWeight + 1.41421f, poll.xy.add(GridCoordinate.NORTH_EAST).distance(finish)));
      if (!traitsEast.contains("solid") && !traitsSouth.contains("solid"))
        poss.add(new PossibleTile(poll.xy.add(GridCoordinate.SOUTH_EAST), poll, poll.pathWeight + 1.41421f, poll.xy.add(GridCoordinate.SOUTH_EAST).distance(finish)));
      if (!traitsSouth.contains("solid") && !traitsWest.contains("solid"))
        poss.add(new PossibleTile(poll.xy.add(GridCoordinate.SOUTH_WEST), poll, poll.pathWeight + 1.41421f, poll.xy.add(GridCoordinate.SOUTH_WEST).distance(finish)));
      if (!traitsWest.contains("solid") && !traitsNorth.contains("solid"))
        poss.add(new PossibleTile(poll.xy.add(GridCoordinate.NORTH_WEST), poll, poll.pathWeight + 1.41421f, poll.xy.add(GridCoordinate.NORTH_WEST).distance(finish)));
     
      for (PossibleTile other: poss) {
        if (!fMap.isValid(other.xy)) continue;
        if (!searched.containsKey(other.xy.toString())) {
          searched.put(other.xy.toString(), other);
          tiles.add(other);
        } else {
          PossibleTile a = searched.get(other.xy.toString());
          if (a.pathWeight + a.approxWeight > other.pathWeight + other.approxWeight) {
            searched.put(other.xy.toString(), other);
            tiles.remove(a);
            tiles.add(other);           
          }
        }
      }
    }
    if (end == null) return null;
   
    LinkedList<GridCoordinate> path = new LinkedList<GridCoordinate>();
   
    PossibleTile next = end;
    while (next != null) {
      path.add(next.xy);
      next = next.parent;
    }
    Collections.reverse(path);
   
    // Perform diagonal optimization
    for (int i = 0; i < path.size() - 1; i++) {
      // Can only optimize straight paths so ignore diagonal ones
      GridCoordinate lineDir = path.get(i + 1).subtract(path.get(i));
      if (lineDir.isDiagonal()) continue;
      // Look ahead for the last diagonal in the next run of diagonals
      boolean inRun = false;
      boolean found = false;
      int j = i + 1;
      GridCoordinate diagDir = null;
      while (j < path.size() - 1) {
        GridCoordinate jDir = path.get(j + 1).subtract(path.get(j));
        // Start following a diagonal run
        if (jDir.isDiagonal() && !inRun) {
          inRun = true;
          found = true;
          diagDir = jDir;
          // Ensure we can go diagonally from i
          if (!diagMove(path.get(i), diagDir)) {
            found = false;
            break;
          }
        // Stop following a diagonal run
        } else if (inRun && !jDir.equals(diagDir)) {
          break;
        // Only go in straight lines
        } else if ((jDir.isDiagonal() && !jDir.equals(diagDir)) || (!jDir.isDiagonal() && !jDir.equals(lineDir))) {
          found = false;
          break;
        }
        j++;
      }
      // If a possible optimization has been found
      if (found) {
        // Get distance
        GridCoordinate dist = path.get(j).subtract(path.get(i));
        // Get perpendicular and parallel distance
        int u;  // Perpendicular
        int v;  // Parallel
        if (Math.abs(lineDir.x()) == 1) {
          u = Math.abs(dist.y());
          v = Math.abs(dist.x());
        } else {
          u = Math.abs(dist.x());
          v = Math.abs(dist.y());
        }
        ArrayList<GridCoordinate> segment = new ArrayList<GridCoordinate>();
        // Found
        boolean segFound = true;
        // Iterate over columns to find an optimal path segment
        for (int m = u; m > 0; m--) {
          GridCoordinate tile = path.get(i).add(diagDir);
          // Try to draw a better path
          segment.clear();
          segFound = true;
          // Iterate over rows
          for (int n = 1; n < v + m - u; n++) {
            if (!fMap.isValid(tile) || !passable(tile)) {
              segFound = false;
              break;
            }
            segment.add(tile);
            if (n < m) {
              // Ensure that we can move diagonally
              if (!diagMove(tile, diagDir)) {
                segFound = false;
                break;
              }
              tile = tile.add(diagDir);
            }
            else tile = tile.add(lineDir);
          }
          if (segFound) break;
          // Reduce j, the possible end point
          j--;
        }
        // If a path segment is found, replace the old path
        if (segFound) {
          // Remove old path
          List<GridCoordinate> sub = path.subList(i + 1, j);
          sub.clear();
          for (GridCoordinate tile: segment) sub.add(tile);
        }
      }
    }
    return path;
  }
 
  protected boolean passable(GridCoordinate tile) {
    TreeSet<String> traits = fScene.mapManager().map().traits(tile);
    if (traits.contains("nogo") || traits.contains("solid") || traits.contains("water")) return false;
    Grunt g = fScene.gridManager().getFirstEntityOfTypeAt(tile, Grunt.class);
    if (g != null && g.state() != GruntState.MOVING) return false;
    return true;
  }
 
  protected boolean diagMove(GridCoordinate tile, GridCoordinate diagDir) {
    GridCoordinate a = tile.add(new GridCoordinate(diagDir.x(), 0));
    GridCoordinate b = tile.add(new GridCoordinate(0, diagDir.y()));
    return !fMap.traits(a).contains("solid") && !fMap.traits(b).contains("solid");
  }
 
  protected class PossibleTile implements Comparable<PossibleTile> {
    public GridCoordinate xy;
    public PossibleTile parent;
    public float pathWeight;  // Weight of the path taken so far
    public float approxWeight;  // Approximate length of the remaining path
   
    public PossibleTile(GridCoordinate xy, PossibleTile parent, float pathWeight, float approxWeight) {
      this.xy = xy;
      this.parent = parent;
      this.pathWeight = pathWeight;
      this.approxWeight = approxWeight;
    }
   
    @Override
    public int compareTo(PossibleTile tile) {
      if (tile.xy.equals(xy)) return 0;
      if (pathWeight + approxWeight > tile.pathWeight + tile.approxWeight) return 1;
      return -1;
    }
   
    @Override
    public boolean equals(Object tile) {
      return (tile instanceof PossibleTile && ((PossibleTile) tile).xy.equals(xy));
    }
   
    @Override
    public int hashCode() {
      return xy.hashCode();
    }
   
    public String toString() {
      return xy.toString() + ": " + (pathWeight + approxWeight) + " <- " + ((parent != null) ? parent.xy.toString() : "");
    }
  }
}
TOP

Related Classes of com.pointcliki.dizgruntled.utils.PathFinder$PossibleTile

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.