CelAut v3
Материал из Department of Theoretical and Applied Mechanics
Версия от 11:28, 15 мая 2014; Денис (обсуждение | вклад)
Кафедра ТМ > Программирование > Интернет > JavaScript > Клеточный автомат > CelAut v3
Справа вы видите легенду геномов, а также можете выбрать, какие клетки вы хотите "рисовать" мышкой.
Скачать программу: CelAut_v3_release.zip
Текст программы на языке JavaScript (разработчик Цветков Денис): <toggledisplay status=hide showtext="Показать↓" hidetext="Скрыть↑" linkstyle="font-size:default">
Файл "CelAut_v3_release.js"
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; // шанс клетки мутировать при рождении
// *** Задание вычислительных параметров ***
const fps = 5; // frames per second - число кадров в секунду
// Выполнение программы
const w = canvas.width; // ширина окна
const h = canvas.height; // высота окна
const N = 50; // количество клеток по горизонтали (желательно, делитель ширины окна)
const M = 50; // количество клеток по горизонтали (желательно, делитель высоты окна)
const cell_w = w / N; // ширина клетки
const cell_h = h / M; // высота клетки
var pause = true; // остановлен ли клеточный автомат
var interval_ID; // для управления работой автомата
// "Жизнь" Конвея: [B = 000100000, L = 001100000]
const 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
}
Файл "CelAut_v3_release.html"
<!DOCTYPE html>
<html>
<head>
<title>Cellular automaton</title>
<script src="CelAut_v3_release.js"></script>
</head>
<body>
<table>
<tr>
<td><canvas id="canvas_CelAut" width="600" height="600" style="border:1px solid #000000;"></canvas></td>
<td valign="top"><table id="type_table"></table></td>
</tr><tr>
<td><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="Очистить поле"/></td>
</tr>
</table>
<script type="text/javascript">var app = new Main_CelAut(document.getElementById('canvas_CelAut'));</script>
</body>
</html>
</toggledisplay>