找到2D阵列中两点之间的最短路径

时间:2016-11-29 03:49:14

标签: search multidimensional-array typescript

我有一个简单的游戏,我正试图获得2点之间的最短路线

enter image description here

地图由2d数组matrix: Node[][]

组成
class Node{
   index: {
     x: number,
     y: number
   },
   isAvailable: boolean
}

算法应返回与节点可用性相关的最短路径。

e.g。树被标记为不可用node.isAvailable = false

我坚持为这个矩阵实现算法

我尝试使用here中的 Dijkstras 算法,但我无法弄清楚如何应用它,我做了

const graph = new Dijkstra();

//convert the matrix (2d array) to graph
matrix.map((row) => {
  row.map((node: Node) => {
    let x = node.index.x;
    let y = node.index.y;
    graph.addVertex(x + ":" + y, {x: x, y: y});

  });
});

console.log(graph.shortestPath('0:0', '5:5'));
//the output was ['0:0'] (definitly not the answer)

如何在此矩阵上应用算法?

P.S这是我的完整code

2 个答案:

答案 0 :(得分:1)

我使用了一种最好描述为目标溢出油漆的方法:

您将目标方块标记为0,然后遍历邻居并将其标记为1,表示到目标的距离,然后遍历邻居的邻居等。重复此过程,直到油漆到达您的巨魔。所有留给巨魔的事情就是开始转移到潜力最小的方块。

一旦你有多个角色需要在每个人都在移动的同时绕过彼此时,它会变得更有趣。

答案 1 :(得分:1)

我必须实现A *算法

enter image description here

export class PathFinder {

  grid: Tile[][];
  gridHeight: number;
  gridWidth: number;
  startTile: Tile;
  endTile: Tile;

  /** Array of the already checked tiles. */
  closedList: List<Tile> = new List<Tile>();
  openList: List<Tile> = new List<Tile>();

  constructor(grid: Tile[][], gridHeight: number, gridWidth: number) {

    this.grid = grid;
    this.gridHeight = gridHeight;
    this.gridWidth = gridWidth;
  }

  searchPath(start: Tile, end: Tile): Tile[] {
    this.startTile = start;
    this.endTile = end;

    /** Path validation */
    if (!start.walkable) {
      console.log('The start tile in not walkable, choose different tile than', start.index);
      return [];
    }
    if (!end.walkable) {
      console.log('The end tile in not walkable, choose different tile than', end.index);
      return [];
    }
    /** Start A* Algorithm */

    /** Add the starting tile to the openList */
    this.openList.push(start);
    let currentTile;

    /** While openList is not empty */
    while (this.openList.length) {
      //current node = node for open list with the lowest cost.
      currentTile = this.getTileWithLowestTotal(this.openList);

      //if the currentTile is the endTile, then we can stop searching
      if(JSON.stringify(currentTile.index) === JSON.stringify(end.index)){

        this.startTile.setBackgroundColor("rgba(255, 45, 45, .8)");
        this.endTile.setBackgroundColor("rgba(255, 45, 45, .8)");
        return this.shortestPath();
      }
      else {
        //move the current tile to the closed list and remove it from the open list.
        this.openList.remove(currentTile);
        this.closedList.push(currentTile);

        // //Get all adjacent Tiles
        let adjacentTiles = this.getAdjacentTiles(currentTile);

        for (let adjacentTile of adjacentTiles) {
          //Get tile is not in the open list
          if (!this.openList.contains(adjacentTile)) {
            //Get tile is not in the closed list
            if (!this.closedList.contains(adjacentTile)) {
              //move it to the open list and calculate cost
              this.openList.push(adjacentTile);

              //calculate the cost
              adjacentTile.cost = currentTile.cost + 1;

              //calculate the manhattan distance
              adjacentTile.heuristic = this.manhattanDistance(adjacentTile);

              // calculate the total amount
              adjacentTile.total = adjacentTile.cost + adjacentTile.heuristic;

              currentTile.setBackgroundColor('rgba(0, 181, 93, 0.8)');
            }
          }
        }
      }
    }
  }

  getTileWithLowestTotal(openList: Tile[]): Tile {
    let tileWithLowestTotal = new Tile();
    let lowestTotal: number = 999999999;
    /** Search open tiles and get the tile with the lowest total cost */
    for (let openTile of openList) {
      if (openTile.total <= lowestTotal) {
        //clone lowestTotal
        lowestTotal = openTile.total;
        tileWithLowestTotal = openTile;
      }
    }
    return tileWithLowestTotal;
  }

  getAdjacentTiles(current: Tile): Tile[] {
    let adjacentTiles: Tile[] = [];
    let adjacentTile: Tile;

    //Tile to left
    if (current.index.x - 1 >= 0) {
      adjacentTile = this.grid[current.index.x - 1][current.index.y];
      if (adjacentTile && adjacentTile.walkable) {
        adjacentTiles.push(adjacentTile);
      }
    }

    //Tile to right
    if (current.index.x + 1 < this.gridWidth) {
      adjacentTile = this.grid[current.index.x + 1][current.index.y];
      if (adjacentTile && adjacentTile.walkable) {
        adjacentTiles.push(adjacentTile);
      }
    }

    //Tile to Under
    if (current.index.y + 1 < this.gridHeight) {
      adjacentTile = this.grid[current.index.x][current.index.y + 1];
      if (adjacentTile && adjacentTile.walkable) {
        adjacentTiles.push(adjacentTile);
      }
    }

    //Tile to Above
    if (current.index.y - 1 >= 0) {
      adjacentTile = this.grid[current.index.x][current.index.y - 1];
      if (adjacentTile && adjacentTile.walkable) {
        adjacentTiles.push(adjacentTile);
      }
    }
    /** TODO: Diagonal moves  */
    return adjacentTiles;
  }

  /** Calculate the manhattan distance */
  manhattanDistance(adjacentTile: Tile): number {
    return Math.abs((this.endTile.index.x - adjacentTile.index.x) +
      (this.endTile.index.y - adjacentTile.index.y));
  }

  shortestPath() {
    let startFound: boolean = false;
    let currentTile = this.endTile;
    let pathTiles = [];

    //includes the end tile in the path
    pathTiles.push(this.endTile);
    this.endTile.ball = true;

    while (!startFound) {
      let adjacentTiles = this.getAdjacentTiles(currentTile);

      //check to see what newest current tile.
      for (let adjacentTile of adjacentTiles) {
        //check if it is the start tile
        if (JSON.stringify(adjacentTile.index) === JSON.stringify(this.startTile.index)){
          return pathTiles;
        }

        //it has to be inside the closedList or openList
        if (this.closedList.contains(adjacentTile) || this.openList.contains(adjacentTile)) {
          if (adjacentTile.cost <= currentTile.cost && adjacentTile.cost > 0) {
            //change the current tile.
            currentTile = adjacentTile;
            //Add this adjacentTile to the path list
            pathTiles.push(adjacentTile);
            //highlight way with yellow balls
            adjacentTile.ball = true;
            break;
          }
        }
      }
    }
  }
}