我已经做了很多搜索,并且已经找到了很多帮助来生成迷宫,但是我有一个非常具体的要求,并且我尝试过的所有循环都失败了。
我创建了一个编辑器,我可以绘制我需要的东西,但是生成器会有很大的帮助,但这已经失败了。
要求:
给定一个DIV元素的正方形网格(不小于10x10且不大于60x60),我需要一个穿过网格的连接路径,除了开始/结束之外,它不会在任何点触及自身。 在所有路径方块之间必须始终至少有一个空白方块(只要路径永远不会与自身接触,任何数量的空白都可以)。 没有死角也没有循环(路径会自行穿过)。
这有点像反向迷宫 - 我不需要填充整个网格,事实上我在路径周围有很多空间没有问题。可能更容易想到这与 Monopoly棋盘游戏的类似路线,其中围绕棋盘的路径徘徊而不是绕过边缘。我实际上被困在一个足够的描述中,因此称它为反向迷宫。
我尝试的事情:
许多过于复杂的循环。我不是非常接近,问题也是性能问题。 设计用于生成迷宫的大量代码。其中一些确实非常好,但它们都产生了一个典型的迷宫,这根本不是我真正需要的,并且调整代码已证明比在循环中编写一组疯狂的循环更难。
任何想法都会有所帮助。感谢。
更新代码
好的,我已经将KIKO的PHP代码翻译成了Javascript,但是在某个地方,我做了一个我无法追踪的简单错误:代码工作并生成一个正确尺寸的表格,并通过它生成一条路径。
但是,在函数“isWithinGrid”中,我必须从表的宽度和高度中减去1,否则整个事情都将失败,如果我这样做,代码将工作并创建一个通过表的路径减去一个单元格,虽然显然是路径的一部分,但会被错误地着色。
请注意,有时路径会被破坏或触碰自己。我毫不怀疑一些小问题导致所有这些,但目前这是我提出的最好的,任何进一步的帮助将不胜感激。
class Grid{
constructor(width,height){
this.width = width;
this.height = height;
this.cells = [];
for(var x=0; x < this.width; x++){
var tmparray = [];
for(var y=0; y < this.height; y++){
tmparray.push(false);
}
this.cells.push(tmparray);
}
}
isWithinGrid(x,y){
return (x >= 0) && (x <= this.width-1) && (y >= 0) && (y <= this.height-1);
}
isWithinPath(x,y){
return this.isWithinGrid(x,y) && this.cells[x][y];
}
setCellInPath(x,y,boolean){
this.cells[x][y] = boolean;
return this;
}
drawHorizontalLine(x1,x2,y){
for(var x=x1; x < x2; x++){
this.setCellInPath(x,y,true);
}
return this;
}
drawVerticalLine(x,y1,y2){
for(var y=y1; y < y2; y++){
this.setCellInPath(x,y,true);
}
return this;
}
drawSquare(){
var left = Math.round(this.width/5);
var right = Math.round(4*this.width/5);
var top = Math.round(this.height/5);
var bottom = Math.round(4*this.height/5);
this.drawHorizontalLine(left,right,top)
.drawHorizontalLine(left,right,bottom)
.drawVerticalLine(left,top,bottom)
.drawVerticalLine(right,top,bottom);
return this;
}
moveCell(x,y,dx,dy){
this.setCellInPath(x,y,false);
this.setCellInPath(x+dx,y+dy,true);
}
canMoveCell(x,y,dx,dy){
return this.isWithinPath(x,y) &&
this.isWithinGrid(x+dx,y+dy) &&
!this.isWithinPath(x+dx,y+dy) &&
!this.isWithinPath(x+2*dx,y+2*dy) &&
!this.isWithinPath(x+dy+dx,y+dx+dy)
!this.isWithinPath(x-dy+dx,y-dx+dy);
}
tryToDistortOnce(x,y,dx,dy){
if (!this.canMoveCell(x,y,dx,dy)) return false;
if (!this.canMoveCell(x+dy,y+dx,dx,dy)) return false;
if (!this.canMoveCell(x-dy,y-dx,dx,dy)) return false;
this.moveCell(x,y,dx,dy);
this.setCellInPath(x+dy+dx,y+dx+dy,true);
this.setCellInPath(x-dy+dx,y-dx+dy,true);
return true;
}
distortOnce(){
var x=0, y=0, dx=0, dy=0;
do {
x = Math.floor(Math.random() * this.width) + 1;
y = Math.floor(Math.random() * this.height) + 1;
} while (!this.isWithinPath(x,y));
switch (Math.floor(Math.random() * 4) + 1){
case 1: dx = -1; dy = 0; break;
case 2: dx = +1; dy = 0; break;
case 3: dx = 0; dy = +1; break;
case 4: dx = 0; dy = -1; break;
}
if (this.tryToDistortOnce(x,y,dx,dy)){
do {
x += dx;
y += dy;
} while (this.tryToDistortOnce(x,y,dx,dy));
return true;
}
return false;
}
distortPath(numberOfDistortions = 10){
for(var counter=1; counter < numberOfDistortions; counter++){
var tries = 0;
while (!this.distortOnce() && (tries < this.width+this.height)){ tries++; }
}
return this;
}
renderGrid(){
var str = '<table class="TSTTAB">';
for(var y=0; y < this.width; y++){
for(var x=0; x < this.height; x++){
str += '<td'+(this.cells[y][x] ? ' class="path">' : '>');
}
str += '</tr>';
}
str += '</table>';
document.getElementById('cont').innerHTML =str;
return this;
}
}
var Testgrid = new Grid(20,20);
Testgrid.drawSquare().distortPath(10).renderGrid();
.TSTTAB{background-color:#7F7F7F;border-collapse:collapse;}
.TSTTAB td{ width:20px; height: 20px; border: 1px solid #000;background-color: #E5E5E5; }
.TSTTAB td.path { background-color: #44F; }
<div id='cont'></div>
答案 0 :(得分:3)
嗯,我试过了。对于一个简单的问题,一小时的工作似乎绰绰有余。当然,这远非完美,但它说明了我在谈论的内容。它会生成如下解决方案:
完整的代码是:
<?php
// error reporting
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// configuration
const SIZE_X = 20;
const SIZE_Y = 20;
const COMPLEXITY = 20;
// grid class
class Grid
{
public function __construct($width,$height)
{
// remember
$this->width = $width;
$this->height = $height;
// initiate grid
foreach (range(1,$width) as $x) {
foreach (range(1,$height) as $y) {
$this->cells[$x][$y] = FALSE; // false means: not in path
}
}
}
public function isWithinGrid($x,$y)
// testb whether (x,y) is within the grid
{
return ($x >= 1) && ($x <= $this->width) &&
($y >= 1) && ($y <= $this->height);
}
public function isWithinPath($x,$y)
// is a cell part of the path?
{
return $this->isWithinGrid($x,$y) && $this->cells[$x][$y];
}
public function setCellInPath($x,$y,$boolean)
// remember whether a cell is part of the path or not
{
$this->cells[$x][$y] = $boolean;
return $this;
}
public function drawHorizontalLine($x1,$x2,$y)
// simple horizontal line
{
foreach (range($x1,$x2) as $x) $this->setCellInPath($x,$y,TRUE);
return $this;
}
public function drawVerticalLine($x,$y1,$y2)
// simple vertical line
{
foreach (range($y1,$y2) as $y) $this->setCellInPath($x,$y,TRUE);
return $this;
}
public function drawSquare()
// simple square
{
$left = round($this->width/5);
$right = round(4*$this->width/5);
$top = round($this->height/5);
$bottom = round(4*$this->height/5);
$this->drawHorizontalLine($left,$right,$top)
->drawHorizontalLine($left,$right,$bottom)
->drawVerticalLine($left,$top,$bottom)
->drawVerticalLine($right,$top,$bottom);
return $this;
}
private function moveCell($x,$y,$dx,$dy)
// move a cell
{
$this->setCellInPath($x,$y,FALSE);
$this->setCellInPath($x+$dx,$y+$dy,TRUE);
}
private function canMoveCell($x,$y,$dx,$dy)
// answers the question whether or not we can move (x,y) by (dx,dy)
{
return $this->isWithinPath($x,$y) && // must be part of path
$this->isWithinGrid($x+$dx,$y+$dy) && // stay within grid
!$this->isWithinPath($x+$dx,$y+$dy) && // but not on the path
!$this->isWithinPath($x+2*$dx,$y+2*$dy) && // and don't touch path
!$this->isWithinPath($x+$dy+$dx,$y+$dx+$dy) && // and don't touch path
!$this->isWithinPath($x-$dy+$dx,$y-$dx+$dy); // and don't touch path
}
private function tryToDistortOnce($x,$y,$dx,$dy)
{
// this one should be able to move
if (!$this->canMoveCell($x,$y,$dx,$dy)) return FALSE;
// but also its neighbours must be able to move
if (!$this->canMoveCell($x+$dy,$y+$dx,$dx,$dy)) return FALSE;
if (!$this->canMoveCell($x-$dy,$y-$dx,$dx,$dy)) return FALSE;
// move the target cell by displacement
$this->moveCell($x,$y,$dx,$dy);
// move neighbours by adding two cells
$this->setCellInPath($x+$dy+$dx,$y+$dx+$dy,TRUE);
$this->setCellInPath($x-$dy+$dx,$y-$dx+$dy,TRUE);
return TRUE; // success!
}
private function distortOnce()
// distort a random cell, returns success or failure
{
// find a random cell in path
do {
$x = rand(1,$this->width);
$y = rand(1,$this->height);
} while (!$this->isWithinPath($x,$y));
// choose one of four directions to move in
switch (rand(1,4))
{
case 1: $dx = -1; $dy = 0; break;
case 2: $dx = +1; $dy = 0; break;
case 3: $dx = 0; $dy = +1; break;
case 4: $dx = 0; $dy = -1; break;
}
// try to do it
if ($this->tryToDistortOnce($x,$y,$dx,$dy))
{
// more moves
do {
$x += $dx;
$y += $dy;
} while ($this->tryToDistortOnce($x,$y,$dx,$dy));
return TRUE; // it was a success!
}
return FALSE; // we failed
}
public function distortPath($numberOfDistortions = 10)
// distort up to a certain amount of times
{
// find a random cell that is part of the path to distort
for ($counter = 1; $counter <= $numberOfDistortions; $counter++) {
// we try that a limited number of times, depending on the grid size
$tries = 0;
while (!$this->distortOnce() &&
($tries < $this->width+$this->height)) { $tries++; }
}
return $this;
}
public function renderGrid()
// render grid
{
echo '<!DOCTYPE HTML><html><head><style>'.
' td { width:20px; height: 20px; border: 1px solid #000; }'.
' .path { background-color: #44F; }'.
'</style></head><body><table>';
foreach (range(1,SIZE_Y) as $y) {
echo '<tr>';
foreach (range(1,SIZE_X) as $x) {
echo '<td'.($this->cells[$x][$y] ? ' class="path">' : '>');
}
echo '</tr>';
}
echo '</body></html></table>';
return $this;
}
}
// create grid
$grid = new Grid(SIZE_X,SIZE_Y);
// start with a square, distort and then render
$grid->drawSquare()
->distortPath(COMPLEXITY)
->renderGrid();
你可以做很多事情来改善这个......玩得开心!
在我的服务器上,此代码需要2到5毫秒才能执行。里程可能会有所不同......