Игра жизни Конвея в JavaScript

}                 //Левый нижний угол                 if (x == 29) {                     if (board [(x - 1)] [y] == "x") {                         counter ++;                     }                     if (board [(x - 1)] [(y + 1)] == "x") {                         counter ++;                     }                 }                 //Левый верхний угол                 else if (x == 0) {                     if (board [(x + 1)] [y] == "x") {                         counter ++;                     }                     if (board [(x + 1)] [(y + 1)] == "x") {                         counter ++;                     }                 }                 еще {                     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 ++;                     }                 }             }             //обрабатывать правую границу             else if (y == 29) {                 if (board [(x)] [(y - 1)] == "x") {                     counter ++;                 }                 //правый нижний угол                 if (x == 29) {                     if (board [(x - 1)] [y] == "x") {                         counter ++;                     }                     if (board [(x - 1)] [(y - 1)] == "x") {                         counter ++;                     }                 }                 //правый верхний угол                 else if (x == 0) {                     if (board [x + 1] [y] == "x") {                         counter ++;                     }                     if (board [(x + 1)] [(y - 1)] == "x") {                         counter ++;                     }                 }                 еще {                     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 ++;                     }                 }             }             еще {                 //Верхние границы                 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 ++;                     }                 }                 //Нижние границы                 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 ++;                     }                 }                 еще {                     //центр доски                     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 ++;                     }                 }             } //проверка конечной платы             //применяем правила к ячейке             if (alive = true) {                 если (счетчик <2) {                     [x] [y] ="";                 }                 if (counter> 3) {                     [x] [y] = "";                 }             }             else if (alive = false) {                 if (counter == 3) {                     [x] [y] = "x";                 }                 else if (counter! = 3) {                     [x] [y] = "";                 }             }         } //Внутренние FOR     } //Внешнее FOR     var IDcheck;         для (var x = 0; x <30; x ++) {             для (var y = 0; y <30; y ++) {                 IDcheck = x.toString () + y.toString ();                 if (board [x] [y] == "x") {                     document.getElementById (IDcheck) .className = 'active';                 }                 еще {                     document.getElementById (IDcheck) .className = 'NotActive';                 }             }         } }//В ТО ВРЕМЯ КАК

HTML:

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

Причина, по которой у меня есть все пробелы, заданные для массива вручную, заключается в том, что вместо случайного генерирования начальных графиков для бактерий можно было бы прокомментировать этот код и поместить их вручную, чтобы увидеть, какие шаблоны они могут сделать.

43 голоса | спросил SaggingRufus 7 Jpm1000000pmTue, 07 Jan 2014 17:01:22 +040014 2014, 17:01:22

4 ответа


68

Существует серьезная ошибка /проблема, а также составление трех разных рекомендаций.

Bug /вопрос

в Game-Of-Life вы должны сканировать всю доску и только затем применить изменения. Вы применяете изменения частично через процесс (когда вы проверяете каждую ячейку, вы меняете ее состояние). Итак, если вы меняете ячейку в одном месте, когда вы проверяете ее сосед, это повлияет на результаты).

Вам нужно «сохранить» счетчик для каждой ячейки, пока вы не завершили сканирование, а затем повторно установите каждый блок на плате. По существу вам необходимо следующее:

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] = "";
    }
}

Примечание. Это объявляет размер для игры. Это единственная константа. Ваше использование 30 и 29 во многих местах называется с использованием Магических номеров , и это плохо ... Вам нужно заменить те, которые имеют размер dimension

ОК, это избавит вас от инициализатора для копирования /вставки 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);

Использование таблицы друзей

В игровой жизни каждая ячейка имеет приятеля. Клетки-приятели:

  • выше меня (или ничего, если мы наверху) - слева, немедленно и вправо
  • слева от меня (или ничего, если мы слева)
  • право меня (или ничего, если мы находимся справа)
  • ниже меня (или ничего, если мы внизу) - слева, немедленно и вправо.

Мы можем представить этих друзей как:

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

Эти массивы - это то, что мы должны добавить к нашей координате (x,y), чтобы получить окружающих приятелей .... но таблица не помогает (пока) с ячейками в поля.

Но для этого существует трюк по модулю . Рассмотрим функцию:

function buddy(dim, pos, direction) {
    return (pos + direction + dim) % dim;
}

Так как ваша версия игры не «завертывает» на полях, используйте эту функцию вместо:

function buddy(maxdim, pos, direction) {
    var newpos = pos + direction;
    if (newpos >= maxdim) {
       newpos = -1;
    }
    return newpos;
}

Эта функция принимает размер доски, позицию (в одном измерении) и каким образом мы хотим искать нашего приятеля. Итак, рассмотрим, что мы хотим найти позицию нашего «левого» собеседника:

