CelAut v3

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

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

Скачать программу: CelAut_v3_release.zip

Текст программы на языке JavaScript (разработчик Цветков Денис):

Файл "CelAut_v3_release.js"

  1 function Main_CelAut(canvas) {
  2 
  3     // Предварительные установки
  4 
  5     var context = canvas.getContext("2d");                  // на context происходит рисование
  6     canvas.oncontextmenu = function() {return false;};      // блокировка контекстного меню
  7     canvas.onselectstart = function() {return false;};      // запрет выделения canvas
  8 
  9     // *** Задание физических параметров ***
 10 
 11     var mutate_chance = 1 / 50;     // шанс клетки мутировать при рождении
 12 
 13     // *** Задание вычислительных параметров ***
 14 
 15     var fps = 5;             	    // frames per second - число кадров в секунду
 16 
 17     // Выполнение программы
 18 
 19     var w = canvas.width;		    // ширина окна
 20     var h = canvas.height;		// высота окна
 21     var N = 50;                   // количество клеток по горизонтали (желательно, делитель ширины окна)
 22     var M = 50;                   // количество клеток по горизонтали (желательно, делитель высоты окна)
 23     var cell_w = w / N;           // ширина клетки
 24     var cell_h = h / M;           // высота клетки
 25 
 26     var pause = true;               // остановлен ли клеточный автомат
 27     var interval_ID;                // для управления работой автомата
 28 
 29     // "Жизнь" Конвея: [B = 000100000, L = 001100000]
 30     var B = "001000000";          // геном рождения
 31     var   L = "000001100";          // геном выживания
 32 
 33     this.set_L = function(n) {      // присваеваем нужный геном (n - положение первой единицы в геноме)
 34         while (L[0] != '1') L = L.substring(1) + "0";
 35         for (var i = 0; i < n; i++) L = "0" + L.substring(0, L.length - 1);
 36     };
 37 
 38     // Работа с мышью
 39 
 40     canvas.onmousedown = function(e){           // функция при нажатии клавиши мыши
 41         var life;
 42         if (e.which == 1) life = true;          // при нажатии левой клавиши мыши клетка рождается
 43         else if (e.which == 3) life = false;    // при нажатии правой клавиши мыши клетка умирает
 44         else return;
 45 
 46         set_cell(e, life, B, L);
 47         canvas.onmousemove = function(e) {set_cell(e, life, B, L);};   // функция, выполняющаяся при перемещении курсора мыши
 48     };
 49 
 50     document.onmouseup = function(e){           // функция при отпускании клавиши мыши
 51         canvas.onmousemove = null;              // когда клавиша отпущена - функции перемещения нету
 52     };
 53 
 54     function set_cell(e, is_alive, B, L){       // придать клетке определенное состояние с нажатия клавиши мыши
 55         var m = mouse_coords(e);                // обновить координаты в переменных mouseX, mouseY
 56         if (m.x < 0 || m.x >= w || m.y < 0 || m.y >= h) return;         // проверка на ошибочные координаты
 57         var i = Math.floor(m.x / cell_w);       // получаем ячейку по горизонтали
 58         var j = Math.floor(m.y / cell_h);       // получаем ячейку по вертикали
 59         // везде прибавляем 1 - из за периодических условий массив сдвинут на 1
 60         cells[i + 1][j + 1].alive = is_alive;
 61         cells[i + 1][j + 1].B = B;
 62         cells[i + 1][j + 1].L = L;
 63         draw();
 64     }
 65 
 66     function mouse_coords(e) {                  // функция возвращает экранные координаты курсора мыши
 67         var m = [];
 68         var rect = canvas.getBoundingClientRect();
 69         m.x = e.clientX - rect.left;
 70         m.y = e.clientY - rect.top;
 71         return m;
 72     }
 73 
 74     // Работа с массивом
 75 
 76     var cells;                                          // массив клеток
 77     var cells_buf = [];                                 // буфер для расчета следующего шага
 78     for (var i1 = 0; i1 < N + 2; i1++) {
 79         cells_buf[i1] = [];
 80         for (var j1 = 0; j1 < M + 2; j1++) {
 81             cells_buf[i1][j1] = [];
 82         }
 83     }
 84     function generate_random_field() {              // каждая клетка жива с вероятностью 50%
 85         cells = [];
 86         var i, j;
 87         for (i = 0; i < N + 2; i++) {
 88             cells[i] = [];
 89             for (j = 0; j < M + 2; j++) {
 90                 if (i != 0 && i != (N + 1) && j != 0 && j != (M + 1))
 91                     cells[i][j] = {B:B, L:L, alive:Math.random() >= 0.5};
 92                 else
 93                     cells[i][j] = [];
 94             }
 95         }
 96     }
 97 
 98     this.clear = function() {                           // очистить поле
 99         for (var i = 1; i < N + 1; i++)
100             for (var j = 1; j < M + 1; j++)
101                 cells[i][j].alive = false;
102         draw();
103         stop_system();
104     };
105 
106     // Управление работой автомата
107 
108     function step() {
109         tick();
110         draw();
111     }
112 
113     this.change_pause_state = function() {              // кнопка паузы
114         if (!pause) stop_system();
115         else start_system()
116     };
117 
118     this.next_step = function(){                        // кнопка "Следующий шаг"
119         stop_system();
120         step();
121     };
122 
123     function start_system() {
124         pause = false;
125         interval_ID = setInterval(step, 1000/fps);
126         document.getElementById('pause').value = "Остановить";
127     }
128 
129     function stop_system() {
130         pause = true;
131         clearInterval(interval_ID);
132         document.getElementById('pause').value = "Запустить";
133     }
134 
135     // Расчетная часть программы
136 
137     function tick() {                            // то, что происходит каждый шаг времени
138 
139         // периодичность - копируем боковые клетки
140         for (i = 1; i < N + 1; i++) {
141             cells[i][0].B = cells[i][M].B;      cells[i][0].L = cells[i][M].L;      cells[i][0].alive = cells[i][M].alive;
142             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;
143         }
144         for (j = 0; j < M + 2; j++) {
145             cells[0][j].B = cells[N][j].B;      cells[0][j].L = cells[N][j].L;      cells[0][j].alive = cells[N][j].alive;
146             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;
147         }
148 
149         // цикл по всем клеткам
150         for (var i = 1; i < N + 1; i++) {
151             for (var j = 1; j < M + 1; j++) {
152                 // подсчет количества живых клеток вокруг рассматриваемой клетки
153                 var near = [];
154                 var cell;
155                 // сначала содержимое cells[][] присваевается переменной cell
156                 // потом проверяется cell.alive, и если оно true - клетка добавляется в список живых клеток
157                 if ((cell = cells[i - 1]    [j - 1] ).alive)   near.push(cell);
158                 if ((cell = cells[i - 1]    [j]     ).alive)   near.push(cell);
159                 if ((cell = cells[i - 1]    [j + 1] ).alive)   near.push(cell);
160                 if ((cell = cells[i]        [j - 1] ).alive)   near.push(cell);
161                 if ((cell = cells[i]        [j + 1] ).alive)   near.push(cell);
162                 if ((cell = cells[i + 1]    [j - 1] ).alive)   near.push(cell);
163                 if ((cell = cells[i + 1]    [j]     ).alive)   near.push(cell);
164                 if ((cell = cells[i + 1]    [j + 1] ).alive)   near.push(cell);
165 
166                 if (cells[i][j].alive) {                            // рассматриваемая клетка жива
167                     if (cells[i][j].L[near.length] == '1') {        // проверка условия выживания по биному L
168                         cells_buf[i][j].alive = true;
169                         cells_buf[i][j].B = cells[i][j].B;
170                         cells_buf[i][j].L = cells[i][j].L;
171                     } else {
172                         cells_buf[i][j].alive = false;
173                     }
174                 } else {                                            // рассматриваемая клетка мертва
175                     //выясним, сколько видов удовлетворяют условиям рождения новой клетки
176                     var good_types = [];
177                     for (var k = 0; k < near.length; k++) {
178                         if (near[k].B[near.length] != "1") continue;
179                         var bad_type = false;
180                         for (var gt = 0; gt < good_types.length; gt++) {
181                             if (good_types[gt].B == near[k].B && good_types[gt].L == near[k].L) bad_type = true;
182                         }
183                         if (!bad_type) good_types.push(near[k]);
184                     }
185 
186                     if (good_types.length > 0) {                    // видов, условия которых удовлетворены - больше 0
187                         // если нужных видов больше одного - выбираем случайно
188                         var type = good_types[Math.floor(Math.random() * good_types.length)];
189 
190                         // здесь происходит мутация
191                         if (Math.random() < mutate_chance) cells_buf[i][j].L = genome_shift(type.L);
192                         else cells_buf[i][j].L = type.L;
193                         cells_buf[i][j].B = type.B;
194                         cells_buf[i][j].alive = true;
195                     } else {
196                         cells_buf[i][j].alive = false;
197                     }
198                 }
199             }
200         }
201 
202         // копирование посчитанных значений следующего шага (cells_buf) в cells
203         for (var i0 = 1; i0 < N + 1; i0++) {
204             for (var j0 = 1; j0 < M + 1; j0++) {
205                 cells[i0][j0].B = cells_buf[i0][j0].B;
206                 cells[i0][j0].L = cells_buf[i0][j0].L;
207                 cells[i0][j0].alive = cells_buf[i0][j0].alive;
208             }
209         }
210     }
211 
212     // функция сдвига. Принимает строку вида 001110000 (количество единиц любое), и случайно сдвигает единицы влево/вправо
213     // Пример_1: 01100 -> 00110 или 01100 -> 11000
214     // Пример_2: 00001 -> 00010 (в другую сторону сдвига быть не может)
215     function genome_shift(genome) {
216         var new_genome = genome;
217         // если первый или последний элемент генома - единичка, сдвиг может быть произведен только в одну сторону с шансом 50%
218         if (Math.random() >= 0.5) {
219             if (genome[0] != '1') new_genome = genome.substring(1) + "0";
220         } else {
221             if (genome[genome.length - 1] != '1') new_genome = "0" + genome.substring(0, genome.length - 1)
222         }
223         return new_genome;
224     }
225 
226     // Рисование
227 
228     var colors = ["#ff0000", "#ffaa00", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#7f00ff", "#ff40ff", "#7f0000"];
229 
230     function draw(){
231         context.fillStyle = "#000000";              // цвет клетки
232         context.fillRect(0, 0, w, h);               // очистить экран
233         for (var i = 1; i < N + 1; i++) {
234             for (var j = 1; j < M + 1; j++) {
235                 if (cells[i][j].alive) {
236                     context.fillStyle = colors[cells[i][j].L.indexOf("1")];  // цвет клетки
237                     context.beginPath();
238                     context.rect((i - 1) * cell_w, (j - 1) * cell_h, cell_w, cell_h);
239                     context.closePath();
240                     context.fill();
241                 }
242             }
243         }
244     }
245 
246     // Интерфейс
247 
248     // запишем все возможные состояния генома L (все возможные мутации)
249     var L_test = L;
250     while (L_test[0] != '1') L_test = L_test.substring(1) + "0";        // сдвинем единицы в геноме максимально влево
251     var variants = [L_test];
252     while (L_test[L_test.length - 1] != '1') {                          // теперь сдвигаем единицы вправо и запоминаем вариант
253         L_test = "0" + L_test.substring(0, L_test.length - 1);
254         variants.push(L_test);
255     }
256 
257     // нарисуем таблицу геномов
258     for (var i0 = 0; i0 < variants.length; i0++) {
259         var tr = document.createElement('tr');
260         tr.innerHTML =
261             "<td><input type='radio' id='radio_" + i0 +"' name='colorTable' onclick='app.set_L(" + i0 + ")'/></td>" +
262             "<td style='width:30px; height:30px; background-color:" + colors[i0] + "; border: 1px solid black;'></td>" +
263             "<td>" + variants[i0] +  "</td>";
264         document.getElementById("type_table").appendChild(tr);
265     }
266 
267     // Запуск системы
268 
269     generate_random_field();                    // сгенероровать поле
270     start_system();
271 
272     document.getElementById("radio_0").checked = true;
273     this.set_L(0);                              // после запуска системы, т.к. изменяет переменную L
274 }

Файл "CelAut_v3_release.html"

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title>Cellular automaton</title>
 5     <script src="CelAut_v3_release.js"></script>
 6 </head>
 7 <body>
 8     <table>
 9         <tr>
10             <td><canvas id="canvas_CelAut" width="600" height="600" style="border:1px solid #000000;"></canvas></td>
11             <td valign="top"><table id="type_table"></table></td>
12         </tr><tr>
13             <td><input id="pause" type="button" name="" style="width: 100px" onclick="app.change_pause_state();return false;"/>
14             <input type="button" name="" onclick="app.next_step();return false;" value="Следующий шаг"/>
15             <input type="button" name="" onclick="app.clear();return false;" value="Очистить поле"/></td>
16         </tr>
17     </table>
18 
19     <script type="text/javascript">var app = new Main_CelAut(document.getElementById('canvas_CelAut'));</script>
20 </body>
21 </html>