CelAut v3

Материал из Department of Theoretical and Applied Mechanics
Версия от 18:52, 8 марта 2015; Wikiadmin (обсуждение | вклад) (Замена текста — «<source lang="(.*)" first-line="(.*)">» на «<syntaxhighlight lang="$1" line start="$2" enclose="div">»)

Перейти к: навигация, поиск
Виртуальная лаборатория > Игра "Жизнь" > Клеточный автомат - версии > CelAut v3

Справа вы видите легенду геномов, а также можете выбрать, какие клетки вы хотите "рисовать" мышкой.

Скачать программу: CelAut_v3_release.zip Текст программы на языке JavaScript (разработчик Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default"> Файл "CelAut_v3_release.js" <syntaxhighlight lang="javascript" line start="1" enclose="div"> function Main_CelAut(canvas) {

   // Предварительные установки
   var context = canvas.getContext("2d");                  // на context происходит рисование
   canvas.oncontextmenu = function() {return false;};      // блокировка контекстного меню
   canvas.onselectstart = function() {return false;};      // запрет выделения canvas
   // *** Задание физических параметров ***
   var mutate_chance = 1 / 50;     // шанс клетки мутировать при рождении
   // *** Задание вычислительных параметров ***
   var fps = 5;             	    // frames per second - число кадров в секунду
   // Выполнение программы
   var w = canvas.width;		    // ширина окна
   var h = canvas.height;		// высота окна
   var N = 50;                   // количество клеток по горизонтали (желательно, делитель ширины окна)
   var M = 50;                   // количество клеток по горизонтали (желательно, делитель высоты окна)
   var cell_w = w / N;           // ширина клетки
   var cell_h = h / M;           // высота клетки
   var pause = true;               // остановлен ли клеточный автомат
   var interval_ID;                // для управления работой автомата
   // "Жизнь" Конвея: [B = 000100000, L = 001100000]
   var B = "001000000";          // геном рождения
   var   L = "000001100";          // геном выживания
   this.set_L = function(n) {      // присваеваем нужный геном (n - положение первой единицы в геноме)
       while (L[0] != '1') L = L.substring(1) + "0";
       for (var i = 0; i < n; i++) L = "0" + L.substring(0, L.length - 1);
   };
   // Работа с мышью
   canvas.onmousedown = function(e){           // функция при нажатии клавиши мыши
       var life;
       if (e.which == 1) life = true;          // при нажатии левой клавиши мыши клетка рождается
       else if (e.which == 3) life = false;    // при нажатии правой клавиши мыши клетка умирает
       else return;
       set_cell(e, life, B, L);
       canvas.onmousemove = function(e) {set_cell(e, life, B, L);};   // функция, выполняющаяся при перемещении курсора мыши
   };
   document.onmouseup = function(e){           // функция при отпускании клавиши мыши
       canvas.onmousemove = null;              // когда клавиша отпущена - функции перемещения нету
   };
   function set_cell(e, is_alive, B, L){       // придать клетке определенное состояние с нажатия клавиши мыши
       var m = mouse_coords(e);                // обновить координаты в переменных mouseX, mouseY
       if (m.x < 0 || m.x >= w || m.y < 0 || m.y >= h) return;         // проверка на ошибочные координаты
       var i = Math.floor(m.x / cell_w);       // получаем ячейку по горизонтали
       var j = Math.floor(m.y / cell_h);       // получаем ячейку по вертикали
       // везде прибавляем 1 - из за периодических условий массив сдвинут на 1
       cells[i + 1][j + 1].alive = is_alive;
       cells[i + 1][j + 1].B = B;
       cells[i + 1][j + 1].L = L;
       draw();
   }
   function mouse_coords(e) {                  // функция возвращает экранные координаты курсора мыши
       var m = [];
       var rect = canvas.getBoundingClientRect();
       m.x = e.clientX - rect.left;
       m.y = e.clientY - rect.top;
       return m;
   }
   // Работа с массивом
   var cells;                                          // массив клеток
   var cells_buf = [];                                 // буфер для расчета следующего шага
   for (var i1 = 0; i1 < N + 2; i1++) {
       cells_buf[i1] = [];
       for (var j1 = 0; j1 < M + 2; j1++) {
           cells_buf[i1][j1] = [];
       }
   }
   function generate_random_field() {              // каждая клетка жива с вероятностью 50%
       cells = [];
       var i, j;
       for (i = 0; i < N + 2; i++) {
           cells[i] = [];
           for (j = 0; j < M + 2; j++) {
               if (i != 0 && i != (N + 1) && j != 0 && j != (M + 1))
                   cells[i][j] = {B:B, L:L, alive:Math.random() >= 0.5};
               else
                   cells[i][j] = [];
           }
       }
   }
   this.clear = function() {                           // очистить поле
       for (var i = 1; i < N + 1; i++)
           for (var j = 1; j < M + 1; j++)
               cells[i][j].alive = false;
       draw();
       stop_system();
   };
   // Управление работой автомата
   function step() {
       tick();
       draw();
   }
   this.change_pause_state = function() {              // кнопка паузы
       if (!pause) stop_system();
       else start_system()
   };
   this.next_step = function(){                        // кнопка "Следующий шаг"
       stop_system();
       step();
   };
   function start_system() {
       pause = false;
       interval_ID = setInterval(step, 1000/fps);
       document.getElementById('pause').value = "Остановить";
   }
   function stop_system() {
       pause = true;
       clearInterval(interval_ID);
       document.getElementById('pause').value = "Запустить";
   }
   // Расчетная часть программы
   function tick() {                            // то, что происходит каждый шаг времени
       // периодичность - копируем боковые клетки
       for (i = 1; i < N + 1; i++) {
           cells[i][0].B = cells[i][M].B;      cells[i][0].L = cells[i][M].L;      cells[i][0].alive = cells[i][M].alive;
           cells[i][M + 1].B = cells[i][1].B;  cells[i][M + 1].L = cells[i][1].L;  cells[i][M + 1].alive = cells[i][1].alive;
       }
       for (j = 0; j < M + 2; j++) {
           cells[0][j].B = cells[N][j].B;      cells[0][j].L = cells[N][j].L;      cells[0][j].alive = cells[N][j].alive;
           cells[N + 1][j].B = cells[1][j].B;  cells[N + 1][j].L = cells[1][j].L;  cells[N + 1][j].alive = cells[1][j].alive;
       }
       // цикл по всем клеткам
       for (var i = 1; i < N + 1; i++) {
           for (var j = 1; j < M + 1; j++) {
               // подсчет количества живых клеток вокруг рассматриваемой клетки
               var near = [];
               var cell;
               // сначала содержимое cells[][] присваевается переменной cell
               // потом проверяется cell.alive, и если оно true - клетка добавляется в список живых клеток
               if ((cell = cells[i - 1]    [j - 1] ).alive)   near.push(cell);
               if ((cell = cells[i - 1]    [j]     ).alive)   near.push(cell);
               if ((cell = cells[i - 1]    [j + 1] ).alive)   near.push(cell);
               if ((cell = cells[i]        [j - 1] ).alive)   near.push(cell);
               if ((cell = cells[i]        [j + 1] ).alive)   near.push(cell);
               if ((cell = cells[i + 1]    [j - 1] ).alive)   near.push(cell);
               if ((cell = cells[i + 1]    [j]     ).alive)   near.push(cell);
               if ((cell = cells[i + 1]    [j + 1] ).alive)   near.push(cell);
               if (cells[i][j].alive) {                            // рассматриваемая клетка жива
                   if (cells[i][j].L[near.length] == '1') {        // проверка условия выживания по биному L
                       cells_buf[i][j].alive = true;
                       cells_buf[i][j].B = cells[i][j].B;
                       cells_buf[i][j].L = cells[i][j].L;
                   } else {
                       cells_buf[i][j].alive = false;
                   }
               } else {                                            // рассматриваемая клетка мертва
                   //выясним, сколько видов удовлетворяют условиям рождения новой клетки
                   var good_types = [];
                   for (var k = 0; k < near.length; k++) {
                       if (near[k].B[near.length] != "1") continue;
                       var bad_type = false;
                       for (var gt = 0; gt < good_types.length; gt++) {
                           if (good_types[gt].B == near[k].B && good_types[gt].L == near[k].L) bad_type = true;
                       }
                       if (!bad_type) good_types.push(near[k]);
                   }
                   if (good_types.length > 0) {                    // видов, условия которых удовлетворены - больше 0
                       // если нужных видов больше одного - выбираем случайно
                       var type = good_types[Math.floor(Math.random() * good_types.length)];
                       // здесь происходит мутация
                       if (Math.random() < mutate_chance) cells_buf[i][j].L = genome_shift(type.L);
                       else cells_buf[i][j].L = type.L;
                       cells_buf[i][j].B = type.B;
                       cells_buf[i][j].alive = true;
                   } else {
                       cells_buf[i][j].alive = false;
                   }
               }
           }
       }
       // копирование посчитанных значений следующего шага (cells_buf) в cells
       for (var i0 = 1; i0 < N + 1; i0++) {
           for (var j0 = 1; j0 < M + 1; j0++) {
               cells[i0][j0].B = cells_buf[i0][j0].B;
               cells[i0][j0].L = cells_buf[i0][j0].L;
               cells[i0][j0].alive = cells_buf[i0][j0].alive;
           }
       }
   }
   // функция сдвига. Принимает строку вида 001110000 (количество единиц любое), и случайно сдвигает единицы влево/вправо
   // Пример_1: 01100 -> 00110 или 01100 -> 11000
   // Пример_2: 00001 -> 00010 (в другую сторону сдвига быть не может)
   function genome_shift(genome) {
       var new_genome = genome;
       // если первый или последний элемент генома - единичка, сдвиг может быть произведен только в одну сторону с шансом 50%
       if (Math.random() >= 0.5) {
           if (genome[0] != '1') new_genome = genome.substring(1) + "0";
       } else {
           if (genome[genome.length - 1] != '1') new_genome = "0" + genome.substring(0, genome.length - 1)
       }
       return new_genome;
   }
   // Рисование
   var colors = ["#ff0000", "#ffaa00", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#7f00ff", "#ff40ff", "#7f0000"];
   function draw(){
       context.fillStyle = "#000000";              // цвет клетки
       context.fillRect(0, 0, w, h);               // очистить экран
       for (var i = 1; i < N + 1; i++) {
           for (var j = 1; j < M + 1; j++) {
               if (cells[i][j].alive) {
                   context.fillStyle = colors[cells[i][j].L.indexOf("1")];  // цвет клетки
                   context.beginPath();
                   context.rect((i - 1) * cell_w, (j - 1) * cell_h, cell_w, cell_h);
                   context.closePath();
                   context.fill();
               }
           }
       }
   }
   // Интерфейс
   // запишем все возможные состояния генома L (все возможные мутации)
   var L_test = L;
   while (L_test[0] != '1') L_test = L_test.substring(1) + "0";        // сдвинем единицы в геноме максимально влево
   var variants = [L_test];
   while (L_test[L_test.length - 1] != '1') {                          // теперь сдвигаем единицы вправо и запоминаем вариант
       L_test = "0" + L_test.substring(0, L_test.length - 1);
       variants.push(L_test);
   }
   // нарисуем таблицу геномов
   for (var i0 = 0; i0 < variants.length; i0++) {
       var tr = document.createElement('tr');
       tr.innerHTML =
           "<td><input type='radio' id='radio_" + i0 +"' name='colorTable' onclick='app.set_L(" + i0 + ")'/></td>" +
           "<td style='width:30px; height:30px; background-color:" + colors[i0] + "; border: 1px solid black;'></td>" +
           "<td>" + variants[i0] +  "</td>";
       document.getElementById("type_table").appendChild(tr);
   }
   // Запуск системы
   generate_random_field();                    // сгенероровать поле
   start_system();
   document.getElementById("radio_0").checked = true;
   this.set_L(0);                              // после запуска системы, т.к. изменяет переменную L

} </source> Файл "CelAut_v3_release.html" <syntaxhighlight lang="html" line start="1" enclose="div"> <!DOCTYPE html> <html> <head>

   <title>Cellular automaton</title>
   <script src="CelAut_v3_release.js"></script>

</head> <body>

<canvas id="canvas_CelAut" width="600" height="600" style="border:1px solid #000000;"></canvas>
<input id="pause" type="button" name="" style="width: 100px" onclick="app.change_pause_state();return false;"/>
           <input type="button" name="" onclick="app.next_step();return false;" value="Следующий шаг"/>
<input type="button" name="" onclick="app.clear();return false;" value="Очистить поле"/>
   <script type="text/javascript">var app = new Main_CelAut(document.getElementById('canvas_CelAut'));</script>

</body> </html> </source> </toggledisplay>