Hormiguero con jQuery (parte 3)

En esta última entrega añado la funcionalidad de recogida y agrupación de larvas (las hormigas porteadoras cambian a azul claro) dentro del hormiguero:


La agrupación de larvas se consigue al agregar la inteligencia individual de cada hormiga, la cual es muy rudimentaria, sólo saben:

  1. Moverse sin chocarse de manera aleatoria.
  2. Coger una larva si la tienen delante y no está muy acompañada de otras larvas.
  3. Depositar la larva en un lugar dónde haya más larvas.

Código HTML:

<!doctype html>
<html>
<head>
<link rel="stylesheet" href="anthill-3.css">
<script src="jquery.min_.1.10.1.js"></script>
<script src="anthill-3.js"></script>
</head>
<body>
<div class="anthill" width="50" height="30" cell="15" />
</body>
</html>

Código CSS:

.anthill {
background-color: black;
position: relative;
}

.cell {
position: absolute;
}

.ant {
background-color: blue;
}

.ant-busy {
background-color: cyan;
}

.grub {
background-color: white;
}

.grub-carried {
display: hidden;
}

.wall {
background-color: orange;
}

Código JS:


// Enumeration of cell types.
var Cell = {
Wall: 0,
Ant: 1,
Grub: 2
}
// Hash to store all the needed data structures to store an anthill.
var theAnthill = {};

// Return a free position in the array.
function FreePosition(cells) {
var position = {x: 0, y: 0};
do {
// Calculate a random position in the anthill.
position.y = Math.floor(Math.random() * cells.length);
position.x = Math.floor(Math.random() * cells[position.y].length);
// Loop until a free position is found.
} while (cells[position.y][position.x] != null);
return position;
}

function FillCell(anthill, position, objects, cellClass, cellType) {
// Create a new div with the needed CSS class.
var element = $(document.createElement("div"));
element.addClass("cell " + cellClass);
// Set the element dimensions & position.
element.css({"width": ("" + (anthill.cell - 1) + "px"),
"height": ("" + (anthill.cell - 1) + "px"),
"left": ("" + ((position.x * anthill.cell) + 1) + "px"),
"top": ("" + ((position.y * anthill.cell) + 1) + "px")});
// Add the element to the anthill.
anthill.div.append(element);
// Add it to the elements array.
object = {position: position, type: cellType, element: element};
objects.push(object);
// Set the cell as busy.
anthill.cells[position.y][position.x] = object;
}

// Fill an array of elements.
function FillCells(countOfElements, anthill, elements, cellType, cellClass) {
for (var i = 0; i < countOfElements; i++) {
// Get a free position in the anthill.
var position = FreePosition(anthill.cells);
// Fill the cell type.
FillCell(anthill, position, elements, cellClass, cellType);
}
}

// Move a position in a toroide anthill world with a direction.
function MovePosition(anthill, position, direction) {
// Calculate a new position based on the direction.
var newPosition = {x: position.x + direction.incX,
y: position.y + direction.incY};
// Make the movement in a toroide world.
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;
}
return newPosition;
}

// Fill the anthill with walls.
function FillWalls(countOfWalls, anthill) {
var maxLength = anthill.cells.length;
maxLength = (anthill.cells[0].length < maxLength) ? anthill.cells[0].length : maxLength;
maxLength /= 3;
var minLength = maxLength / 5;
for (var leftLength = countOfWalls; leftLength >= 0; ) {
// Calculate wall length.
var length = Math.floor(Math.random() * (maxLength - minLength)) + minLength;
// Decrement the wall left length.
leftLength -= length;
// Get a free position in the anthill.
var position = FreePosition(anthill.cells);
// Calculate a direction to create the wall along to.
var direction = {incX: Math.floor(Math.random() * 2), incY: 0};
direction.incY = (direction.incX == 0) ? 1 : 0;
// Create as many walls as needed by the length.
for (var i = 0; i < length; i++) {
// Fill the cell with a wall.
FillCell(anthill, position, anthill.walls, "wall", Cell.Wall);
// Move the position.
position = MovePosition(anthill, position, direction);
}
}
}

// Change the direction of an ant.
function ChangeDirection(direction) {
do {
direction.incX = Math.floor(Math.random() * 3) - 1;
direction.incY = Math.floor(Math.random() * 3) - 1;
} while ((direction.incX == 0) && (direction.incY == 0));
}

// Move an ant inside the anthill.
function ChangeAntPosition(anthill, ant) {
// Calculate a new position based on the ant direction.
var newPosition = MovePosition(anthill, ant.position, ant.direction);
// Check if the new position is free.
if (anthill.cells[newPosition.y][newPosition.x] == null) {
// Empty the previous cell, and fill the new position.
anthill.cells[ant.position.y][ant.position.x] = null;
anthill.cells[newPosition.y][newPosition.x] = ant;
// Change the ant position.
ant.position = newPosition;
ant.element.css({"left": ("" + ((newPosition.x * anthill.cell) + 1) + "px"),
"top": ("" + ((newPosition.y * anthill.cell) + 1) + "px")});
} else {
// The new position is busy, change the ant direction (& don't move it).
ChangeDirection(ant.direction);
}
}

