是否可以使用 requestanimationframe 对递归进行建模?
Is it possible to model recursion with requestanimationframe?
我正在创建一个迷宫生成函数,它需要使用递归回溯,显然需要递归。函数要运行length * breath
次,有时会超过最大递归深度。以下是迷宫的代码:
function maze(width, height){
var grid = [];
for (var y = 0; y < height; y++){
grid.push([]);
for (var x = 0; x < width; x++) grid[y].push(0);
}
function shuffle(){
var result = ["N", "S", "E", "W"];
for (var count = result.length; count > 0; count--){
rand = Math.floor(Math.random() * count);
[result[count - 1], result[rand]] = [result[rand], result[count - 1]];
}
return result;
}
function carve(curr_x, curr_y){
for (var dir of shuffle()){
var new_x = curr_x + {N: 0, S: 0, E: 1, W: -1}[dir], new_y = curr_y + {N: -1, S: 1, E: 0, W: 0}[dir];
if (new_y >= 0 && new_y <= height - 1 && new_x >= 0 && new_x <= width - 1 && grid[new_y][new_x] == 0){
grid[curr_y][curr_x] += {N: 1, S: 2, E: 4, W: 8}[dir];
grid[new_y][new_x] += {N: 2, S: 1, E: 8, W: 4}[dir];
carve(new_x, new_y);
}
}
}
carve(Math.floor(width / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * width)), Math.floor(height / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * height)));
return grid;
}
鉴于递归的定义,我相信这个函数可以在requestAnimationFrame
中重写,这样就不会超过最大递归深度。可能吗?有什么方法可以将递归转换为其他东西吗?谢谢!
这是在单个 while 循环中重构为 运行 的代码示例。我不确定是否有具体的方法,但只是向您展示代码可能会有所帮助...
这是主要部分:
let pos = start;
let revisit = [];
while (pos) {
const next = randomEmptyAdjacent(pos);
if (next) {
carve(pos, next);
revisit.push(pos); // Mark this cell for revisiting
pos = next; // Go depth-first
} else {
pos = revisit.pop();
}
}
使用 requestAnimationFrame
或 setTimeout
来逃避堆栈从来都不是一个好主意。它会使代码 运行 变慢。
例子
这是 运行 可用代码段中的内容。我添加了迷宫的可视化,以便于检查输出。
function maze(width, height) {
const grid = [];
const start = [
Math.floor(width / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * width)),
Math.floor(height / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * height))
];
for (var y = 0; y < height; y++) {
grid.push([]);
for (var x = 0; x < width; x++) grid[y].push(0);
}
const randomEmptyAdjacent = ([y, x]) => {
const available = [
[y - 1, x], // top
[y, x + 1], // right
[y + 1, x], // bottom
[y, x - 1] // left
].filter(
([y, x]) => (
y >= 0 && y <= height - 1 &&
x >= 0 && x <= width - 1 &&
grid[y][x] === 0
)
);
return available[Math.floor(Math.random() * available.length)];
}
const carve = (from, to) => {
const [y1, x1] = from;
const [y2, x2] = to;
const dy = y2 - y1;
const dx = x2 - x1;
if (dy === 1) {
grid[y1][x1] += 2;
grid[y2][x2] += 1;
} else if (dy === -1) {
grid[y1][x1] += 1;
grid[y2][x2] += 2;
}
if (dx === 1) {
grid[y1][x1] += 4;
grid[y2][x2] += 8;
} else if (dx === -1) {
grid[y1][x1] += 8;
grid[y2][x2] += 4;
}
}
let pos = start;
let revisit = [];
while (pos) {
const next = randomEmptyAdjacent(pos);
if (next) {
carve(pos, next);
revisit.push(pos);
pos = next;
} else {
pos = revisit.pop();
}
}
return grid;
}
const renderMaze = maze => {
const h = maze.length;
const w = maze[0].length;
const S = 10;
const mazeEl = document.querySelector(".maze");
mazeEl.innerHTML = "";
mazeEl.style.width = `${w * S}px`;
mazeEl.style.height = `${h * S}px`;
for (const row of maze) {
for (const cell of row) {
const cellEl = document.createElement("div");
cellEl.classList.add("cell");
if (cell & 8) {
cellEl.style.borderLeftColor = "transparent";
}
if (cell & 4) {
cellEl.style.borderRightColor = "transparent";
}
if (cell & 2) {
cellEl.style.borderBottomColor = "transparent";
}
if (cell & 1) {
cellEl.style.borderTopColor = "transparent";
}
cellEl.style.width = `${S - 2}px`;
cellEl.style.height = `${S - 2}px`;
mazeEl.appendChild(cellEl);
}
}
}
const m = maze(50, 50);
renderMaze(m);
.maze {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
}
.cell {
flex: none;
border-width: 1px;
border-style: solid;
border-color: black;
font-size: 12px;
}
<div class="maze"></div>
动画!
只是为了好玩,这里有一个 requestAnimationFrame
有用的例子:动画的东西!此代码段在单个循环中生成迷宫,但使用 requestAnimationFrame
对要绘制的单元格的渲染进行排队 frame-by-frame。
function maze(width, height) {
const start = [
Math.floor(width / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * width)),
Math.floor(height / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * height))
];
const grid = [];
const S = 6;
const mazeEl = document.querySelector(".maze");
mazeEl.innerHTML = "";
mazeEl.style.width = `${width * S}px`;
mazeEl.style.height = `${height * S}px`;
for (var y = 0; y < height; y++) {
grid.push([]);
for (var x = 0; x < width; x++) {
grid[y].push(0);
const cellEl = document.createElement("div");
cellEl.style.width = `${S - 2}px`;
cellEl.style.height = `${S - 2}px`;
cellEl.classList.add("cell");
mazeEl.appendChild(cellEl);
}
}
const randomEmptyAdjacent = ([y, x]) => {
const available = [
[y - 1, x], // top
[y, x + 1], // right
[y + 1, x], // bottom
[y, x - 1] // left
].filter(
([y, x]) => (
y >= 0 && y <= height - 1 &&
x >= 0 && x <= width - 1 &&
grid[y][x] === 0
)
);
return available[Math.floor(Math.random() * available.length)];
}
const carve = (from, to) => {
const [y1, x1] = from;
const [y2, x2] = to;
const dy = y2 - y1;
const dx = x2 - x1;
if (dy === 1) {
grid[y1][x1] += 2;
grid[y2][x2] += 1;
} else if (dy === -1) {
grid[y1][x1] += 1;
grid[y2][x2] += 2;
}
if (dx === 1) {
grid[y1][x1] += 4;
grid[y2][x2] += 8;
} else if (dx === -1) {
grid[y1][x1] += 8;
grid[y2][x2] += 4;
}
queue(
mazeEl.children[y1 * width + x1], grid[y1][x1],
mazeEl.children[y2 * width + x2], grid[y2][x2],
)
}
let pos = start;
let revisit = [];
while (pos) {
const next = randomEmptyAdjacent(pos);
if (next) {
carve(pos, next);
revisit.push(pos);
pos = next;
} else {
pos = revisit.pop();
}
}
return grid;
}
const renderQueue = [];
const queue = (c1, v1, c2, v2) => {
renderQueue.push(() => {
renderCell(c1, v1);
renderCell(c2, v2);
const next = renderQueue.shift();
if (next) requestAnimationFrame(next);
});
}
const renderCell = (cellEl, cell) => {
if (cell & 8) {
cellEl.style.borderLeftColor = "transparent";
}
if (cell & 4) {
cellEl.style.borderRightColor = "transparent";
}
if (cell & 2) {
cellEl.style.borderBottomColor = "transparent";
}
if (cell & 1) {
cellEl.style.borderTopColor = "transparent";
}
}
const m = maze(40, 40);
renderQueue.shift()();
.maze {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
}
.cell {
flex: none;
border-width: 1px;
border-style: solid;
border-color: black;
font-size: 12px;
}
<div class="maze"></div>
我正在创建一个迷宫生成函数,它需要使用递归回溯,显然需要递归。函数要运行length * breath
次,有时会超过最大递归深度。以下是迷宫的代码:
function maze(width, height){
var grid = [];
for (var y = 0; y < height; y++){
grid.push([]);
for (var x = 0; x < width; x++) grid[y].push(0);
}
function shuffle(){
var result = ["N", "S", "E", "W"];
for (var count = result.length; count > 0; count--){
rand = Math.floor(Math.random() * count);
[result[count - 1], result[rand]] = [result[rand], result[count - 1]];
}
return result;
}
function carve(curr_x, curr_y){
for (var dir of shuffle()){
var new_x = curr_x + {N: 0, S: 0, E: 1, W: -1}[dir], new_y = curr_y + {N: -1, S: 1, E: 0, W: 0}[dir];
if (new_y >= 0 && new_y <= height - 1 && new_x >= 0 && new_x <= width - 1 && grid[new_y][new_x] == 0){
grid[curr_y][curr_x] += {N: 1, S: 2, E: 4, W: 8}[dir];
grid[new_y][new_x] += {N: 2, S: 1, E: 8, W: 4}[dir];
carve(new_x, new_y);
}
}
}
carve(Math.floor(width / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * width)), Math.floor(height / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * height)));
return grid;
}
鉴于递归的定义,我相信这个函数可以在requestAnimationFrame
中重写,这样就不会超过最大递归深度。可能吗?有什么方法可以将递归转换为其他东西吗?谢谢!
这是在单个 while 循环中重构为 运行 的代码示例。我不确定是否有具体的方法,但只是向您展示代码可能会有所帮助...
这是主要部分:
let pos = start;
let revisit = [];
while (pos) {
const next = randomEmptyAdjacent(pos);
if (next) {
carve(pos, next);
revisit.push(pos); // Mark this cell for revisiting
pos = next; // Go depth-first
} else {
pos = revisit.pop();
}
}
使用 requestAnimationFrame
或 setTimeout
来逃避堆栈从来都不是一个好主意。它会使代码 运行 变慢。
例子
这是 运行 可用代码段中的内容。我添加了迷宫的可视化,以便于检查输出。
function maze(width, height) {
const grid = [];
const start = [
Math.floor(width / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * width)),
Math.floor(height / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * height))
];
for (var y = 0; y < height; y++) {
grid.push([]);
for (var x = 0; x < width; x++) grid[y].push(0);
}
const randomEmptyAdjacent = ([y, x]) => {
const available = [
[y - 1, x], // top
[y, x + 1], // right
[y + 1, x], // bottom
[y, x - 1] // left
].filter(
([y, x]) => (
y >= 0 && y <= height - 1 &&
x >= 0 && x <= width - 1 &&
grid[y][x] === 0
)
);
return available[Math.floor(Math.random() * available.length)];
}
const carve = (from, to) => {
const [y1, x1] = from;
const [y2, x2] = to;
const dy = y2 - y1;
const dx = x2 - x1;
if (dy === 1) {
grid[y1][x1] += 2;
grid[y2][x2] += 1;
} else if (dy === -1) {
grid[y1][x1] += 1;
grid[y2][x2] += 2;
}
if (dx === 1) {
grid[y1][x1] += 4;
grid[y2][x2] += 8;
} else if (dx === -1) {
grid[y1][x1] += 8;
grid[y2][x2] += 4;
}
}
let pos = start;
let revisit = [];
while (pos) {
const next = randomEmptyAdjacent(pos);
if (next) {
carve(pos, next);
revisit.push(pos);
pos = next;
} else {
pos = revisit.pop();
}
}
return grid;
}
const renderMaze = maze => {
const h = maze.length;
const w = maze[0].length;
const S = 10;
const mazeEl = document.querySelector(".maze");
mazeEl.innerHTML = "";
mazeEl.style.width = `${w * S}px`;
mazeEl.style.height = `${h * S}px`;
for (const row of maze) {
for (const cell of row) {
const cellEl = document.createElement("div");
cellEl.classList.add("cell");
if (cell & 8) {
cellEl.style.borderLeftColor = "transparent";
}
if (cell & 4) {
cellEl.style.borderRightColor = "transparent";
}
if (cell & 2) {
cellEl.style.borderBottomColor = "transparent";
}
if (cell & 1) {
cellEl.style.borderTopColor = "transparent";
}
cellEl.style.width = `${S - 2}px`;
cellEl.style.height = `${S - 2}px`;
mazeEl.appendChild(cellEl);
}
}
}
const m = maze(50, 50);
renderMaze(m);
.maze {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
}
.cell {
flex: none;
border-width: 1px;
border-style: solid;
border-color: black;
font-size: 12px;
}
<div class="maze"></div>
动画!
只是为了好玩,这里有一个 requestAnimationFrame
有用的例子:动画的东西!此代码段在单个循环中生成迷宫,但使用 requestAnimationFrame
对要绘制的单元格的渲染进行排队 frame-by-frame。
function maze(width, height) {
const start = [
Math.floor(width / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * width)),
Math.floor(height / 3) + Math.floor(Math.random() * Math.floor(2 / 3 * height))
];
const grid = [];
const S = 6;
const mazeEl = document.querySelector(".maze");
mazeEl.innerHTML = "";
mazeEl.style.width = `${width * S}px`;
mazeEl.style.height = `${height * S}px`;
for (var y = 0; y < height; y++) {
grid.push([]);
for (var x = 0; x < width; x++) {
grid[y].push(0);
const cellEl = document.createElement("div");
cellEl.style.width = `${S - 2}px`;
cellEl.style.height = `${S - 2}px`;
cellEl.classList.add("cell");
mazeEl.appendChild(cellEl);
}
}
const randomEmptyAdjacent = ([y, x]) => {
const available = [
[y - 1, x], // top
[y, x + 1], // right
[y + 1, x], // bottom
[y, x - 1] // left
].filter(
([y, x]) => (
y >= 0 && y <= height - 1 &&
x >= 0 && x <= width - 1 &&
grid[y][x] === 0
)
);
return available[Math.floor(Math.random() * available.length)];
}
const carve = (from, to) => {
const [y1, x1] = from;
const [y2, x2] = to;
const dy = y2 - y1;
const dx = x2 - x1;
if (dy === 1) {
grid[y1][x1] += 2;
grid[y2][x2] += 1;
} else if (dy === -1) {
grid[y1][x1] += 1;
grid[y2][x2] += 2;
}
if (dx === 1) {
grid[y1][x1] += 4;
grid[y2][x2] += 8;
} else if (dx === -1) {
grid[y1][x1] += 8;
grid[y2][x2] += 4;
}
queue(
mazeEl.children[y1 * width + x1], grid[y1][x1],
mazeEl.children[y2 * width + x2], grid[y2][x2],
)
}
let pos = start;
let revisit = [];
while (pos) {
const next = randomEmptyAdjacent(pos);
if (next) {
carve(pos, next);
revisit.push(pos);
pos = next;
} else {
pos = revisit.pop();
}
}
return grid;
}
const renderQueue = [];
const queue = (c1, v1, c2, v2) => {
renderQueue.push(() => {
renderCell(c1, v1);
renderCell(c2, v2);
const next = renderQueue.shift();
if (next) requestAnimationFrame(next);
});
}
const renderCell = (cellEl, cell) => {
if (cell & 8) {
cellEl.style.borderLeftColor = "transparent";
}
if (cell & 4) {
cellEl.style.borderRightColor = "transparent";
}
if (cell & 2) {
cellEl.style.borderBottomColor = "transparent";
}
if (cell & 1) {
cellEl.style.borderTopColor = "transparent";
}
}
const m = maze(40, 40);
renderQueue.shift()();
.maze {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
}
.cell {
flex: none;
border-width: 1px;
border-style: solid;
border-color: black;
font-size: 12px;
}
<div class="maze"></div>