【问题标题】:A* Pathfinding, path not showing upA* 寻路,路径未显示
【发布时间】:2021-06-02 06:35:39
【问题描述】:

我有这段代码,在脚本 (p5.js) 中,我正在运行它并按预期工作,但是当我包含路径代码时,这有助于找到以前的父母以形成通往最终目标的路径,如图所示,浏览器崩溃了。它首先为 3 个单元格着色,然后崩溃。这是代码。

    var col = 12, row = 12, grid = new Array(col), openSet = [], closeSet = [], start, end, w, h, path = [];

function removefromArray(array_, element){
    for(var i = array_.length - 1; i >= 0; i--){
        if(array_[i] == element){
            array_.splice(i, 1);
        }
    }
}

function heuristic(a, b){
    var distance = abs(a.i - b.i) + abs(a.j - b.j);
    return distance;
}

function Spot(i, j){
    this.i = i;
    this.j = j;
    this.f = 0;
    this.g = 0;
    this.h = 0;
    this.neighbor = [];
    this.parent = undefined;
    this.wall = false;
    

    this.show = function (color){
        
        fill(color);
        if(this.wall){
            fill(0);
        }
        noStroke();
        rect(this.i * w, this.j * h, w - 1, h - 1);
    }

    this.addNeighbor = function(grid){
        var i = this.i, j = this.j;
        if(i < col - 1){
            this.neighbor.push(grid[i+1] [j]);
        }
        if(i > 0){
            this.neighbor.push(grid[i-1] [j]);
        }
        if(j < row-1){
            this.neighbor.push(grid[i] [j+1]);
        }
        if(j > 0){
            this.neighbor.push(grid[i] [j-1]);
        }
    }

}

function setup(){
    createCanvas(500,500);
    console.log("A*");

    w = width / col;
    h = height / row;
    for( var i = 0; i< col; i++){
        grid[i] = new Array(row);
    }
    //Adding a spot
    for( var i = 0; i< col; i++){
        for( var j = 0; j< row; j++){
            grid[i][j] = new Spot(i,j); 
        }
    }
    //Adding a neighbor
    for( var i = 0; i< col; i++){
        for( var j = 0; j< row; j++){
            grid[i][j].addNeighbor(grid);  
        }
    }
    start = grid[0][0];
    end = grid[col - 1][row - 1];

    openSet.push(start);
}
function draw(){
    var winner = 0;
    if(openSet.length > 0){
        for( var i = 0; i< openSet.length; i++){
            if(openSet[i].f < openSet[winner].f){
                winner = i; 
            }
        }
        var current = openSet[winner]; 
        if(current === end){
            noLoop();
            console.log("Done!");
        }

        removefromArray(openSet, current);
        closeSet.push(current);

        var neighbors = current.neighbor;
        for(var i = 0; i < neighbors.length; i++){
            var the_neighbor = neighbors[i];

            if(!closeSet.includes(the_neighbor)){
                var tempG  = current.g + 1;
            }
            if(openSet.includes(the_neighbor)){
                if(tempG < the_neighbor.g){
                    the_neighbor.g = tempG;
                }
            }
            else{
                the_neighbor.g = tempG;
                openSet.push(the_neighbor);
            }
            the_neighbor.h = heuristic(the_neighbor, end);
            the_neighbor.f = the_neighbor.g + the_neighbor.h;
            the_neighbor.parent = current; // the previous node
        }
    }
    else{
        // no solution
    }

    background(0)
    for( var i = 0; i< col; i++){
        for( var j = 0; j< row; j++){
            grid[i][j].show(color(255)); 
        }
    }

    for( var i = 0; i< openSet.length; i++){
        openSet[i].show(color("green"));
    }

    for( var i = 0; i< closeSet.length; i++){
        closeSet[i].show(color("red"));
    }

    // path = [];
    // var temp = current;
    // path.push(temp);
    // while(temp.parent){
    //     path.push(temp.parent);
    //     temp = temp.parent;
    // }

    // for(var i = 0; i < path.length; i++){
    //     path[i].show(color(0,0,255));
    // }
    
}

如果我尝试删除最后一部分的 cmets 斜线,系统将运行大约 5 秒然后崩溃。有解决方案的人,我将不胜感激。

路径部分未注释时的结果

【问题讨论】:

  • “粉碎”是指“崩溃”吗?如果是这样,“崩溃”是什么意思?
  • @Andy Ray 对不起。以及所做的更改。

标签: javascript p5.js path-finding


【解决方案1】:

您正在以某种方式在父链中生成一个循环(即点 A 具有父点 B,点 B 具有父点 A),这导致了无限循环。我不确定到底在哪里/为什么会发生这种情况。您的代码有点难以阅读。您应该避免使用不详的单字母变量和属性名称。

还有几个范围界定问题可能会导致意外行为。关键字var 是自goto 以来所有编程语言中最糟糕的元素,应该用1000 个太阳的火焰从互联网上清除它。请使用let。有关详细说明,请参阅下面的 cmets。

var col = 12,
  row = 12,
  grid = new Array(col),
  openSet = [],
  closeSet = [],
  start, end, w, h, path = [];

function removefromArray(array_, element) {
  for (let i = array_.length - 1; i >= 0; i--) {
    if (array_[i] == element) {
      array_.splice(i, 1);
    }
  }
}

function heuristic(a, b) {
  var distance = abs(a.i - b.i) + abs(a.j - b.j);
  return distance;
}

function Spot(i, j) {
  this.i = i;
  this.j = j;
  this.f = 0;
  this.g = 0;
  this.h = 0;
  this.neighbor = [];
  this.parent = undefined;
  this.wall = false;

  this.show = function(color) {

    fill(color);
    if (this.wall) {
      fill(0);
    }
    noStroke();
    rect(this.i * w, this.j * h, w - 1, h - 1);
  }

  this.addNeighbor = function(grid) {
    let i = this.i,
      j = this.j;
    if (i < col - 1) {
      this.neighbor.push(grid[i + 1][j]);
    }
    if (i > 0) {
      this.neighbor.push(grid[i - 1][j]);
    }
    if (j < row - 1) {
      this.neighbor.push(grid[i][j + 1]);
    }
    if (j > 0) {
      this.neighbor.push(grid[i][j - 1]);
    }
  }

}

function setup() {
  createCanvas(500, 500);
  console.log("A*");

  w = width / col;
  h = height / row;
  for (let i = 0; i < col; i++) {
    grid[i] = new Array(row);
  }
  //Adding a spot
  for (let i = 0; i < col; i++) {
    for (let j = 0; j < row; j++) {
      grid[i][j] = new Spot(i, j);
    }
  }
  //Adding a neighbor
  for (let i = 0; i < col; i++) {
    for (let j = 0; j < row; j++) {
      grid[i][j].addNeighbor(grid);
    }
  }
  start = grid[0][0];
  end = grid[col - 1][row - 1];

  openSet.push(start);
}

function draw() {
  let winner = 0;
  let current
  if (openSet.length > 0) {
    for (let i = 0; i < openSet.length; i++) {
      if (openSet[i].f < openSet[winner].f) {
        winner = i;
      }
    }
    current = openSet[winner];
    if (current === end) {
      noLoop();
      console.log("Done!");
    }

    removefromArray(openSet, current);
    closeSet.push(current);

    var neighbors = current.neighbor;
    for (let i = 0; i < neighbors.length; i++) {
      var the_neighbor = neighbors[i];

      // The use of var here results in very weird behavior
      // where tempG's value is preserved from the previous 
      // iteration of this loop even when it is not assigned
      // a value. If that is desired you should declare this
      // variable outside of the for loop.
      //
      // If you always use let isntead of var you will get
      // errors instead of bizarre behavior. That will help
      // you be deliberate about your variable scoping.
      if (!closeSet.includes(the_neighbor)) {
        var tempG = current.g + 1;
      } else {
        print('tempG not set');
      }
      if (openSet.includes(the_neighbor)) {
        if (tempG < the_neighbor.g) {
          the_neighbor.g = tempG;
        }
      } else {
        print(`tempG: ${tempG}`);
        the_neighbor.g = tempG;
        openSet.push(the_neighbor);
        print(`openSet: ${openSet.length}`);
      }
      the_neighbor.h = heuristic(the_neighbor, end);
      the_neighbor.f = the_neighbor.g + the_neighbor.h;
      the_neighbor.parent = current; // the previous node
    }
  } else {
    // no solution
  }

  background(0)
  for (let i = 0; i < col; i++) {
    for (var j = 0; j < row; j++) {
      grid[i][j].show(color(255));
    }
  }

  for (let i = 0; i < openSet.length; i++) {
    openSet[i].show(color("green"));
  }

  for (let i = 0; i < closeSet.length; i++) {
    closeSet[i].show(color("red"));
  }

  path = [];
  let temp = current;
  path.push(temp);
  while (temp.parent) {
    if (path.includes(temp.parent)) {
      print('Cycle detected!');
      console.log({ current: { i: temp.i, j: temp.j }, parent: { i: temp.parent.i, j: temp.parent.j } });
      break;
    }
    path.push(temp.parent);
    temp = temp.parent;
  }

  for (let i = 0; i < path.length; i++) {
    path[i].show(color(0, 0, 255));
  }

}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"&gt;&lt;/script&gt;

更新:无限循环修复

更新邻居的代码部分与the definition of A* 有很大的不同。这是我想出的(单字母变量名替换为有意义的名称):

let tentativePathScore = current.shortestPathScore + 1;
if (the_neighbor.heuristicScore === undefined) {
  the_neighbor.heuristicScore = heuristic(the_neighbor, end);
}

if (the_neighbor.combinedScore === undefined ||
    tentativePathScore + the_neighbor.heuristicScore < the_neighbor.combinedScore) {
  
  // Update the path score and combined score for this neighbor.
  the_neighbor.shortestPathScore = tentativePathScore;
  the_neighbor.combinedScore = the_neighbor.shortestPathScore + the_neighbor.heuristicScore;
  the_neighbor.parent = current; // the previous node
  
  if (!openSet.includes(the_neighbor)) {
    openSet.push(the_neighbor);
  }
}

