我用JavaScript制作了Conway的《人生游戏》,希望有人能给我一些有关检查相邻单元格的逻辑的指导。我知道必须有更好的方法,但同时也可以。 >
var board = [["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""],
            ["","","","","","","","","","","","","","","","","","","","","","","","","","","","","",""]];

//boolean to tell if cell is alive
var alive = new Boolean(null);

//counter will be used to see how many cells are alive
var counter;



//number of initial alive cells
var number = Math.floor((Math.random()*900)+5);

//coordinates for alive cells
var xcor;
var ycor;

//make table and assign ID to all cells
var idAssign;
document.write("<table border='1px>'");
for (var x = 0; x < 30; x++ ) {
    document.write("<tr>");

    for (var y = 0; y < 30; y ++) {
        idAssign = x.toString() + y.toString();
        document.write("<td id='" + idAssign + "'>oo</td>");
    }
    document.write("</tr>");
}
document.write("</table>");

//generate coordinates for the cells that are alive
for (var i=0; i < number; i++) {
    xcor = Math.floor((Math.random()*29)+0);
    ycor = Math.floor((Math.random()*29)+0);

    board[xcor][ycor] = "x";

}
function run(){

    //loop to check all cells
    for (var x=0; x != 30; x++) {
        for (var y=29; y != -1; y--) {

            //reset counter and boolean after every iteration
            counter = 0;
            alive = null;

            //evaluation of cells
            var check;
            check = x.toString() + y.toString();

            //check current cell
            if(board[x][y] == "x") {
                alive = true;
            }
            else{
                alive = false;
            }

            //BOUNDS
            //Handle left bound
            if (y == 0) {
                if (board[x ][(y + 1)] == "x"){
                    counter ++;
                }
                //Left bottom corner
                if (x == 29) {
                    if (board[(x - 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                }
                //Left top corner
                else if (x == 0) {
                    if (board[(x + 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                }
                else{
                    if (board[(x - 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[x][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                }
            }
            //handle right bound
            else if (y == 29) {
                if (board[(x)][(y - 1)] == "x"){
                    counter ++;
                }
                //right bottom corner
                if (x == 29) {
                    if (board[(x - 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y - 1)] == "x") {
                        counter ++;
                    }
                }
                //right top corner
                else if (x == 0) {
                    if (board[x + 1][y] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][(y - 1)] == "x") {
                        counter ++;
                    }
                }
                else{
                    if (board[(x - 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y - 1)] == "x") {
                        counter ++;
                    }
                    if (board[x][(y - 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][(x - 1)] == "x") {
                        counter ++;
                    }
                }
            }
            else{
                //Top bounds
                if (x == 0) {
                    if (board[x][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[x][(y - 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][y + 1] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][y - 1] == "x") {
                        counter ++;
                    }

                }
                //Bottom bounds
                else if (x == 29) {
                    if (board[x][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[x][(y - 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y - 1)] == "x") {
                        counter ++;
                    }
                }
                else{
                    //center of board
                    if (board[x][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[x][(y - 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][y] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x - 1)][(y - 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][(y + 1)] == "x") {
                        counter ++;
                    }
                    if (board[(x + 1)][(y - 1)] == "x") {
                        counter ++;
                    }
                }
            } //end board checking

            //apply rules to cell
            if (alive == true) {
                if (counter < 2) {
                    board[x][y] = "";
                }
                if (counter > 3) {
                    board[x][y] = "";
                }
            }
            else if (alive == false) {
                if (counter == 3) {
                    board[x][y] = "x";
                }
                else if (counter != 3){
                    board[x][y] = "";
                }
            }


        }//Inner FOR

    }// Outer FOR

    var IDcheck;
        for (var x= 0; x < 30; x ++) {
            for (var y = 0; y <30; y ++) {
                IDcheck = x.toString() + y.toString();
                if (board[x][y] == "x" ) {
                    document.getElementById(IDcheck).className = 'active';
                }
                else{
                    document.getElementById(IDcheck).className = 'NotActive';
                }
            }
        }

}//WHILE


我将所有空间手动设置为数组的原因是,除了随机生成细菌的起始图之外,还可以注释掉该代码并将其手动放置到看到他们可以做出的那种样式。

评论

大约一年前,我用Java编写了一个“人生游戏”作为练习。我使用html5 canvas进行渲染,但是您可以看到我是如何解决某些问题的。 jsfiddle.net/mgvd/a2tVe

jsfiddle链接在我的浏览器中不起作用,因此我不知道您如何解决任何问题。您应将详细信息发布在此网站上,并带有仅供参考的链接。

感谢所有回答。我不会给出正确答案,但我确实打算这样做。我只想看一两天,看看是否还有其他东西可以。感谢您的帮助!

不值得一个完整的答案,但是setInterval调用中的function(){run();}可以更简洁地编写run -因为run已经是一个不带参数的函数,所以我没有理由想将其包装为匿名没有参数的函数。另外,请记住,var仅作用于当前函数(包括在var声明上方编写的代码),而不是当前{}对。因此,对于(var x = 0; x!= 30; x ++),人们对x的范围产生了误解。

#1 楼

这里有一个主要的错误/问题,这里还提出了三个非常不同的建议。

错误/问题

在生命游戏中,您应该扫描整个董事会,然后才应用更改。您将在过程的一部分进行更改(在检查每个单元格时,将更改其状态)。因此,如果您在一个位置更改单元格,则在检查它的邻居时会影响结果。

您需要为每个单元格“存储”计数器,直到完成扫描为止,并且然后重新设置板上的每个块。本质上,您需要以下内容:

var counter[];
//loop to check all cells
for (var x=0; x < DIMENSION; x++) {
    for (var y=0; y < DIMENSION; y++) {
        counter[x][y] = 0;
        // check all cells and update counter[x][y]
    }
}
// loop to update board....
for (var x=0; x < DIMENSION; x++) {
    for (var y=0; y < DIMENSION; y++) {
        // check whether we are alive or dead....
        if (counter[x][y] == 2 || counter[x][y] == 3) {
            board[x][y] = "x";
        } else {
            board[x][y] = "";
        }
    }
}


代码样式:

认真地,您手动创建了空白的board吗?那不是手腕酸痛吗?懒惰的程序员是一件好事,请考虑以下因素:

var DIMENSION= 30;
var board = [];
for (var y = 0; y < DIMENSION; y++) {
    for (var x = 0; x < DIMENSION; x++) {
        board[y][x] = "";
    }
}


请注意,这声明了游戏的尺寸。这是一个常数。您在许多地方都使用3029被称为使用幻数,这是一件坏事...您需要用dimension替换它们,

确定,这样可以摆脱/ paste board初始值设定项。

Math.random()

要初始化“活动”单元格,请使用:

xcor = Math.floor((Math.random()*29)+0);
ycor = Math.floor((Math.random()*29)+0);


这永远不会产生值29 ...使用Math.random()时经常犯一个错误,就是忘记Math.random()0.0(含)返回到1.0(排他)的值。换句话说,它将永远不会生成1.0,因此也永远不会获得值29。正确的方法是:

xcor = Math.floor(Math.random()*DIMENSION);
ycor = Math.floor(Math.random()*DIMENSION);


好友表

在生命游戏中,每个单元都有一个好友。伙伴细胞是:


在我上方(或者如果我们在顶部,则什么都不是)-在我的左侧,左侧和右侧
(或者什么都不是)如果我们在左边)
我右边(或者如果我们在右边则什么都不是)
在我下面(或者如果在底部,则不显示任何内容)-在左侧,右侧和右侧。 >
这些数组是我们必须添加到(x,y)坐标中以获取周围伙伴的原因。...但是,该表仍无法帮助(但)边界处的单元格。 />
但是,这有一个模数技巧。考虑以下功能:

var BUDDIES = [
      [-1, -1], [0, -1], [1, -1],  // above
      [-1,  0],          [1,  0],  // our row
      [-1,  1], [0,  1], [1,  1]]; // below




由于您的游戏版本不能“自动换行”,请使用此功能: br />
function buddy(dim, pos, direction) {
    return (pos + direction + dim) % dim;
}


此功能获取板子的尺寸,位置(一维)以及我们要寻找伙伴的方式。因此,请考虑我们要查找“左”伙伴的位置:例如,如果x为5,则它将-1和5加在一起。得到4。这是x,并将转换为-1。

现在,您用于邻居检查的所有“重复”代码都可以简化为循环:
function buddy(maxdim, pos, direction) {
    var newpos = pos + direction;
    if (newpos >= maxdim) {
       newpos = -1;
    }
    return newpos;
}


结论

在这三个(.5)建议(板初始化(和尺寸幻数),随机和伙伴)之间,您应该能够减小代码大小大规模。

评论


\ $ \ begingroup \ $
在写回值之前,您不必实际处理整个电路板。有一种方法可以将行的值存储在数组中,并在计算下一行时使用该数组获取旧值。
\ $ \ endgroup \ $
– AJMansfield
2014年1月7日在16:29

\ $ \ begingroup \ $
另外,为什么Math.floor((Math.random()* dimension)+0);中有+0?
\ $ \ endgroup \ $
– AJMansfield
2014年1月7日在16:31

\ $ \ begingroup \ $
@AJMansfield-从OP的代码中复制粘贴。没有其他原因...将进行编辑。
\ $ \ endgroup \ $
–rolfl
2014年1月7日在16:32

\ $ \ begingroup \ $
@DaveJarvis是正确的,不应该这样。 DIMENSION是30,并且由于数组索引是基于0的,所以我们想要的最大值是29。我们希望生成0到29之间的值(包括0和29),因此Math.floor(Math.random()* 30)是想做的事(注意DIMENSION是30,而不是JFiddle中的29)
\ $ \ endgroup \ $
–rolfl
2014年1月7日19:19



\ $ \ begingroup \ $
@ David,Google的Javascript样式指南建议不要使用它(IE中不支持)。
\ $ \ endgroup \ $
–rolfl
2014年1月8日,0:02

#2 楼

除了显而易见的巨大字面量数组之外,我还看到了初始化的几个问题。当您引用ID 121时,是(12,1)还是(1,21)?

名称空间污染(全局变量):x.toString() + y.toString()alivecounterxcorycor不需要在全球范围内。简单的解决方法是使用立即调用的函数表达式(IIFE)来初始化开发板:

var board = (function(dim, min) {
    var b = new Array(dim);
    // TODO: initialize b
    return b;
})(30, 5);


我将更进一步,并将其更改为构造函数(请参见下文) )。


人口不足:您可以使用以下代码生成初始的活细胞:

//number of initial alive cells
var number = Math.floor((Math.random()*900)+5);

/* Some code here which should not have been placed
   in an intervening position */

//generate coordinates for the cells that are alive
for (var i=0; i < number; i++) {
    xcor = Math.floor((Math.random()*29)+0);
    ycor = Math.floor((Math.random()*29)+0);

    board[xcor][ycor] = "x";
}


生命两次进入某些细胞,因此idAssign实际上是最初存活的细胞数的上限,而不是确切的计数。也许您并不在乎确切的计数,因为无论如何它都是随机的,但至少应使“初始活细胞数”的注释更准确。



number作为布尔值:您使用"x"基本上充当布尔值; "x"会更传统。

我将这样写:

function Board(dim, min) {
    var b = new Array(dim);
    for (var i = 0; i < b.length; i++) {
        b[i] = new Array(dim);
    }

    // Give life to a random number of cells
    var initPopulation = min + Math.floor((dim * dim + 1 - min) * Math.random());
    for (var i = 0; i < initPopulation; i++) {
        b[i / dim >> 0][i % dim] = true;
    }
    // Two-dimensional Fisher-Yates shuffle
    for (var i = dim * dim - 1; i > 0; i--) {
        var j = Math.floor((i + 1) * Math.random());
        var swap = b[i / dim >> 0][i % dim]; 
        b[j / dim >> 0][j % dim] = b[i / dim >> 0][i % dim]; 
        b[i / dim >> 0][i % dim] = swap; 
    }
    this.array = b;
}

var board = new Board(30, 5);


#3 楼

为了减少内存使用并提高性能,我建议您考虑使用稀疏矩阵方法。您不必保留整个包含大部分为空的单元格的阵列,而只需保留活动单元格的列表即可。随着单元格状态的改变,您将更新该单元格的显示以及周围单元格的列表。

编辑:我将在有空的时候对其进行更详细的描述。 >要执行此操作,请创建一个单元格对象,该对象具有要表示的每个活动单元格的x,y坐标属性:以下方法:
function cell(x, y) {
    this.x = x;
    this.y = y;
}

要创建新的网格状态,请使用(x,y)测试整个网格空间的每个grid.liveNeighbors(x,y)坐标,并将活动单元格添加到下一个网格对象。
可以进一步优化通过使用现有的网格消除测试中的较大盲区。例如,您可以找出最小和最大x和y值,然后仅测试该区域。
或者可以将boolean liveint liveNeighbors属性添加到单元格对象以跟踪附近liveNeighbors单元格中的dead值,然后当值在寿命阈值内时,将它们实时切换。这将允许您仅遍历网格列表,而不是整个网格空间,从而在许多情况下提供更好的性能。会使其更长一点。


评论


\ $ \ begingroup \ $
这是一个好主意,但是您应该提供一些有关如何在JS中实际实现的更多信息。对于OP来说,这不是显而易见的。
\ $ \ endgroup \ $
–加布
2014年1月7日在16:48

\ $ \ begingroup \ $
您能提供一个小规模的例子吗?你使我的兴趣达到顶峰
\ $ \ endgroup \ $
– SaggingRufus
2014年1月7日18:52

\ $ \ begingroup \ $
“被激怒”。 :)english.stackexchange.com/questions/101450/…
\ $ \ endgroup \ $
–大卫·康拉德(David Conrad)
2014年1月8日,0:04

#4 楼

除了@rolfl建议之外,您还可以创建一个单独的LifeBoard类,并将该数组保留为私有,并具有另一个count数组。然后提供getsetremove方法。只要设置了平方,就在count数组中增加它的邻居,每移除一个,就将它们减少。

评论


\ $ \ begingroup \ $
因为这是JavaScript,所以没有“类”之类的东西。我想您的意思是拥有一个对象,或者可能是多个相似对象的构造函数,而“私有”数据的范围仅限于构造函数或IIFE。值得牢记这些区别,因为我认为许多JS代码变得过于复杂,因为人们试图模仿他们从另一种语言了解的OO,而不是使用JS OO工具来解决实际问题。
\ $ \ endgroup \ $
–IMSoP
2014年1月8日,0:02

\ $ \ begingroup \ $
是的,我不好。这些天我用Java术语看到了一切;)
\ $ \ endgroup \ $
–罗斯·德鲁(Ross Drew)
2014年1月8日在8:48