【问题标题】:Random path generation algorithm随机路径生成算法
【发布时间】:2015-01-05 07:49:54
【问题描述】:

我想生成一个从矩阵顶部到底部的随机路径。

FIDDLE

要求:

  • 路径可以绕行,但必须从第 1 行连接到最后一行。
  • 最终,我希望每个路径片段的颜色是随机的,但现在它可以是统一的(我在下面仅使用红色进行了测试)
  • 从上到下连接的路径是随机生成的
  • 路径碎片显然必须连接,并且不应该分叉(也就是给玩家 2 个选择去的选项,如图所示)
  • 路径只能从上往下走(不能往回走),但可以左右蜿蜒

我的考虑:

  • 我不能简单地检查上面的行的列是否是路径的一部分,因为它会在找到第一个真值时不断生成路径片段。
  • 我对手动生成路径不感兴趣,因为这需要一个新的矩阵来指定我想要路径去的 1 和 0。然后对于每个“随机”路径选项,我必须构建一个新矩阵。更重要的是,在矩阵中手动生成路径会使矩阵大小的缩放变得更加乏味......例如,如果我将 6x6 矩阵更改为 100x100。

所以,是的,最简单的方法就是制作并迭代它:

        var matrixPaths = [
            [0,1,0,0,0,0],
            [0,1,1,1,0,0],
            [0,0,0,1,0,0],
            [0,0,0,1,1,1],
            [0,0,0,0,0,1],
            [0,0,0,0,0,1]
        ];

左边是空网格,右边是它应该生成的内容

我的想法是首先创建网格并在每个矩阵条目中添加跨度:

        function createMyGrid() {
            //create 6x6 matrix
            for(var i=1; i<=6; i++) {
                matrix[i] = [];
                for(var j=1; j<=6; j++) {
                    var colorIndex = Math.floor(Math.random() * (color.length - 0) + 0);
                    var $span = $('<span />').attr('class', 'colorSquare').html("[" + i + "][" + j + "]");
                    $("#grid").append($span);
                    matrix[i][j] = $span;
                }
            }
        }

然后,在第 1 行随机生成第一个路径片段。然后对于每个后续行,检查其上方的路径片段以连接...然后从该片段开始生成下一组:

        function createPath() {
            var randomColumn = Math.floor(Math.random() * (matrix[1].length - 0) + 0);
            matrix[1][randomColumn].data('partOfPath', true);
            matrix[1][randomColumn].addClass("red");
            for (var row = 2; row <= 6; row++) {
                for (var col = 1; col <= 6; col++) {
                    if (matrix[row-1][col].data('partOfPath')) { //if block above is partOfPath... add a set of items of random # of columns across
                        addRowPath(row, col);
                    }
                }           
            }
        }

        function addRowPath (row, pathCol) { //need to start offset from that row/col position, 
            var randRowPathLength = Math.floor(Math.random() * (matrix[row].length - 0) + 0);
                for (var col = pathCol; col <= randRowPathLength; col++) {          
                    matrix[row][col].addClass("red");   
                }
        }

到目前为止,它正在添加初始步骤,然后是下面的行,然后停止。

【问题讨论】:

  • 在这里添加小提琴真的很有用,让人们更容易回答。
  • @IngoBürk Fiddle 添加了
  • 你做了什么调试?一些console.logs 应该向您显示它是否正在停止以及当时的变量是什么。添加断点并单步执行代码应该可以解决问题。
  • @squint 好吧,我不知道从哪里开始。
  • 什么意思?很明显,您的代码中有一些条件不符合您的期望,因此要么在各个点调用 console.log() 以查看值是什么以及路径结束的原因,要么在代码中放置一个断点并单步执行逐行直到你弄清楚。这就是调试。

标签: javascript jquery css algorithm random


【解决方案1】:

如果您一次走一步,并且不包括向上走,那么在我看来,您需要跟踪的只是最后两个动作(确定第一步时除外)。然后算法可以遵循一组规则,从可用选项中随机选择下一个图块。

例如:

If the last two movements were West then South,
  East would not be available.

If the last two movements were East then East,
  Southeast would not be available.

And so on.

【讨论】:

    【解决方案2】:

    下面是我解决这个问题的方法。

    1) 准确定义构成有效路径的规则。即将矩阵映射到布尔值的函数。根据您的问题,我不清楚什么是有效路径,我也不清楚您是否还。

    2) 重复生成随机排列的瓦片,直到产生的排列符合有效路径的规则。在当前的处理器上,这对于 6x6 将很快工作,但在较大的正方形上会花费更长的时间,例如100x100。

    算法解决方案可能是可行的,可以节省处理时间,但这在开发时间和代码简单性方面是速赢的。还具有为您提供均匀分布的优势,即每条路径都与任何其他路径一样可能产生。

    示例规则集可能是:

    且仅当矩阵满足所有这些条件时,路径才有效:

    1) 矩阵中的每个图块(除了两个末端图块)必须与四个可能方向 N、S、E、W 中的两个图块相邻

    2) 矩阵中的两个瓦片(末端瓦片)必须与四个可能方向 N、S、E、W 中的一个瓦片相邻

    3) 一个端瓦必须在顶行,另一个在底行

    【讨论】:

      【解决方案3】:

      我将使用一种方法生成一个迷宫,该方法提供一个迷宫,您可以从每个点到达另一个迷宫。递归回溯器已经足够好并且易于实现。

      在矩阵上生成迷宫后,您必须找到从起点到终点的路径(可以是每个点。您只需要记住,每一秒都可以是一堵墙)。您可以在文章底部的链接中找到许多不同的算法。通过使用递归回溯生成的迷宫,您可以保证根据需要连接行/列(就像我说的,每个点都相互连接),但并非每个位置都可以是路径(由于需要放置墙壁)

      您找到的从头到尾的路径符合您的要求,因此您只需删除不属于该路径的所有内容。

      如果您创建自己的堆栈,您可以轻松处理大小约为 2Kx2K 的矩阵。也许更大。

      为了进一步阅读与迷宫相关的所有内容,我推荐这个网站 http://www.astrolog.org/labyrnth/algrithm.htm

      您可以对迷宫生成器进行一些更改,这样就可以在任何可能的图块上开始/结束,而不仅仅是每隔一个。我能想到的最简单的解决方案就是在 50% 的情况下生成偏移量为 1 的迷宫。

      【讨论】:

        【解决方案4】:

        这里jsfiddle

        制作任意大小的行和列

        function yolYap(satir,sutun=0){ //My Create Path Function
        if(ilk){ //İlk seçim: First selection
            ilk=false;
            sutun = Math.floor((Math.random()*6)+1);
            matrix[satir][sutun].data('p', true);
            matrix[satir][sutun].addClass("red");
            yolYap(satir,sutun);
        }else{
            var xy=[];
            var solust=satir>1&&sutun>1?!matrix[satir-1][sutun-1].data("p"):true
            var sol=sutun>1?!matrix[satir][sutun-1].data("p"):false
            if(sol && solust)//{
                xy.push([satir,sutun-1]);//alert("<-");}
        
            var sagust=satir>1&&sutun<matrix[1].length-1?!matrix[satir-1][sutun+1].data("p"):true; 
            var sag=sutun<matrix[1].length-1?!matrix[satir][sutun+1].data("p"):false;
            if(sag && sagust)//{
                xy.push([satir,sutun+1]);//alert("->");}
            //satir>-1?alert("büyük"):alert("küçük");
        
            xy.push([satir+1,sutun]);
            var sec = Math.floor((Math.random()*102)+1)%xy.length;
            matrix[xy[sec][0]][xy[sec][1]].data("p", true);
            matrix[xy[sec][0]][xy[sec][1]].addClass("red");
            //alert(xy.join(" - "));
            if(xy[sec][0]<matrix.length-1)
                yolYap(xy[sec][0],xy[sec][1]);
            else
                alert("Finish");
        }
        

        }

        【讨论】:

          【解决方案5】:

          我想指出的一件事是,您应该将数组的范围更改为从零开始,或者修复生成的数字范围。目前,它正在生成一个包含无效索引的范围。由于您的问题没有集中在这个问题上,所以我保留了它。

          这会产生一条蜿蜒曲折的路径,该路径可以上下移动,直到它用完有效的移动或到达屏幕底部。这是一个 JFIDDLE http://jsfiddle.net/j6gkzbr5/1/

          var colorEn = ["RoyalBlue", "LawnGreen", "red", "orange", "yellow", "black", "white", "MediumOrchid"];
          var $color = "null";
          var matrix = [];
          var list = []
          
          $(document).ready(function () {
          
              createMyGrid();
              createPath();
          
          });
          
          function createPath() {
              var row = 1;
              var randomColumn = Math.floor(Math.random() * (matrix[1].length - 0) + 0);
          
              matrix[1][randomColumn].data('partOfPath', true);
              matrix[1][randomColumn].addClass("red");
          
             //Main loop, runs until we reach the final row.
              do {
                  CreateNewFrontier(row, randomColumn);
                  //list now contains a list of all legal moves to make
          
                  var randomNumber = Math.floor((Math.random() * (list.length)));
                  //Select one at random
          
                  row = list[randomNumber][0];
                  randomColumn = list[randomNumber][1];
          
                  //And mark it
                  MarkPath(row, randomColumn);
              } while (row < 6)//This should be matrix.length - 1
          }
          
          //This function clears out the previous list of valid moves and generates a new one.
          
          function CreateNewFrontier(row, column) {
              list = [];
          
              //Check if each cardinal direction falls within the bounds of the matrix.
              //If it does pass that node to the addtofrontier function for further consideration.
          
              //if (row - 1 >= 1) AddToFrontier(row - 1, column);
              //Commented out, as we are no longer considering paths that lead up.
              if (column + 1 < matrix[row].length) AddToFrontier(row, column + 1);
              if (row + 1 < matrix.length) AddToFrontier(row + 1, column);
              if (column - 1 >= 1) AddToFrontier(row, column - 1);
          }
          
          //This function checks to make sure nodes to be added to the frontier don't violate any restrictions
          //Mainly, per the question description, no node can touch more than 2 nodes on any cardinal direction
          
          function AddToFrontier(row, column) {
              //First we make sure this node is not already on the path. No backtracking, as it would violate the condition that there be only one continuous path.
          
              if (matrix[row][column].data('partOfPath') != true) {
          
                  //Now we need to make sure that this node currently only has 1 neighbor at the most that
                 //is already on a path, otherwise we will violate the single path condition.
                 //So count up all marked neighbors...
                  var markedNeighbors = 0;
                  if (row - 1 >= 1 && !IsNotMarked(row - 1, column)) {
                      markedNeighbors++;
                  }
                  if (column + 1 < matrix[row].length && !IsNotMarked(row, column + 1)) {
                      markedNeighbors++;
                  }
                  if (row + 1 < matrix.length && !IsNotMarked(row + 1, column)) {
                      markedNeighbors++;
                  }
                  if (column - 1 >= 1 && !IsNotMarked(row, column - 1)) {
                      markedNeighbors++;
                  }
          
                  //...and if there is only 1, we add the node to the list of possible moves.
                  if (markedNeighbors < 2) {
                      var index = list.length;
                      list[index] = [];
                      list[index][0] = row;
                      list[index][1] = column;
                  }
              }
          }
          
          //Helper function to mark a node as visited.
          function MarkPath(row, column) {
              matrix[row][column].data('partOfPath', true);
              matrix[row][column].addClass("red");
          }
          
          //Helper function to check if a path is marked. 
          //It looks a little odd because i'm not that familiar with JS and wasn't sure how an uninitialized     //variable would return, so i decided to return the opposite.
          
          function IsNotMarked(row, column) {
              if (row < 1 || row >= matrix.length) return true;
              if (column < 1 || column >= matrix[row].length) return true;
              return matrix[row][column].data('partOfPath') != true;
          }
          
          function createMyGrid() {
              //create 6x6 matrix
              for (var i = 1; i <= 6; i++) {
                  matrix[i] = [];
                  for (var j = 1; j <= 6; j++) {
                      var colorIndex = Math.floor(Math.random() * (colorEn.length - 0) + 0);
                      var $span = $('<span />').attr('class', 'colorSquare').html("[" + i + "][" + j + "]");
                      $("#grid").append($span);
                      matrix[i][j] = $span;
                  }
              }
          }
          
          function log(word) {
              console.log(word);
          }
          

          【讨论】:

          • 这在某些时候有效。有时它会生成一条不从上到下的路径 (i.imgur.com/u98ZYW9.png),或者根本不生成路径。你能解释一下你的代码吗?
          • 根本没有生成路径很可能是因为我在帖子开头所做的注释。有时生成的初始随机数不在 1 到 6 的范围内。关于有时不是从上到下,这可能是一个用尽所有可能的合法移动的例子。你链接的图片就是一个例子,它没有更多的动作,不会违反你的规定。如果你想保证每次运行都会产生一条向下的路径,你将不得不限制它向上,或者以不同的方式生成它。我将对代码进行一些注释。
          • 好吧,我加了一条不能向上的规则……只能从上到下,从左到右,而且必须从上到下连接。
          • 在这种情况下,注释掉“if (row - 1 >= 1) AddToFrontier(row - 1, column);”并且不再将“向上”视为可能的举动。我会在我的答案中的代码中做到这一点。
          • 谢谢。将循环更改为for (var i = 0; i &lt;6; i++) { 以解决非生成路径问题现在从第二行开始路径。
          猜你喜欢
          • 1970-01-01
          • 2020-01-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-23
          • 1970-01-01
          • 2019-05-31
          • 1970-01-01
          相关资源
          最近更新 更多