Игра Жизнь

Об игре


Игра «Жизнь» (англ. Conway's Game of Life) — это игра, не требующая участия других игроков и работающая без постороннего вмешательства исходя только из начальной конфигурации. Игра придумана английским математиком Джоном Конвеем в 1970 году.

Основная идея игры состоит в том, чтобы, начав с какого-нибудь простого расположения живых клеток, проследить за эволюцией исходной позиции под действием ГЕНЕТИЧЕСКИХ ЗАКОНОВ КОНВЕЯ, которые управляют рождением, гибелью и выживанием клеток.

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

Правила


Действие игры происходит на некой плоскости, разделенной на клетки.

Поле ограничено: 30х30 клеток. Верхняя граница поля «соединена» с нижней, а левая граница — с правой, что представляет собой эмуляцию поверхности тора, но на экране поле отображается в виде равномерной сетки.

Каждая клетка окружена 8 такими же клетками.

Каждая клетка может быть живой или мёртвой.

Поколения сменяются синхронно по простым правилам:

  1. РОЖДЕНИЕ. В пустой клетке появляется живая клетка, если у исходной клетки ровно 3 соседа.
  2. ВЫЖИВАНИЕ. Если у живой клетки есть 2 или 3 живые соседки, то эта клетка продолжает жить.
  3. ГИБЕЛЬ. Если соседей меньше 2 или больше 3, клетка умирает («от одиночества» или «от перенаселённости»).

Гибель и рождение всех организмов происходит одновременно.

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

О многопоточности в JS


Яваскрипт - однопоточный язык программирования.

В JavaScript есть многопоточность (либо подобие), которое реализовано посредством инструмента Web Workers.

Спецификация объектов Web Worker определяет API для создания фоновых скриптов в веб-приложениях. Эти объекты позволяют запускать долговременные скрипты для выполнения задач, требующих большого объема вычислений, не прибегая к блокировке интерфейса пользователя или других скриптов, управляющих взаимодействием пользователя с системой.

Параллельное выполнение кода возможно с помощью таких средств, как методы setTimeout() и setInterval(), технология XMLHttpRequest, а также обработчики событий. Все эти функции выполняются асинхронно.

Играть


На поле 30x30 уже хаотично расставлены клетки. Нажмите кнопку "Запустить", чтобы начать игру.




Код


Игра написана для браузеров на JavaScript, который внедрен в html5. Визуализация создана на canvas. (Canvas (англ. canvas — «холст», рус. канва́с) — элемент HTML5, предназначенный для создания растрового двухмерного изображения при помощи скриптов, обычно на языке JavaScript) Игра использует самые передовые технологии и подходит для современных браузеров: Chrome, FF, Opera, IE9, Safari.

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


<script>
//colors
var arrCol = new Array( "#000066", "#9999ff" );
//create table with cells
function createCells( arr, m, n, widthCell, border )
{
 var i, j, k, val, col;
 if( "" + widthCell == "undefined" ) widthCell = 10;
 if( "" + border == "undefined" ) border = 1;
 document.writeln( "<table style='border: 4px solid #fff; table-layout:fixed;' cellpadding='0' cellspacing='0' border='" + border + "'>" );
 
 k = 0;
 for( i = 0; i < m; i++ )
 {
  document.writeln( "<tr height='" + widthCell + "'>" );
  for( j = 0; j < n; j++ )
  {
   val = arr[ k++ ];
   col = ( val ? arrCol[ 1 ] : arrCol[ 0 ] );
   
   document.writeln( "<td width='" + widthCell + " height='" + widthCell + "' id='c" + i + "_" + j + "'" +
    " style='background-color:" + col + ";'><img src='point.png' width='0' height='0'></td>" );
  }
  document.writeln( "</tr>" );
 }
 
 document.writeln( "</table>" );
}
//randomly init cells and update table
function Init( bUpdate )
{
 var i;
 for( i = 0; i < sz; i++ ) arr[ i ] = 0;
 
 for( i = 0; i < nInit; i++ )
 {
  arr[ Math.floor( sz * Math.random() ) ] = 1;
 }
 
 if( bUpdate ) updateCells();
}
//add one more alive cell
function addLive()
{
 var k = Math.floor( sz * Math.random() );
 var i = Math.floor( k / n ); //VI
 var j = k % n; 
 var s = "c" + i + "_" + j;
 var obj = document.getElementById( s );
 if( obj )
 {
  obj.style.backgroundColor = arrCol[ 1 ];
  arr[ k ] = 1;
 }
 return false;
}
//update
function updateCells()
{
 var k;
 for( k = 0; k < sz; k++ )
 {
  if( arrObj[ k ] )
  {
   arrObj[ k ].style.backgroundColor = arr[ k ] ? arrCol[ 1 ] : arrCol[ 0 ];
   if( iDebug ) arrObj[ k ].innerText = arrNeighb[ k ];
  }
 }
 return false;
}
//new iteration
function Iterate()
{
 var i;
 //calculate neighboors
 for( i = 0; i < sz; i++ ) arrNeighb[ i ] = 0;
 
 for( i = 0; i < sz; i++ )
 { //check all 8 neighboors
  if( i % n == 0 )
  { //left border
  }
  else
  { //not left
   if( arr[ i - 1 ] == 1 ) arrNeighb[ i ]++;
   if( i - 1 - n >= 0 && arr[ i - 1 - n ] == 1 ) arrNeighb[ i ]++;
   if( i - 1 + n < sz && arr[ i - 1 + n ] == 1 ) arrNeighb[ i ]++;
  }
  if( i % n == n - 1 )
  { //right border
  }
  else
  { //not right
   if( arr[ i + 1 ] == 1 ) arrNeighb[ i ]++;
   if( i + 1 - n >= 0 && arr[ i + 1 - n ] == 1 ) arrNeighb[ i ]++;
   if( i + 1 + n < sz && arr[ i + 1 + n ] == 1 ) arrNeighb[ i ]++;
  }
  //top
  if( i - n >= 0 && arr[ i - n ] == 1 ) arrNeighb[ i ]++;
  //bottom
  if( i + n >= 0 && arr[ i + n ] == 1 ) arrNeighb[ i ]++;  
 }
 
 //new generation
 for( i = 0; i < sz; i++ )
 {
  if( arr[ i ] == 0 && arrNeighb[ i ] == 3 )
  { //new alive
   arr[ i ] = 1;
  }
  else if( arr[ i ] == 1 && ( arrNeighb[ i ] == 2 || arrNeighb[ i ] == 3 ) )
  { //keep alive
  }
  else
  { //die
   arr[ i ] = 0;
  }
 }
 
 //update table
 updateCells();
 return false;
}
//start loop
function Start()
{
 cycle = 1;
 Next();
 return false;
}
function Next()
{
 if( cycle == 1 )
 {
  Iterate();
  window.setTimeout( "Next()", 50 );
 }
 return false;
}
//stop loop
function Stop()
{
 cycle = 0;
 return false;
}

var i, j, k, s;
var m = 30; //rows
var n = 30; //columns
var widthCell = 6; //cell width
var border = 0;  //border width
var nInit = 500; //initially alive
var sz = m * n;  //table size
var arr = new Array( sz );   //cell state
var arrNeighb = new Array( sz ); //number of neighboors
var arrObj = new Array( sz );  //reference by ID
var cycle = 0; //loop
var iDebug = 0;

Init( false );

createCells( arr, m, n, widthCell, border );

//assign objects
i = 0;
j = 0;
for( k = 0; k < sz; k++ )
{
 s = "c" + i + "_" + j;
 j++;
 if( j == n )
 { //next row
  j = 0;
  i = i + 1;
 }
 obj = document.getElementById( s );
 arrObj[ k ] = obj;
}

</script>
<input type="button" value="Инициализация" onclick="Init( true )">
<input type="button" value="Добавить клетку" onclick="addLive()">
<input type="button" value="1 итерация" onclick="Iterate()">
<input type="button" value="Запустить" onclick="Start()">
<input type="button" value="Остановить" onclick="Stop()">