No es mi primera ni segunda implementación de un autómata celular, he implementado este hormiguero en varios lenguajes (en JAVA, en Objective-C para iOS, …) pero dado que estaba repasando jQuery, me decidí a implementarlo para web usando sólo HTML, CSS y jQuery.
En esta primera entrega implementaré una versión básica del hormiguero, dónde las hormigas (puntos azules) se mueven sin chocarse con otras hormigas y con las larvas (puntos blancos).
Como podéis observar el mundo del hormiguero es toroide, es decir cuándo una hormiga sale por la izquierda reaparece por la derecha y viceversa, lo mismo ocurre hacia arriba y hacia abajo.
Código HTML:
[html]
< !doctype html>
[/html]
Código CSS:
[css]
.anthill {
background-color: black;
position: relative;
}
.cell {
position: absolute;
}
.ant {
background-color: blue;
}
.grub {
background-color: white;
}
[/css]
Código JS:
[javascript]
var Cell = {
Empty: 0,
Wall: 1,
Ant: 2,
Grub: 3
}
var anthill = {};
// Return a free position in the array.
function FreePosition(cells) {
var position = {x: 0, y: 0};
do {
position.y = Math.floor(Math.random() * cells.length);
position.x = Math.floor(Math.random() * cells[position.y].length);
} while (cells[position.y][position.x] != Cell.Empty);
return position;
}
// Fill an array of elements.
function FillCells(countOfElements, anthillDiv, cells, elements, cellType, cellClass) {
for (var i = 0; i < countOfElements; i++) {
var position = FreePosition(cells);
var element = $(document.createElement("div"));
element.addClass("cell " + cellClass);
element.css({"width": ("" + anthill.cell + "px"),
"height": ("" + anthill.cell + "px"),
"left": ("" + (position.x * anthill.cell) + "px"),
"top": ("" + (position.y * anthill.cell) + "px")});
anthillDiv.append(element);
elements[i] = {position: position, element: element};
cells[position.y][position.x] = cellType;
}
}
function ChangeDirection(ant) {
do {
ant.incX = Math.floor(Math.random() * 3) - 1;
ant.incY = Math.floor(Math.random() * 3) - 1;
} while ((ant.incX == 0) && (ant.incY == 0));
}
function ChangePosition(ant, anthill) {
var newPosition = {x: ant.position.x + ant.incX,
y: ant.position.y + ant.incY};
if (newPosition.x < 0) {
newPosition.x = anthill.width - 1;
} else if (newPosition.x >= anthill.width) {
newPosition.x = 0;
}
if (newPosition.y < 0) {
newPosition.y = anthill.height - 1;
} else if (newPosition.y >= anthill.height) {
newPosition.y = 0;
}
if (anthill.cells[newPosition.y][newPosition.x] == Cell.Empty) {
anthill.cells[ant.position.y][ant.position.x] = Cell.Empty;
anthill.cells[newPosition.y][newPosition.x] = Cell.Ant;
ant.position = newPosition;
ant.element.css({“left”: (“” + (newPosition.x * anthill.cell) + “px”),
“top”: (“” + (newPosition.y * anthill.cell) + “px”)});
} else {
ChangeDirection(ant);
}
}
$(document).ready(function() {
$(“.anthill”).each(function() {
// Get the DIV element.
anthill.div = $(this);
// Get DIV attributes.
anthill.width = anthill.div.attr(“width”) || 50;
anthill.height = anthill.div.attr(“height”) || 30;
anthill.cell = anthill.div.attr(“cell”) || 15;
// Protect DIV attribute values.
anthill.width = (anthill.width < 10) ? 10 : anthill.width;
anthill.width = (anthill.width > 100) ? 100 : anthill.width;
anthill.height = (anthill.height < 10) ? 10 : anthill.height;
anthill.height = (anthill.height > 100) ? 100 : anthill.height;
anthill.cell = (anthill.cell < 1) ? 1 : anthill.cell;
anthill.cell = (anthill.cell > 100) ? 100 : anthill.cell;
// Set DIV dimensions.
anthill.div.css({“width”: (“” + (anthill.width * anthill.cell) + “px”),
“height”: (“” + (anthill.height * anthill.cell) + “px”)});
// Fill the cells array.
anthill.cells = [];
for (var y = 0; y < anthill.height; y++) {
anthill.cells[y] = [];
for (var x = 0; x < anthill.width; x++) {
anthill.cells[y][x] = Cell.Empty;
}
}
// Calculate how many cell types we should generate.
var countOfAnts = (anthill.width * anthill.height) * 0.01;
var countOfGrubs = (anthill.width * anthill.height) * 0.1;
// Fill the ants array.
anthill.ants = [];
FillCells(countOfAnts, anthill.div, anthill.cells, anthill.ants, Cell.Ant, "ant");
for (var i = 0; i < anthill.ants.length; i++) {
ChangeDirection(anthill.ants[i]);
}
// Fill the grubs array.
anthill.grubs = [];
FillCells(countOfGrubs, anthill.div, anthill.cells, anthill.grubs, Cell.Grub, "grub");
});
});
window.setInterval(function() {
for (var i = 0; i < anthill.ants.length; i++) {
var ant = anthill.ants[i];
if (Math.floor(Math.random() * 20) == 0) {
ChangeDirection(ant);
}
ChangePosition(ant, anthill);
}
}, 100);
[/javascript]
Os dejo los enlaces a los ficheros con el código fuente:
- anthill-1.html
- anthill-1.css
- jquery.min_.1.10.1.js
- anthill-1.js
- Añadir paredes al hormiguero.
- Que las hormigas puedan recoger larvas.
- Y que las depositen si cerca hay otras larvas.
En próximas entregas añadiré funcionalidad adicional como:
Con esto se conseguirán agrupaciones automáticas sin que las hormigas tengan visión global del hormiguero, sólo de las celdas de alrededor.