CelAut v3 iFrame
Материал из Department of Theoretical and Applied Mechanics
Кафедра ТМ > Программирование > Интернет > JavaScript > Клеточный автомат > CelAut v3 iFrame
Справа вы видите легенду геномов, а также можете выбрать, какие клетки вы хотите "рисовать" мышкой.
Скачать программу: CelAut_v3_release_iFrame.zip
Текст программы на языке JavaScript (разработчик Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default">
Файл "CelAut_v3_release_iFrame.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 const fps = 5; // frames per second - число кадров в секунду
16
17 // Выполнение программы
18
19 const w = canvas.width; // ширина окна
20 const h = canvas.height; // высота окна
21 const N = 50; // количество клеток по горизонтали (желательно, делитель ширины окна)
22 const M = 50; // количество клеток по горизонтали (желательно, делитель высоты окна)
23 const cell_w = w / N; // ширина клетки
24 const cell_h = h / M; // высота клетки
25
26 var pause = true; // остановлен ли клеточный автомат
27 var interval_ID; // для управления работой автомата
28
29 // "Жизнь" Конвея: [B = 000100000, L = 001100000]
30 const 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_iFrame.html"
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>Cellular automaton</title>
5 <script src="CelAut_v3_release_iFrame.js"></script>
6 </head>
7 <body>
8 <table>
9 <tr>
10 <td id="canvas_conteiner">
11 <!--<canvas id="canvas_CelAut" width="600" height="600" style="border:1px solid #000000;"></canvas>-->
12 <script>canvas_conteiner.innerHTML = "<canvas id='canvas_CelAut' width='" +
13 (document.documentElement.clientWidth - 200) +"' height='" +
14 (document.documentElement.clientHeight - 60) +"' style='border:1px solid #000000;'></canvas>"</script>
15 </td>
16 <td valign="top"><table id="type_table"></table></td>
17 </tr><tr>
18 <td><input id="pause" type="button" name="" style="width: 100px" onclick="app.change_pause_state();return false;"/>
19 <input type="button" name="" onclick="app.next_step();return false;" value="Следующий шаг"/>
20 <input type="button" name="" onclick="app.clear();return false;" value="Очистить поле"/></td>
21 </tr>
22 </table>
23
24 <script type="text/javascript">var app = new Main_CelAut(document.getElementById('canvas_CelAut'));</script>
25 </body>
26 </html>
</toggledisplay>