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 Replies to “Hormiguero con jQuery (parte 3)”

  1. umbilical cord

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

  2. 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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.