var left = buddy(DIMENSION, x, -1);

Если x равен 5, он добавит -1 и 5 вместе, чтобы получить 4.

Если x равен 0, он вернет -1

Если мы хотим найти приятеля right в позиции 29, математика будет равна: 29 + 1, то есть 30, и это будет переведено на -1 .

Теперь все коды «дубликатов», которые вы имеете для проверок соседа, можно свести к циклу:

# Using the buddies array above
for (var i = 0; i < buddies.length; i++) {
    var budx = buddy(DIMENSION, x, BUDDIES[i][0]);
    var budy = buddy(DIMENSION, y, BUDDIES[i][1]);
    if (budx >= 0 && budy >= 0 && "x" == board[budx][budy]) {
        counter++;
    }
} 

Заключение

Между этими тремя (.5) предложениями (инициализацией платы (и магическим числом измерений), Random и Buddies) вы должны иметь возможность значительно уменьшить размер вашего кода.

ответил rolfl 7 Jpm1000000pmTue, 07 Jan 2014 17:52:36 +040014 2014, 17:52:36
9

Я вижу несколько проблем с инициализацией, кроме очевидного гигантского массива литералов.

  • Неопределенные идентификаторы элементов: Вы объединяете x.toString() + y.toString() для идентификаторов элементов. Когда вы ссылаетесь на ID 121, это (12, 1) или (1, 21)?
  • Загрязнение пространства имен (глобальные переменные): alive, counter, xcor, ycor code> и idAssign не обязательно должны находиться в глобальной области. Простое исправление состоит в том, чтобы инициализировать плату, используя Expression Expression Expression (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";
    }
    

    Вы будете дважды вдыхать жизнь в некоторые клетки, поэтому number - это верхняя граница, а не точное количество числа ячеек, которые изначально были живы. Возможно, вас не интересует точное количество, так как оно случайное в любом случае, но, по крайней мере, комментарий «число начальных живых ячеек» должно быть более точным.

    Чтобы получить точный счет, заполните первые ячейки n , затем сделайте перетасовку.

  • "x" как логическое: . Вы используете "x", чтобы в основном служить логическим значением; true будет более условным.

Вот как я его написал:

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);
ответил 200_success 8 Jam1000000amWed, 08 Jan 2014 01:11:59 +040014 2014, 01:11:59
7

Чтобы уменьшить использование памяти и повысить производительность, я рекомендую вам рассмотреть разреженный матричный подход. Вместо того, чтобы иметь весь массив в основном пустых ячеек, вы можете просто сохранить список живых ячеек. По мере изменения состояния ячейки вы обновляете дисплей для этой ячейки и список для окружающих ячеек.


Изменить: Я опишу его более подробно, поскольку у меня есть время.


Для этого создайте объект ячейки, который имеет атрибуты для координат x, y для каждой живой ячейки, которую вы хотите представить:

function cell(x, y) {
    this.x = x;
    this.y = y;
}

Также создайте объект сетки, который является всего лишь массивом ячеек, и имеет следующие методы:

grid.isLive(x,y) \\ return true if cell(x, y) is found in grid array.
grid.liveNeighbors(x,y) \\ returns number of live neighbors around any grid coordinate

Чтобы создать новое состояние сетки, проверьте каждую координату (x,y) всего пространства сетки с помощью grid.liveNeighbors(x,y) и добавьте живые ячейки к следующему объекту сетки.

Это можно оптимизировать, используя существующую сетку, чтобы исключить большие мертвые зоны из теста. Например, вы можете узнать значения min и max x и y и только проверить эту область.

Или можно добавить к объекту ячейки атрибут boolean live и int liveNeighbors для отслеживания значения liveNeighbors в соседнем dead, затем переключите их в реальном времени, когда значение находится в пределах срока службы. Это позволит вам выполнять только перебор по списку сетки, а не по всему пространству сетки, обеспечивая во многих случаях гораздо лучшую производительность.


Изменить: Я создал короткую, простую примерную версию Python здесь , преобразование в JavaScript сделает его немного дольше.


ответил dansalmo 7 Jpm1000000pmTue, 07 Jan 2014 20:11:54 +040014 2014, 20:11:54
4

В дополнение к предложению @rolfl вы можете создать отдельный класс LifeBoard и сохранить массив приватным и иметь второй массив count. Затем предоставите методы get, set и remove. Когда задан квадрат, увеличивайте его соседи в массиве count, всякий раз, когда вы удаляете, уменьшаете их.

Таким образом, вы сохраняете всегда двойные счетные квадраты при перемещении по доске.

ответил Ross Drew 7 Jpm1000000pmTue, 07 Jan 2014 18:27:47 +040014 2014, 18:27:47

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132