在 p5 中设置时,我的代码进入无限循环

My code gets into an infinite loop when setting up in p5

我很难找到循环所在的位置。我 运行 代码并挂起。我正在尝试制作一个电路游戏,其中有供用户连接的电路。但是我什至在设置地图以确保它是可解决的情况下都陷入了困境。有一个无限循环,但我找不到它我看了又看...这是代码。`

//maxLength of circut board is the board 
let theHighestMaxLength = 10;
let board;
let gridX = 10;
let gridY = 10;
let perX = 10;
let perY = 10;
//s is here so we don't have to pass it everywhere the square we are looking at in functions
let s=null;
//length and usedsquares and begin are here to be used across functions
let length=0;
let usedSquares=0;
let begin=null;
class Asquare {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.isCircut = false;
    this.isWire = false;
    this.OtherCircut = null;
    this.Left = null;
    this.Right = null;
    this.Top = null;
    this.Bottom = null;
    this.otherCircut = null;
    this.isBlank = false;
  }
  drawSelf() {
    if (this.isCircut) {
      rectMode(CENTER)
      var xx = max(this.otherCircut.x, this.x);
      var yy = max(this.otherCircut.y, this.y);
      fill(color(xx * 25, yy * 25, 0));

      square((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
    }
    else if (this.isWire) {
      fill(color(this.otherCircut.x * 20, this.otherCircut.y * 20, 0));
      circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
    }
    else if (this.isBlank) {
      fill(color(0, 204, 0))
      circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
    }
  }
}
function handleWireMove(){
  s.isWire = true;
  //remember the starting circut
  s.otherCircut = begin;
  informAll(s);
  //the length is used
  length++;
}
function informAll() {
//tell all the other squares of s
if (s.x - 1 >= 0) {
board[s.x - 1][s.y].Right = s;
}
if (s.x + 1 < gridX) {
board[s.x + 1][s.y].Left = s;
}
if (s.y - 1 >= 0) {
board[s.x][s.y - 1].Bottom = s;
}
if (s.y + 1 < gridY) {
board[s.x][s.y + 1].Top = s;
}
//the used squares is now higher
usedSquares++;
}
function setup() {
  createCanvas(gridX * perX, gridY * perY);
  noLoop();
  //fill the board with squares
  board = new Array(gridX).fill(0).map(() => new Array(gridY).fill(0));
  for (var x = 0; x < gridX; x++) {
    for (var y = 0; y < gridY; y++) {
      board[x][y] = new Asquare(x, y);
    }
  }
  //the number of squares in the grid used
  usedSquares = 0;
  //till the board is full
  while (usedSquares < gridX * gridY) {
    //get a random x y
    var rx = floor(random(gridX));
    var ry = floor(random(gridY));
    //create an s and begin var s for every nw square and begin for the first
    s = board[rx][ry];
    //if this square is already in use
    if(s.isBlank||s.isCircut||s.isWire){continue;}
    //begin at this square
    begin = s;
    //if there is some way to go
    if (s.Left == null || s.Right == null || s.Top == null || s.Bottom == null) {
      // get a random length to try and reach
      var maxLength = floor(random(1, theHighestMaxLength))
      //the starting length
      length = 0;
      begin.isCircut = true;
      //inform all the surounding squares and increase the number of used 
      informAll();
      //while the length isn't full
      while (length <= maxLength) {
        //add an option count for left right top botoom 
        var numOption = s.Left == null ? 1 : 0;
        numOption = s.Right == null ? numOption + 1 : numOption;
        numOption = s.Top == null ? numOption + 1 : numOption;
        numOption = s.Bottom == null ? numOption + 1 : numOption;
        //if there are no toptions to move we must stop here ot if the maxLength is reached
        if (numOption == 0 || length == maxLength) {
          //this is a circut the beigin circut is begin the begin other circut is this final one
          s.isCircut = true;
          s.isWire = false;
          s.otherCicut = begin;
          begin.otherCircut = s;
          //make sure the loop ends
          length=9999;
          break;
        }
        //pick a random direction number
        var randomChoiseNumber = floor(random(numOption));
        //if left is an option
        if (s.Left == null) {
          //if r is already 0 that means we picked left
          if (randomChoiseNumber == 0) {
            //while left is an option and the maxlength isn't hit and left isn't off the map
            while (s.Left == null && length < maxLength && s.x - 1 >= 0) {
              //set s to the left
              s = board[s.x - 1][s.y];
              //handleWireMove the move
              handleWireMove()
            }
            //continue we used the direction
            continue;
          } else {
            //we passed an option reduce the number
            randomChoiseNumber--;
          }
        }
        //if right is an option
        if (s.Right == null) {
          //if this is the zero choice
          if (randomChoiseNumber == 0) {
            //if right is not off the map and an option while the length is not hit
            while (s.Right == null && length < maxLength && s.x + 1 < gridX) {
              //set s to right
              s = board[s.x + 1][s.y];
              //handleWireMove the move
              handleWireMove();
            }
            continue;
          } else {
            //reduce the number as we passed an option
            randomChoiseNumber--;
          }
        }
        //if top is an option
        if (s.Top == null) {
          //if this is the zero option
          if (randomChoiseNumber == 0) {
            //while top is a choise and the length is not reached and top is not off the map
            while (s.Top == null && length < maxLength && s.y - 1 >= 0) {
              //move to the top
              s = board[s.x][s.y - 1];
              //handleWireMove the move
              handleWireMove();
            }
            //continue the direction is used up
            continue;
          } else {
            //we passed a number reduce the choise number
            randomChoiseNumber--;
          }
        }
        //if bottom is an option
        if (s.Bottom == null) {
          //if this is the zero option
          if (randomChoiseNumber == 0) {
            //while bottom is a choise and the length is not reached and it is not off the map
            while (s.Bottom == null && length < maxLength && s.y + 1 < gridY) {
              //go to the bottom
              s = board[s.x][s.y + 1];
              //handleWireMove the move
              handleWireMove();
            }
          }
        }
      }
    }
    else {
      //if there was no way to go the square is blank tell the others and increace usedSquares
      s.isBlank = true;
      informAll();
    }
  }
}



function drawAll() {
  board.forEach(a => a.forEach(b => b.drawSelf()));
}
function draw() {
  background(gridX * perX);
  drawAll();
}

问题出在设置函数中,但我找不到它。请帮忙

问题是您计算可用 numOption 的数量仅基于随机 selected 网格方块是否有未连接的插槽(LeftTopRight,或 Bottom),而不考虑 s.xs.y 的值是否使 select 这些方向之一成为可能)。结果,您的内部循环无限重复,因为它从不调用 handleWireMove() ,因此从不增加 length.

下面的代码修改了一些额外的日志记录,无限循环在哪里变得很明显(它最终永远打印 'we failed to make a choice. that is not good'):

  //the number of squares in the grid used
  usedSquares = 0;
  //till the board is full
  while (usedSquares < gridX * gridY) {
    console.log(usedSquares);
    //get a random x y
    var rx = floor(random(gridX));
    var ry = floor(random(gridY));
    //create an s and begin var s for every nw square and begin for the first
    s = board[rx][ry];
    //if this square is already in use
    if (s.isBlank || s.isCircut || s.isWire) {
      continue;
    }
    //begin at this square
    begin = s;
    //if there is some way to go
    if (
      s.Left == null ||
      s.Right == null ||
      s.Top == null ||
      s.Bottom == null
    ) {
      // get a random length to try and reach
      var maxLength = floor(random(1, theHighestMaxLength));
      //the starting length
      length = 0;
      begin.isCircut = true;
      //inform all the surounding squares and increase the number of used
      informAll();
      //while the length isn't full
      console.log('beggining inner loop');
      while (length <= maxLength) {
        //add an option count for left right top botoom
        var numOption = s.Left == null ? 1 : 0;
        numOption = s.Right == null ? numOption + 1 : numOption;
        numOption = s.Top == null ? numOption + 1 : numOption;
        numOption = s.Bottom == null ? numOption + 1 : numOption;
        //if there are no toptions to move we must stop here ot if the maxLength is reached
        if (numOption == 0 || length == maxLength) {
          //this is a circut the beigin circut is begin the begin other circut is this final one
          s.isCircut = true;
          s.isWire = false;
          s.otherCicut = begin;
          begin.otherCircut = s;
          //make sure the loop ends
          console.log('no options, abort');
          length = 9999;
          break;
        }
        //pick a random direction number
        var randomChoiseNumber = floor(random(numOption));
        //if left is an option
        if (s.Left == null) {
          //if r is already 0 that means we picked left
          if (randomChoiseNumber == 0) {
            //while left is an option and the maxlength isn't hit and left isn't off the map
            while (s.Left == null && length < maxLength && s.x - 1 >= 0) {
              //set s to the left
              s = board[s.x - 1][s.y];
              //handleWireMove the move
              handleWireMove();
            }
            //continue we used the direction
            continue;
          } else {
            //we passed an option reduce the number
            randomChoiseNumber--;
          }
        }
        //if right is an option
        if (s.Right == null) {
          //if this is the zero choice
          if (randomChoiseNumber == 0) {
            //if right is not off the map and an option while the length is not hit
            while (s.Right == null && length < maxLength && s.x + 1 < gridX) {
              //set s to right
              s = board[s.x + 1][s.y];
              //handleWireMove the move
              handleWireMove();
            }
            continue;
          } else {
            //reduce the number as we passed an option
            randomChoiseNumber--;
          }
        }
        //if top is an option
        if (s.Top == null) {
          //if this is the zero option
          if (randomChoiseNumber == 0) {
            //while top is a choise and the length is not reached and top is not off the map
            while (s.Top == null && length < maxLength && s.y - 1 >= 0) {
              //move to the top
              s = board[s.x][s.y - 1];
              //handleWireMove the move
              handleWireMove();
            }
            //continue the direction is used up
            continue;
          } else {
            //we passed a number reduce the choise number
            randomChoiseNumber--;
          }
        }
        //if bottom is an option
        if (s.Bottom == null) {
          //if this is the zero option
          if (randomChoiseNumber == 0) {
            //while bottom is a choise and the length is not reached and it is not off the map
            while (s.Bottom == null && length < maxLength && s.y + 1 < gridY) {
              //go to the bottom
              s = board[s.x][s.y + 1];
              //handleWireMove the move
              handleWireMove();
            }
          }
        }
        console.log('we failed to make a choice. that is not good');
      }
      
      console.log('exited inner loop');
    } else {
      //if there was no way to go the square is blank tell the others and increace usedSquares
      s.isBlank = true;
      informAll();
    }
  }

这是一个工作版本,不仅可以正确检查 Left/Top/Right/Bottom 是否有开口,而且可以正确检查该方向是否可行(不超出网格边缘):

//maxLength of circut board is the board
let theHighestMaxLength = 10;
let board;
let gridX = 10;
let gridY = 10;
let perX = 10;
let perY = 10;
//s is here so we don't have to pass it everywhere the square we are looking at in functions
let s = null;
//length and usedsquares and begin are here to be used across functions
let length = 0;
let usedSquares = 0;
let begin = null;
class Asquare {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.isCircut = false;
    this.isWire = false;
    this.OtherCircut = null;
    this.Left = null;
    this.Right = null;
    this.Top = null;
    this.Bottom = null;
    this.otherCircut = null;
    this.isBlank = false;
  }
  drawSelf() {
    if (this.isCircut) {
      rectMode(CENTER);
      var xx = max(this.otherCircut.x, this.x);
      var yy = max(this.otherCircut.y, this.y);
      fill(color(xx * 25, yy * 25, 0));

      square((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
    } else if (this.isWire) {
      fill(color(this.otherCircut.x * 20, this.otherCircut.y * 20, 0));
      circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
    } else if (this.isBlank) {
      fill(color(0, 204, 0));
      circle((this.x + 0.5) * perX, (this.y + 0.5) * perY, perX);
    }
  }
}

function handleWireMove() {
  s.isWire = true;
  //remember the starting circut
  s.otherCircut = begin;
  informAll(s);
  //the length is used
  length++;
}

function informAll() {
  //tell all the other squares of s
  if (s.x - 1 >= 0) {
    board[s.x - 1][s.y].Right = s;
  }
  if (s.x + 1 < gridX) {
    board[s.x + 1][s.y].Left = s;
  }
  if (s.y - 1 >= 0) {
    board[s.x][s.y - 1].Bottom = s;
  }
  if (s.y + 1 < gridY) {
    board[s.x][s.y + 1].Top = s;
  }
  //the used squares is now higher
  usedSquares++;
}

function setup() {
  createCanvas(gridX * perX, gridY * perY);
  noLoop();
  //fill the board with squares
  board = new Array(gridX).fill(0).map(() => new Array(gridY).fill(0));
  for (var x = 0; x < gridX; x++) {
    for (var y = 0; y < gridY; y++) {
      board[x][y] = new Asquare(x, y);
    }
  }
  //the number of squares in the grid used
  usedSquares = 0;
  //till the board is full
  while (usedSquares < gridX * gridY) {
    console.log(usedSquares);
    //get a random x y
    var rx = floor(random(gridX));
    var ry = floor(random(gridY));
    //create an s and begin var s for every nw square and begin for the first
    s = board[rx][ry];
    //if this square is already in use
    if (s.isBlank || s.isCircut || s.isWire) {
      continue;
    }
    //begin at this square
    begin = s;
    //if there is some way to go
    if (
      s.Left == null ||
      s.Right == null ||
      s.Top == null ||
      s.Bottom == null
    ) {
      // get a random length to try and reach
      var maxLength = floor(random(1, theHighestMaxLength));
      //the starting length
      length = 0;
      begin.isCircut = true;
      //inform all the surounding squares and increase the number of used
      informAll();
      //while the length isn't full
      console.log('beggining inner loop');
      while (length <= maxLength) {
        //add an option count for left right top botoom
        var numOption = s.Left == null && s.x - 1 >= 0 ? 1 : 0;
        numOption = s.Right == null && s.x + 1 < gridX ? numOption + 1 : numOption;
        numOption = s.Top == null && s.y - 1 >= 0 ? numOption + 1 : numOption;
        numOption = s.Bottom == null && s.y + 1 < gridY ? numOption + 1 : numOption;
        //if there are no toptions to move we must stop here ot if the maxLength is reached
        if (numOption == 0 || length == maxLength) {
          //this is a circut the beigin circut is begin the begin other circut is this final one
          s.isCircut = true;
          s.isWire = false;
          s.otherCicut = begin;
          begin.otherCircut = s;
          //make sure the loop ends
          console.log('no options, abort');
          length = 9999;
          break;
        }
        //pick a random direction number
        var randomChoiseNumber = floor(random(numOption));
        //if left is an option
        if (s.Left == null && s.x - 1 >= 0) {
          //if r is already 0 that means we picked left
          if (randomChoiseNumber == 0) {
            //while left is an option and the maxlength isn't hit and left isn't off the map
            while (s.Left == null && length < maxLength && s.x - 1 >= 0) {
              //set s to the left
              s = board[s.x - 1][s.y];
              //handleWireMove the move
              handleWireMove();
            }
            //continue we used the direction
            continue;
          } else {
            //we passed an option reduce the number
            randomChoiseNumber--;
          }
        }
        //if right is an option
        if (s.Right == null && s.x + 1 < gridX) {
          //if this is the zero choice
          if (randomChoiseNumber == 0) {
            //if right is not off the map and an option while the length is not hit
            while (s.Right == null && length < maxLength && s.x + 1 < gridX) {
              //set s to right
              s = board[s.x + 1][s.y];
              //handleWireMove the move
              handleWireMove();
            }
            continue;
          } else {
            //reduce the number as we passed an option
            randomChoiseNumber--;
          }
        }
        //if top is an option
        if (s.Top == null && s.y - 1 >= 0) {
          //if this is the zero option
          if (randomChoiseNumber == 0) {
            //while top is a choise and the length is not reached and top is not off the map
            while (s.Top == null && length < maxLength && s.y - 1 >= 0) {
              //move to the top
              s = board[s.x][s.y - 1];
              //handleWireMove the move
              handleWireMove();
            }
            //continue the direction is used up
            continue;
          } else {
            //we passed a number reduce the choise number
            randomChoiseNumber--;
          }
        }
        //if bottom is an option
        if (s.Bottom == null && s.y + 1 < gridY) {
          //if this is the zero option
          if (randomChoiseNumber == 0) {
            //while bottom is a choise and the length is not reached and it is not off the map
            while (s.Bottom == null && length < maxLength && s.y + 1 < gridY) {
              //go to the bottom
              s = board[s.x][s.y + 1];
              //handleWireMove the move
              handleWireMove();
            }
          }
        }
        console.log('we failed to make a choice. that is not good');
      }

      console.log('exited inner loop');
    } else {
      //if there was no way to go the square is blank tell the others and increace usedSquares
      s.isBlank = true;
      informAll();
    }
  }
}

function drawAll() {
  board.forEach((a) => a.forEach((b) => b.drawSelf()));
}

function draw() {
  background(gridX * perX);
  drawAll();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

一个结束:这个错误是这个代码非常复杂并且违反良好设计的基本原则的症状,例如没有作为函数调用的副作用更新的共享全局状态。