// Count how many grubs surrond a cell.
function CountGrubs(anthill, position) {
var count = 0;
// Generate the array with the 8 round directions.
var directions = [{incX: -1, incY: -1}, {incX: 0, incY: -1}, {incX: 1, incY: -1},
{incX: -1, incY: 0}, {incX: 1, incY: 0},
{incX: -1, incY: 1}, {incX: 0, incY: 1}, {incX: 1, incY: 1}];
$.each(directions, function(i, direction) {
// Calculate the position & search the grub.
var nearPosition = MovePosition(anthill, position, direction);
var grub = anthill.cells[nearPosition.y][nearPosition.x];
if ((grub != null) && (grub.type == Cell.Grub)) {
// It's a grub, count it.
count++;
}
})
return count;
}

// Carry or leave a grub.
function CarryGrub(anthill, ant) {
// Calculate the next position into the ant trajectory.
var nextPosition = MovePosition(theAnthill, ant.position, ant.direction);
if (ant.grub == null) {
// Check if in the trajectory there is an alone grub.
var grub = anthill.cells[nextPosition.y][nextPosition.x];
if ((grub != null) && (grub.type == Cell.Grub) && (CountGrubs(anthill, nextPosition) <= 3)) {
grub.element.removeClass("grub");
grub.element.addClass("grub-carried");
anthill.cells[nextPosition.y][nextPosition.x] = null;
ant.grub = grub;
ant.element.removeClass("ant");
ant.element.addClass("ant-busy");
}
} else {
// Check if in the trajectory there is an alone grub.
var empty = anthill.cells[nextPosition.y][nextPosition.x];
if ((empty == null) && (CountGrubs(anthill, nextPosition) >= 3)) {
ant.grub.element.removeClass("grub-carried");
ant.grub.element.addClass("grub");
ant.grub.position = nextPosition;
ant.grub.element.css({"left": ("" + ((nextPosition.x * anthill.cell) + 1) + "px"),
"top": ("" + ((nextPosition.y * anthill.cell) + 1) + "px")});
anthill.cells[nextPosition.y][nextPosition.x] = ant.grub;
ant.grub = null;
ant.element.removeClass("ant-busy");
ant.element.addClass("ant");
}
}
}

$(document).ready(function() {
$(".anthill").each(function() {
// Get the DIV element.
theAnthill.div = $(this);
// Get DIV attributes.
theAnthill.width = theAnthill.div.attr("width") || 50;
theAnthill.height = theAnthill.div.attr("height") || 30;
theAnthill.cell = theAnthill.div.attr("cell") || 15;
// Protect DIV attribute values.
theAnthill.width = (theAnthill.width < 10) ? 10 : theAnthill.width;
theAnthill.width = (theAnthill.width > 100) ? 100 : theAnthill.width;
theAnthill.height = (theAnthill.height < 10) ? 10 : theAnthill.height;
theAnthill.height = (theAnthill.height > 100) ? 100 : theAnthill.height;
theAnthill.cell = (theAnthill.cell < 1) ? 1 : theAnthill.cell;
theAnthill.cell = (theAnthill.cell > 100) ? 100 : theAnthill.cell;
// Set DIV dimensions.
theAnthill.div.css({"width": ("" + ((theAnthill.width * theAnthill.cell) + 1) + "px"),
"height": ("" + ((theAnthill.height * theAnthill.cell) + 1) + "px")});
// Fill the cells array.
theAnthill.cells = [];
for (var y = 0; y < theAnthill.height; y++) {
theAnthill.cells[y] = [];
for (var x = 0; x < theAnthill.width; x++) {
theAnthill.cells[y][x] = null;
}
}
// Calculate how many cell types we should generate.
var countOfWalls = (theAnthill.width * theAnthill.height) * 0.1;
var countOfAnts = (theAnthill.width * theAnthill.height) * 0.01;
var countOfGrubs = (theAnthill.width * theAnthill.height) * 0.07;
// Fill the walls array.
theAnthill.walls = [];
FillWalls(countOfWalls, theAnthill);
// Fill the ants array.
theAnthill.ants = [];
FillCells(countOfAnts, theAnthill, theAnthill.ants, Cell.Ant, "ant");
for (var i = 0; i < theAnthill.ants.length; i++) {
var ant = theAnthill.ants[i];
ant.direction = {};
ChangeDirection(ant.direction);
ant.grub = null;
}
// Fill the grubs array.
theAnthill.grubs = [];
FillCells(countOfGrubs, theAnthill, theAnthill.grubs, Cell.Grub, "grub");
});
});

window.setInterval(function() {
// Loop along all the ants.
for (var i = 0; i < theAnthill.ants.length; i++) {
var ant = theAnthill.ants[i];
// Change the direction of the ant 1 of 20 times.
if (Math.floor(Math.random() * 20) == 0) {
ChangeDirection(ant.direction);
}
// Move the ant.
ChangeAntPosition(theAnthill, ant);
CarryGrub(theAnthill, ant);
}
}, 100); // 100ms period.

A continuación os dejo los enlaces al código fuente:

Esto finaliza las entregas del hormiguero, de momento 😀

3 comentarios

  1. umbilical cord

    Hormiguero con jQuery (parte 3) « OCIO y TECnología

  2. David dice: Responder

    Hola una consulta, estuve viendo este ejemplo 3 de Aut. Celul. Mi duda es como definís esta simulación con la definición formal de Aut. Celul. A través de la Sextupla que lo define, y también el lenguajes que se genera y/o produce.. Muchas gracias!!! y disculpa mis molestias

    1. Tal y como puedes ver el código fuente está en JavaScript, no se hace uso de la definición formal del autómata.

Deja un comentario