这是一个添加了墙壁的工作 sn-p:

let col = 12,
    row = 12,
    grid = new Array(col),
    openSet = [],
    closeSet = [],
    start, end, w, h, path = [];

function removefromArray(array_, element) {
    for (let i = array_.length - 1; i >= 0; i--) {
        if (array_[i] == element) {
            array_.splice(i, 1);
        }
    }
}

function heuristic(a, b) {
    if (a.wall) {
        return Infinity;
    }
    
    return abs(a.i - b.i) + abs(a.j - b.j);
}

function Spot(i, j) {
    this.i = i;
    this.j = j;
    this.combinedScore = undefined;
    this.shortestPathScore = undefined;
    this.heuristicScore = undefined;
    this.neighbor = [];
    this.parent = undefined;
    this.wall = false;

    this.show = function(color) {
        fill(color);
        if (this.wall) {
            fill(0);
        }
        noStroke();
        rect(this.i * w, this.j * h, w - 1, h - 1);
    }

    this.addNeighbor = function(grid) {
        let i = this.i,
            j = this.j;
        if (i < col - 1) {
            this.neighbor.push(grid[i + 1][j]);
        }
        if (i > 0) {
            this.neighbor.push(grid[i - 1][j]);
        }
        if (j < row - 1) {
            this.neighbor.push(grid[i][j + 1]);
        }
        if (j > 0) {
            this.neighbor.push(grid[i][j - 1]);
        }
    }

}

function setup() {
    createCanvas(500, 500);
    console.log("A*");

    w = width / col;
    h = height / row;
    for (let i = 0; i < col; i++) {
        grid[i] = new Array(row);
    }
    //Adding a spot
    for (let i = 0; i < col; i++) {
        for (let j = 0; j < row; j++) {
            grid[i][j] = new Spot(i, j);
        }
    }
    //Adding a neighbor
    for (let i = 0; i < col; i++) {
        for (let j = 0; j < row; j++) {
            grid[i][j].addNeighbor(grid);
        }
    }
    // make walls
    for (let i = 0; i < col; i++) {
        for (let j = 0; j < row; j++) {
            if ((i > 1 || j > 1) && (i < col - 2 || j < row - 2) && random() < 0.2) {
                grid[i][j].wall = true;
            }
        }
    }
    
    start = grid[0][0];
    end = grid[col - 1][row - 1];
    
    start.shortestPathScore = 0;
    start.heuristicScore = heuristic(start, end);
    start.combinedScore = start.shortestPathScore + start.heuristicScore;

    openSet.push(start);
}

function draw() {
    let winner = 0;
    let current;
    if (openSet.length > 0) {
        for (let i = 0; i < openSet.length; i++) {
            if (openSet[i].combinedScore < openSet[winner].combinedScore) {
                winner = i;
            }
        }
        current = openSet[winner];
        if (current === end) {
            noLoop();
            console.log("Done!");
        }

        removefromArray(openSet, current);
        closeSet.push(current);

        var neighbors = current.neighbor;
        for (let i = 0; i < neighbors.length; i++) {
            var the_neighbor = neighbors[i];

            let tentativePathScore = current.shortestPathScore + 1;
            if (the_neighbor.heuristicScore === undefined) {
                the_neighbor.heuristicScore = heuristic(the_neighbor, end);
            }
            
            if (the_neighbor.combinedScore === undefined ||
                    tentativePathScore + the_neighbor.heuristicScore < the_neighbor.combinedScore) {
                
                // Update the path score and combined score for this neighbor.
                the_neighbor.shortestPathScore = tentativePathScore;
                the_neighbor.combinedScore = the_neighbor.shortestPathScore + the_neighbor.heuristicScore;
                the_neighbor.parent = current; // the previous node
                
                if (!openSet.includes(the_neighbor)) {
                    openSet.push(the_neighbor);
                }
            }
        }
    } else {
        // no solution
    }

    background(0)
    for (let i = 0; i < col; i++) {
        for (let j = 0; j < row; j++) {
            grid[i][j].show(color(255));
        }
    }

    for (let i = 0; i < openSet.length; i++) {
        openSet[i].show(color("green"));
    }

    for (let i = 0; i < closeSet.length; i++) {
        closeSet[i].show(color("red"));
    }

    path = [];
    let temp = current;
    path.push(temp);
    while (temp.parent) {
        if (path.includes(temp.parent)) {
            print('Cycle detected!');
            break;
        }
        path.push(temp.parent);
        temp = temp.parent;
    }

    for (let i = 0; i < path.length; i++) {
        path[i].show(color(0, 0, 255));
    }

}
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"&gt;&lt;/script&gt;

【讨论】:

  • 非常感谢@Paul Wheeler,它现在可以工作了。我认为你是对的,我的代码在某个地方有一个无限循环。将通过并纠正它。非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多