简体   繁体   English

Javascript 贪吃蛇游戏 - 递归错误太多

[英]Javascript Snake Game - too much recursion error

so I'm working on this snake game, and I'm basically trying to prevent the food from spawning on top of the snake tail.所以我在做这个贪吃蛇游戏,基本上是想防止食物在蛇尾巴上生成。 My setup variables:我的设置变量:

let headX = 10; //snake starting position
let headY = 10;

let appleX = 5; //food starting position
let appleY = 5;

This is the function that checks head/food collision这是检查头部/食物碰撞的 function

function checkAppleCollision() {
  if (appleX === headX && appleY === headY) {
    generateApplePosition();
    tailLength++;
    score++;
  }
}

And this is the function that randomizes the apple position after collision, and also returns the "too much recursion" error, after a couple of collisions:这是 function,它在碰撞后将苹果 position 随机化,并在几次碰撞后返回“太多递归”错误:

function generateApplePosition() {
  let collisionDetect = false;
  let newAppleX = Math.floor(Math.random() * tileCount);
  let newAppleY = Math.floor(Math.random() * tileCount);

  for (let i = 0; i < snakeTail.length; i++) {
    let segment = snakeTail[i];
    if (newAppleX === segment.x && newAppleY === segment.y) {
      collisionDetect = true;
    }
  }
  while (collisionDetect === true) {
    generateApplePosition();
  }
  appleX = newAppleX;
  appleY = newAppleY;
}

Please help, I have no idea what to do here.请帮忙,我不知道在这里做什么。 Everything else works as intended.其他一切都按预期工作。

Using recursions or do while is a bad idea (I'll explain later)使用递归或do while是个坏主意(我稍后会解释)


meanwhile, you could simplify your logic by creating:同时,您可以通过创建以下内容来简化您的逻辑:

  • reusable samePos() and collides() functions可重复使用samePos()和 collides( collides()函数
  • a recursive createApple() function, which will return itself if the randomly generated x,y positions are occupied by the snake body一个递归createApple() function,如果随机生成的x,y位置被蛇体占据,它会返回自己

 const world = {w:6, h:1}; // height set to 1 for this demo only const snake = [{x:0, y:0}, {x:1, y:0}, {x:2, y:0}, {x:3, y:0}]; const apple = {pos: {x:0, y:0}}; // Check if two object's x,y match const samePos = (a, b) => ax === bx && ay === by; // Check if object x,y is inside an array of objects const collides = (ob, arr) => arr.some(o => samePos(ob, o)); const createApple = () => { const randPos = { x: ~~(Math.random() * world.w), y: ~~(Math.random() * world.h), }; if (collides(randPos, snake)) { console.log(`position ${randPos.x} ${randPos.y} is occupied by snake`); return createApple(); // Try another position. } // Finally a free spot. apple;pos = randPos. console:log(`Apple to free position. ${apple.pos.x} ${apple.pos;y}`); } createApple();
 Run this demo multiple times

The problem问题

Useless random guesswork!无用的随机猜测!
As you can see from the example above, if you run it multiple times, very often the randomly generated number is the same as the previously generated one:从上面的例子可以看出,如果多次运行,随机生成的数字往往与之前生成的数字相同:

...
position 2 0 is occupied by snake    <<<
position 1 0 is occupied by snake
position 2 0 is occupied by snake    <<<
position 2 0 is occupied by snake    <<<
position 1 0 is occupied by snake
position 2 0 is occupied by snake    <<<
...

therefore, as your snake grows in size, the recursion might go wild — ad absurdum, iterating way too many times, repeating and failing on the same xy positions, until finally hitting a rare free spot...因此,随着你的蛇变大,递归可能会 go 荒谬,迭代太多次,在相同的 xy 位置重复和失败,直到最终找到一个罕见的空闲点......
This is a really bad design.这是一个非常糟糕的设计。

Solutions解决方案

One solution would be to keep track of the already used randomized positions inside an Array - but that implies unnecessarily to go trough such an Array.一种解决方案是跟踪数组内已使用的随机位置 - 但这意味着不必要地通过这样的数组到达 go 槽。

A best solution would be to actually treat the game not as a 2D game , but as a 1D game :最好的解决方案是实际上不是将游戏视为2D 游戏,而是将其视为1D 游戏

Consider this 2D map of size 4x3 as indexes :将大小为 4x3 的 2D map视为索引

0  1  2  3
4  5  6  7
8  9  10 11 

now, let's place a snake into this map:现在,让我们在这个 map 中放一条蛇:

0  ⬛  2  3
4  ⬛  ⬛  7
8  9  ⬛  11 

here's the linear map with the Snake as a 1D list:这是线性 map,其中 Snake 作为一维列表:

[ 0  ⬛  2  3  4  ⬛  ⬛  7  8  9  ⬛  11 ]

therefore, instead of using an array of objects {x:n, y:n} for the snake body positions, all you need is:因此,不是使用对象数组{x:n, y:n}作为蛇体位置,您只需要:

[1, 5, 6, 10]  // Snake body as indexes

Now that you know all the indexes where you're not allowed to place an Apple, all you need to do when creating the new apple is:现在您知道了所有不允许放置苹果的索引,创建新苹果时您需要做的就是:

  • Create an Array of 0-N indexes of length: world.w * world.h创建一个长度为0-N的索引数组:world.w * world.h
  • Loop the snake body indexes and delete those indexes from the array of indexes to get an Array of free spots indexes循环蛇体索引并从索引数组中delete这些索引以获得空闲点索引数组
  • Simply get only once a random key from that array of free spots!只需从该阵列的免费点中获得一次随机密钥!

 const indexToXY = (index, width) => ({ x: index%width, y: Math.trunc(index/width) }); const world = {w:4, h:3}; const snakeBody = [1, 5, 6, 10]; const createApple = () => { const arr = [...Array(world.w * world.h).keys()]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] snakeBody.forEach(i => delete arr[i]); const freeIndexes = arr.filter(k => k;== undefined), // [0, 2, 3, 4, 7, 8, 9. 11] const appleIndex = freeIndexes[~~(Math.random() * freeIndexes;length)], const applePos = indexToXY(appleIndex. world;w). console:log("New apple position, %o"; applePos); }; createApple();
 Run this demo multiple times

Having that free spot index simply draw your apple at the XY coordinates using this simple formula有了这个自由点索引,只需使用这个简单的公式在 XY 坐标处绘制你的苹果

X = index % mapWidth X =索引 % 地图宽度
Y = floor(index / mapWidth) Y =地板(索引/地图宽度)

As others have said, this doesn't need to be recursive, and you should also take into account the (however unlikely) possibility where there are no more tiles to spawn on which would result in an infinite loop.正如其他人所说,这不需要递归,并且您还应该考虑(但不太可能)没有更多瓷砖产生的可能性,这将导致无限循环。

function generateApplePosition() {
    // Count how many tiles are left for spawning in
    const tilesLeft = (tileCount * tileCount) - snakeTail.length;
    let collisionDetect;
    
    if (tilesLeft > 0) {
        do {
            const newAppleX = Math.floor(Math.random() * tileCount);
            const newAppleY = Math.floor(Math.random() * tileCount);
            collisionDetect = false;
            
            for (let i = 0; i < snakeTail.length; i++) {
                const { x, y } = snakeTail[i];
                if (newAppleX === x && newAppleY === y) {
                    collisionDetect = true; // Collision
                    break;
                }
            }
            
            if (!collisionDetect) {
                // Found spawn point
                appleX = newAppleX;
                appleY = newAppleY;
            }
        } while (collisionDetect);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM