WaterBugs the Simulator
Paste ID: c118c0c8
Created at: 2024-08-18 00:46:48
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Comprehensive Water Bugs Simulation</title>
<script src="https://unpkg.com/brain.js"></script>
<style>
canvas { border: 1px solid black; }
#communication, #bugActions { font-family: monospace; white-space: pre; }
</style>
</head>
<body>
<canvas id="simulationCanvas" width="800" height="600"></canvas>
<div id="communication"></div>
<div id="bugActions"></div>
<script>
const canvas = document.getElementById('simulationCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const commDisplay = document.getElementById('communication');
const actionDisplay = document.getElementById('bugActions');
const commNet = new brain.NeuralNetwork();
const net = new brain.NeuralNetwork();
net.train([
{ input: new Array(17).fill(0), output: new Array(19).fill(0.1) },
{ input: new Array(17).fill(1), output: new Array(19).fill(0.9) },
{ input: new Array(17).fill(0.5), output: new Array(19).fill(0.5) },
]);
commNet.train([
{ input: [1, 0, 0, 0], output: [1, 0, 0, 0] },
{ input: [0, 1, 0, 0], output: [0, 1, 0, 0] },
{ input: [0, 0, 1, 0], output: [0, 0, 1, 0] },
{ input: [0, 0, 0, 1], output: [0, 0, 0, 1] },
]);
function drawWaterBackground() {
// Create a gradient that resembles water
const gradient = ctx.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, '#00aaff'); // Top color
gradient.addColorStop(1, '#004f7f'); // Bottom color
// Fill the canvas with the gradient
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Draw animated water effect
const waveAmplitude = 10;
const waveFrequency = 0.02;
ctx.save();
ctx.globalAlpha = 0.3; // Semi-transparent effect
for (let i = 0; i < height; i += 20) {
ctx.beginPath();
ctx.moveTo(0, i);
for (let x = 0; x < width; x += 5) {
const y = i + waveAmplitude * Math.sin(waveFrequency * x + performance.now() * 0.001);
ctx.lineTo(x, y);
}
ctx.lineTo(width, i + waveAmplitude);
ctx.lineTo(width, i);
ctx.closePath();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.3)'; // Light wave highlight
ctx.stroke();
}
ctx.restore();
}
class Environment {
constructor() {
this.tunnels = [];
}
addTunnel(tunnel) {
this.tunnels.push(tunnel);
}
getTunnelAtPosition(position) {
return this.tunnels.find(tunnel =>
tunnel.segments.some(seg => seg.x === position.x && seg.y === position.y)
);
}
}
class Boss {
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 60;
this.height = 60;
this.health = 500;
this.speed = 1;
}
update() {
// Basic movement logic (move in a random direction)
this.x += (Math.random() - 0.5) * this.speed;
this.y += (Math.random() - 0.5) * this.speed;
// Keep within bounds
this.x = Math.max(0, Math.min(width - this.width, this.x));
this.y = Math.max(0, Math.min(height - this.height, this.y));
}
draw() {
// Draw the boss
ctx.fillStyle = 'purple';
ctx.fillRect(this.x, this.y, this.width, this.height);
}
takeDamage(amount) {
this.health -= amount;
if (this.health <= 0) {
this.die();
}
}
die() {
console.log("Boss defeated!");
// Logic for what happens when the boss is defeated
}
}
class Tunnel {
constructor(startX, startY, length, direction) {
this.startX = startX;
this.startY = startY;
this.length = length;
this.direction = direction; // 0: horizontal, 1: vertical
this.segments = this.createSegments();
}
createSegments() {
const segments = [];
for (let i = 0; i < this.length; i++) {
if (this.direction === 0) { // Horizontal
segments.push({ x: this.startX + i, y: this.startY });
} else { // Vertical
segments.push({ x: this.startX, y: this.startY + i });
}
}
return segments;
}
}
let environment = new Environment();
class Bug {
constructor(x, y, tribe) {
this.x = x;
this.y = y;
this.angle = Math.random() * Math.PI * 2;
this.speed = 1 + Math.random();
this.slowDownTime = 0;
this.target = null;
this.radius = 5;
this.hitPoints = 100;
this.gender = Math.random() < 0.5 ? 'male' : 'female';
this.foodEaten = 0;
this.growthScore = 0;
this.tribe = tribe;
this.color = this.randomColor();
this.variety = this.randomVariety();
this.jumping = false;
this.building = false; // Indicates if the bug is currently building
this.buildDirection = Math.random() < 0.5 ? 0 : 1; // Randomly choose direction (0: horizontal, 1: vertical)
this.buildLength = 10; // Length of the tunnel to build
this.running = false;
this.hasCommunicated = false;
this.strikingAnimation = 0;
this.vote = null;
this.message = [0, 0, 0, 0];
this.lastActions = [];
this.inTunnel = false; // Indicates if the bug is currently in a tunnel
this.topics = {}; // Dictionary to keep track of topics and their content
this.sprayingGoo = false; // Indicates if the bug is spraying goo
this.gooColor = this.randomColor(); // Goo color for this bug
this.collectingPebbles = false;
this.pebbles = 0;
this.tracerActive = false;
this.tracerPositions = [];
this.auraCharging = false;
}
// Method to start spraying goo
startSpraying() {
this.sprayingGoo = true;
}
// Method to stop spraying goo
stopSpraying() {
this.sprayingGoo = false;
}
// Method to handle goo spraying
sprayGoo(mapGoo) {
if (this.sprayingGoo) {
// Spray goo in the direction of movement
const goo = new Goo(this.x, this.y, this.gooColor);
mapGoo.push(goo);
}
}
// Random color generation
randomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
randomVariety() {
// Define varieties and select one at random
const varieties = ['standard', 'rare', 'legendary'];
return varieties[Math.floor(Math.random() * varieties.length)];
}
mate() {
// Define the mating process
return new Bug(this.tribe);
}
dodge() {
// Simple dodge logic
const dodgeDirection = Math.random() < 0.5 ? Math.PI / 2 : -Math.PI / 2;
this.x += Math.cos(this.angle + dodgeDirection) * this.speed;
this.y += Math.sin(this.angle + dodgeDirection) * this.speed;
}
castVote() {
this.vote = Math.floor(Math.random() * 3);
console.log(`Bug cast vote: ${this.vote}`);
this.lastActions.push('vote');
}
decide(bugs, food) {
const input = [
this.x / width,
this.y / height,
this.hitPoints / 100,
bugs.length / 50,
food.length / 100,
this.slowDownTime > 0 ? 1 : 0,
this.target ? 1 : 0,
this.tribe ? 1 : 0,
this.gender === 'male' ? 1 : 0,
this.foodEaten / 100,
this.growthScore / 1000,
this.jumping ? 1 : 0,
this.running ? 1 : 0,
this.strikingAnimation > 0 ? 1 : 0,
this.collectingPebbles ? 1 : 0,
this.tracerActive ? 1 : 0,
this.auraCharging ? 1 : 0
];
const output = net.run(input);
this.lastActions = [];
if (output[0] > 0.8) this.startBuilding();
if (output[1] > 0.5) this.run();
if (output[2] > 0.6) this.jump();
if (output[3] > 0.7 && !this.tribe) this.joinTribe(bugs);
if (output[4] > 0.8) this.fight(bugs);
if (output[5] > 0.9) this.mate(bugs);
if (output[6] > 0.7) this.spit(bugs);
if (output[7] > 0.6) this.findTarget(bugs);
if (this.tribe && output[8] > 0.5) this.castVote();
if (output[9] > 0.8) this.grow();
if (output[10] > 0.9) this.leaveTribe();
if (output[11] > 0.95) this.evolve();
if (output[12] > 0.7) this.sacrifice();
if (output[13] > 0.7) this.defend();
if (output[14] > 0.7) this.hide();
if (output[15] > 0.7) this.explore();
if (output[16] > 0.7) this.reproduce();
if (output[17] > 0.7) this.collectPebbles();
if (output[18] > 0.8) this.activateTracer();
if (output[19] > 0.9) this.auraCharge(bugs);
this.message = output.slice(0, 4).map(v => v > 0.5 ? 1 : 0);
}
startBuilding() {
this.building = true;
const tunnel = new Tunnel(this.x, this.y, this.buildLength, this.buildDirection);
this.lastActions.push('start building tunnel');
environment.addTunnel(tunnel);
}
update() {
// Existing update logic...
if (this.inTunnel) {
this.moveThroughTunnel();
} else {
const currentPosition = { x: Math.floor(this.x), y: Math.floor(this.y) };
const tunnel = environment.getTunnelAtPosition(currentPosition);
if (tunnel) {
this.inTunnel = true;
this.currentTunnel = tunnel;
}
}
}
moveThroughTunnel() {
// Logic to move the bug through the tunnel
const exit = this.currentTunnel.getExitPoint();
if (this.x === exit.x && this.y === exit.y) {
this.inTunnel = false;
this.currentTunnel = null;
} else {
// Move towards the exit of the tunnel
const direction = Math.atan2(exit.y - this.y, exit.x - this.x);
this.x += Math.cos(direction);
this.y += Math.sin(direction);
}
}
run() {
this.running = true;
this.lastActions.push('run');
}
jump() {
this.jumping = true;
this.lastActions.push('jump');
setTimeout(() => this.jumping = false, 200); // Simulate a short jump
}
move() {
const tempEffect = 1;
const speed = (this.running ? this.speed * 2 : this.speed) * tempEffect;
if (this.slowDownTime > 0) {
this.slowDownTime--;
this.speed = 0.5 * tempEffect;
} else {
this.speed = (1 + Math.random()) * tempEffect;
}
if (this.tribe && this.tribe.currentTask) {
const taskTarget = this.tribe.getTaskTarget();
this.angle = Math.atan2(taskTarget.y - this.y, taskTarget.x - this.x);
} else if (this.target) {
this.angle = Math.atan2(this.target.y - this.y, this.target.x - this.x);
} else {
this.angle += (Math.random() - 0.5) * Math.PI / 5;
}
const newX = this.x + Math.cos(this.angle) * speed;
const newY = this.y + Math.sin(this.angle) * speed;
const collided = bugs.some(other => {
if (other === this) return false;
return Math.hypot(newX - other.x, newY - other.y) < this.radius + other.radius;
});
if (!collided) {
this.x = newX;
this.y = newY;
} else {
this.angle += Math.PI; // Bounce back
}
this.x = Math.max(0, Math.min(width, this.x));
this.y = Math.max(0, Math.min(height, this.y));
}
joinTribe(bugs) {
const nearbyBugs = bugs.filter(b => b !== this && this.distanceTo(b) < 50);
if (nearbyBugs.length >= 3) {
if (nearbyBugs[0].tribe) {
this.tribe = nearbyBugs[0].tribe;
this.tribe.addBug(this);
} else {
this.tribe = new Tribe(nearbyBugs.concat(this));
}
this.lastActions.push('join tribe');
}
}
fight(bugs) {
const opponent = bugs.find(b => b !== this && this.distanceTo(b) < 10);
if (opponent) {
opponent.hitPoints -= 10;
this.strikingAnimation = 10;
this.lastActions.push('fight');
}
}
mate(bugs) {
if (this.gender === 'female') {
const male = bugs.find(b => b.gender === 'male' && this.distanceTo(b) < 10);
if (male) {
setTimeout(() => {
bugs.push(new Bug(this.x, this.y));
}, Math.random() * 9000 + 1000); // 1-10 seconds delay for spawning new bug
this.lastActions.push('mate');
}
}
}
spit(bugs) {
const target = bugs.find(b => b !== this && this.distanceTo(b) < 50);
if (target) {
target.slowDownTime = 100;
this.lastActions.push('spit');
}
}
findTarget(bugs) {
if (!this.target || Math.random() < 0.01) {
this.target = bugs.find(bug => bug !== this);
this.lastActions.push('find target');
}
}
castVote() {
this.vote = Math.floor(Math.random() * 3);
this.lastActions.push('vote');
}
grow() {
if (this.foodEaten > 10) {
this.radius += 0.1;
this.foodEaten -= 10;
this.lastActions.push('grow');
}
}
leaveTribe() {
if (this.tribe) {
this.tribe.removeBug(this);
this.tribe = null;
this.lastActions.push('leave tribe');
}
}
evolve() {
if (this.growthScore > 1000) {
this.speed *= 1.1;
this.hitPoints *= 1.1;
this.growthScore -= 1000;
this.lastActions.push('evolve');
}
}
sacrifice() {
if (this.tribe && this.tribe.bugs.length > 5) {
this.tribe.bugs.forEach(bug => {
if (bug !== this) bug.hitPoints += this.hitPoints / (this.tribe.bugs.length - 1);
});
this.hitPoints = 0;
this.lastActions.push('sacrifice');
}
}
defend() {
this.strikingAnimation = 5;
this.lastActions.push('defend');
}
hide() {
this.slowDownTime = 50;
this.lastActions.push('hide');
}
explore() {
this.angle += Math.random() * Math.PI * 2;
this.lastActions.push('explore');
}
reproduce() {
if (this.gender === 'female') {
setTimeout(() => {
bugs.push(new Bug(this.x, this.y));
}, Math.random() * 9000 + 1000);
this.lastActions.push('reproduce');
}
}
eat(food) {
const eatenFood = food.filter(f => Math.hypot(f.x - this.x, f.y - this.y) < this.radius);
this.foodEaten += eatenFood.length;
if (this.foodEaten >= 100) {
this.growthScore += this.foodEaten * 0.3 + 100 + this.growthScore;
this.radius = 5 + this.growthScore / 100;
this.foodEaten = 0;
}
if (eatenFood.length > 0) this.lastActions.push('eat');
return food.filter(f => !eatenFood.includes(f));
}
communicate(otherBug, event) {
if (event.occurs) {
this.hasCommunicated = true;
const response = commNet.run(this.message);
return `Bug says: ${this.message.join('')} -> Response: ${response.map(v => v > 0.5 ? 1 : 0).join('')}`;
} else {
return 'No event to communicate about.';
}
}
collectPebbles() {
this.collectingPebbles = true;
this.pebbles++;
if (this.pebbles >= 5) {
this.depositPebbles();
}
this.lastActions.push('collect pebbles');
}
depositPebbles() {
if (this.tribe) {
const center = this.tribe.getCenter();
// Simple ragdoll physics: pebbles scatter randomly around the center
for (let i = 0; i < this.pebbles; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * 20;
const pebbleX = center.x + Math.cos(angle) * distance;
const pebbleY = center.y + Math.sin(angle) * distance;
// You'd need to create a pebbles array to store these
pebbles.push({ x: pebbleX, y: pebbleY });
}
}
this.pebbles = 0;
this.collectingPebbles = false;
}
activateTracer() {
this.tracerActive = true;
this.tracerPositions.push({ x: this.x, y: this.y });
if (this.tracerPositions.length > 10) {
this.tracerPositions.shift();
}
this.lastActions.push('activate tracer');
}
auraCharge(bugs) {
this.auraCharging = true;
bugs.forEach(bug => {
if (bug !== this && this.distanceTo(bug) < 50) {
const angle = Math.atan2(this.y - bug.y, this.x - bug.x);
bug.x += Math.cos(angle) * 2;
bug.y += Math.sin(angle) * 2;
}
});
this.lastActions.push('aura charge');
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
if (this.tribe) {
ctx.fillStyle = 'green';
} else if (this.slowDownTime > 0) {
ctx.fillStyle = 'red';
} else {
ctx.fillStyle = this.gender === 'male' ? 'blue' : 'pink';
}
ctx.fill();
ctx.closePath();
if (this.strikingAnimation > 0) {
ctx.beginPath();
for (let i = 0; i < 5; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * 10;
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + Math.cos(angle) * distance, this.y + Math.sin(angle) * distance);
}
ctx.strokeStyle = 'white';
ctx.stroke();
ctx.closePath();
this.strikingAnimation--;
}
if (this.tracerActive) {
ctx.beginPath();
this.tracerPositions.forEach((pos, index) => {
ctx.lineTo(pos.x, pos.y);
ctx.strokeStyle = `rgba(255, 255, 255, ${index / this.tracerPositions.length})`;
ctx.lineWidth = index / 2;
});
ctx.stroke();
}
if (this.auraCharging) {
ctx.beginPath();
ctx.arc(this.x, this.y, 30, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(0, 255, 255, 0.3)';
ctx.fill();
}
}
distanceTo(other) {
return Math.hypot(other.x - this.x, other.y - this.y);
}
}
class WorkerBug extends Bug {
constructor(x, y) {
super(x, y);
this.speed = 1.5;
this.color = 'green'; // Color for WorkerBug
}
decide(bugs, food) {
super.decide(bugs, food);
if (Math.random() < 0.1) this.build();
if (Math.random() < 0.2) this.gatherFood(food);
}
build() {
this.lastActions.push('build');
}
gatherFood(food) {
this.lastActions.push('gather food');
}
}
class WarriorBug extends Bug {
constructor(x, y) {
super(x, y);
this.hitPoints = 150;
this.speed = 1.2;
this.color = 'red'; // Color for WarriorBug
}
decide(bugs, food) {
super.decide(bugs, food);
if (Math.random() < 0.3) this.patrol();
if (Math.random() < 0.4) this.attack(bugs);
}
patrol() {
this.lastActions.push('patrol');
}
attack(bugs) {
const enemy = bugs.find(bug => bug !== this && bug.tribe !== this.tribe);
if (enemy) {
enemy.hitPoints -= 20;
this.lastActions.push('attack');
}
}
}
class ScoutBug extends Bug {
constructor(x, y) {
super(x, y);
this.speed = 2;
this.color = 'blue'; // Color for ScoutBug
}
decide(bugs, food) {
super.decide(bugs, food);
if (Math.random() < 0.4) this.scout();
if (Math.random() < 0.3) this.report();
}
scout() {
this.lastActions.push('scout');
}
report() {
this.lastActions.push('report');
}
}
// New Bug Classes
class HealerBug extends Bug {
constructor(x, y) {
super(x, y);
this.healingPower = 50;
this.speed = 1.4;
this.color = 'yellow'; // Color for HealerBug
}
decide(bugs, food) {
super.decide(bugs, food);
if (Math.random() < 0.2) this.heal();
if (Math.random() < 0.3) this.collectResources(food);
}
heal() {
const ally = bugs.find(bug => bug !== this && bug.tribe === this.tribe);
if (ally) {
ally.hitPoints += this.healingPower;
this.lastActions.push('heal');
}
}
collectResources(food) {
this.lastActions.push('collect resources');
}
}
class BuilderBug extends Bug {
constructor(x, y) {
super(x, y);
this.constructionSkill = 80;
this.speed = 1.6;
this.color = 'orange'; // Color for BuilderBug
}
decide(bugs, food) {
super.decide(bugs, food);
if (Math.random() < 0.3) this.construct();
if (Math.random() < 0.2) this.gatherMaterials(food);
}
construct() {
this.lastActions.push('construct');
}
gatherMaterials(food) {
this.lastActions.push('gather materials');
}
}
class DragonBug {
constructor(x, y) {
this.head = { x: x, y: y };
this.segments = [];
// Initialize the segments, each one slightly behind the previous one
for (let i = 0; i < 20; i++) {
this.segments.push({ x: x - i * 5, y: y });
}
this.speed = 2;
this.angle = 0;
}
update() {
// Update the head position based on speed and angle
this.angle += (Math.random() - 0.5) * 0.05;
this.head.x += Math.cos(this.angle) * this.speed;
this.head.y += Math.sin(this.angle) * this.speed;
// Ensure the head stays within bounds
this.head.x = Math.max(0, Math.min(width, this.head.x));
this.head.y = Math.max(0, Math.min(height, this.head.y));
// Update each segment to follow the one before it
for (let i = this.segments.length - 1; i > 0; i--) {
this.segments[i].x = this.segments[i - 1].x;
this.segments[i].y = this.segments[i - 1].y;
}
// The first segment follows the head
this.segments[0].x = this.head.x;
this.segments[0].y = this.head.y;
}
draw() {
// Draw the head
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(this.head.x, this.head.y, 10, 0, 2 * Math.PI);
ctx.fill();
// Draw each segment, making them progressively smaller
ctx.fillStyle = 'orange';
for (let i = 0; i < this.segments.length; i++) {
const size = 8 - i * 0.3; // Smaller as we move away from the head
ctx.beginPath();
ctx.arc(this.segments[i].x, this.segments[i].y, size, 0, 2 * Math.PI);
ctx.fill();
}
}
}
class GooDot {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 5; // Size of the goo dot
this.color = 'green'; // Color of the goo dot
}
draw() {
console.log("Drawing GooDot at", this.x, this.y);
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
}
const gooDots = [];
function initializeGooDots(count) {
for (let i = 0; i < count; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
gooDots.push(new GooDot(x, y));
}
}
initializeGooDots(10); // Add 10 goo dots to the map
class GooBug extends Bug {
constructor(x, y) {
super(x, y);
this.gooCollected = [];
}
decide(bugs, food, gooDots) {
super.decide(bugs, food);
// Check for nearby goo dots and collect them
gooDots.forEach((gooDot, index) => {
if (this.distanceTo(gooDot) < 20) {
this.gooCollected.push(gooDot);
gooDots.splice(index, 1); // Remove goo dot from the map
}
});
// Randomly decide to disperse goo dots
if (this.gooCollected.length > 0 && Math.random() < 0.05) {
this.disperseGoo(bugs);
}
}
disperseGoo(bugs) {
console.log("GooBug is dispersing goo");
const patternCenterX = this.x;
const patternCenterY = this.y;
const patternRadius = 50; // Radius of the dispersal pattern
const angleStep = (Math.PI * 2) / this.gooCollected.length;
this.gooCollected.forEach((gooDot, i) => {
const angle = i * angleStep;
gooDot.x = patternCenterX + Math.cos(angle) * patternRadius;
gooDot.y = patternCenterY + Math.sin(angle) * patternRadius;
gooDots.push(gooDot);
});
this.gooCollected = []; // Clear collected goo after dispersal
}
distanceTo(object) {
return Math.hypot(object.x - this.x, object.y - this.y);
}
draw() {
super.draw();
// Optional: Visualize the collected goo dots on the bug
ctx.fillStyle = 'green';
this.gooCollected.forEach(gooDot => {
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
ctx.fill();
});
}
}
class Goo {
constructor(x, y, color) {
this.x = x; // X position
this.y = y; // Y position
this.color = color; // Goo color
this.size = 10; // Initial size of goo
this.maxSize = 30; // Maximum size of goo
}
// Update the goo's size and other properties if needed
update() {
if (this.size < this.maxSize) {
this.size += 0.5; // Increase size gradually
}
}
// Draw the goo on the canvas
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2, false);
ctx.fillStyle = this.color;
ctx.fill();
}
}
class Submarine {
constructor(x, y) {
this.x = x;
this.y = y;
this.angle = 0;
this.speed = 1;
this.width = 40;
this.height = 20;
this.isDiving = false; // Flag to check if the submarine is diving
this.diveDuration = 5000; // Duration of the dive in milliseconds (e.g., 5 seconds)
this.lastDiveTime = 0; // Time when the submarine last dove
}
update(bugs) {
if (!this.isDiving) {
// Move randomly
this.angle += (Math.random() - 0.5) * 0.1;
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
// Keep within bounds
this.x = Math.max(0, Math.min(width, this.x));
this.y = Math.max(0, Math.min(height, this.y));
// Shoot at aura charging bugs
bugs.forEach(bug => {
if (bug.auraCharging && this.distanceTo(bug) < 100) {
this.shoot(bug);
}
});
// Random submarine actions
if (Math.random() < 0.01) this.dive();
if (Math.random() < 0.01) this.surface();
if (Math.random() < 0.01) this.ping();
} else {
// Check if it's time to resurface
if (Date.now() - this.lastDiveTime >= this.diveDuration) {
this.surface();
}
}
}
shoot(bug) {
bug.hitPoints -= 20;
// You could add visual effects for shooting here
}
dive() {
console.log("Submarine is diving");
this.isDiving = true;
this.lastDiveTime = Date.now();
}
surface() {
console.log("Submarine is surfacing");
this.isDiving = false;
}
ping() {
console.log("Submarine is pinging");
}
distanceTo(object) {
return Math.hypot(object.x - this.x, object.y - this.y);
}
draw() {
if (!this.isDiving) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.fillStyle = 'gray';
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
ctx.restore();
}
}
}
class Tribe {
constructor(bugs) {
bugs.forEach(bug => bug.tribe = this);
this.currentTask = null;
this.taskProgress = 0;
this.taskTarget = null;
this.bugs = []; // List of bugs in the tribe
}
addBug(bug) {
this.bugs.push(bug);
bug.tribe = this;
console.log(`Bug added to tribe. Tribe now has ${this.bugs.length} bugs`);
}
removeBug(bug) {
this.bugs = this.bugs.filter(b => b !== bug);
bug.tribe = null;
console.log(`Bug removed from tribe. Tribe now has ${this.bugs.length} bugs`);
}
getCenter() {
const x = this.bugs.reduce((sum, bug) => sum + bug.x, 0) / this.bugs.length;
const y = this.bugs.reduce((sum, bug) => sum + bug.y, 0) / this.bugs.length;
return { x, y };
}
vote() {
if (this.bugs.length === 0) {
return null; // or some default value
}
const votes = this.bugs.map(bug => bug.vote).filter(vote => vote !== null);
if (votes.length === 0) {
return null; // or some default value
}
const voteCount = votes.reduce((acc, vote) => {
acc[vote] = (acc[vote] || 0) + 1;
return acc;
}, {});
const winner = Object.keys(voteCount).reduce((a, b) => voteCount[a] > voteCount[b] ? a : b);
return parseInt(winner);
}
act() {
if (!this.currentTask) {
const decision = this.vote();
if (decision !== null) {
switch(decision) {
case 0:
this.currentTask = 'move';
this.taskTarget = {
x: Math.random() * width,
y: Math.random() * height
};
break;
case 1:
this.currentTask = 'eat';
this.taskProgress = 0;
break;
case 2:
this.disband();
return;
}
} else {
// Handle case when there's no valid vote
this.currentTask = 'idle'; // or some default behavior
}
}
if (this.currentTask === 'move') {
const center = this.getCenter();
if (Math.hypot(center.x - this.taskTarget.x, center.y - this.taskTarget.y) < 10) {
this.currentTask = null;
}
} else if (this.currentTask === 'eat') {
this.taskProgress += this.bugs.length;
if (this.taskProgress >= 1000) {
this.currentTask = null;
}
}
this.bugs.forEach(bug => {
const grazingAngle = Math.random() * Math.PI * 2;
bug.x += Math.cos(grazingAngle) * 0.5;
bug.y += Math.sin(grazingAngle) * 0.5;
bug.x = Math.max(0, Math.min(width, bug.x));
bug.y = Math.max(0, Math.min(height, bug.y));
});
}
disband() {
this.bugs.forEach(bug => bug.tribe = null);
this.bugs = [];
}
getTaskTarget() {
return this.taskTarget || this.getCenter();
}
mateBugs() {
// Select pairs of bugs for mating
const newBugs = [];
for (let i = 0; i < this.bugs.length; i += 2) {
if (i + 1 < this.bugs.length) {
const parent1 = this.bugs[i];
const parent2 = this.bugs[i + 1];
const child1 = parent1.mate();
const child2 = parent2.mate();
// Optionally, mix traits from both parents
child1.color = this.mixColors(parent1.color, parent2.color);
child2.color = this.mixColors(parent1.color, parent2.color);
child1.variety = this.randomVariety(); // New variety
child2.variety = this.randomVariety(); // New variety
newBugs.push(child1, child2);
}
}
// Add new bugs to the tribe
newBugs.forEach(bug => this.addBug(bug));
}
mixColors(color1, color2) {
// Simple color mixing algorithm
const r1 = parseInt(color1.slice(1, 3), 16);
const g1 = parseInt(color1.slice(3, 5), 16);
const b1 = parseInt(color1.slice(5, 7), 16);
const r2 = parseInt(color2.slice(1, 3), 16);
const g2 = parseInt(color2.slice(3, 5), 16);
const b2 = parseInt(color2.slice(5, 7), 16);
const r = Math.floor((r1 + r2) / 2).toString(16).padStart(2, '0');
const g = Math.floor((g1 + g2) / 2).toString(16).padStart(2, '0');
const b = Math.floor((b1 + b2) / 2).toString(16).padStart(2, '0');
return `#${r}${g}${b}`;
}
randomVariety() {
// Define varieties and select one at random
const varieties = ['standard', 'rare', 'legendary'];
return varieties[Math.floor(Math.random() * varieties.length)];
}
}
let bugs = [];
for (let i = 0; i < 50; i++) {
const bugType = Math.random();
if (bugType < 0.2) {
bugs.push(new WorkerBug(Math.random() * width, Math.random() * height));
} else if (bugType < 0.3) {
bugs.push(new WarriorBug(Math.random() * width, Math.random() * height));
} else if (bugType < 0.4) {
bugs.push(new ScoutBug(Math.random() * width, Math.random() * height));
} else if (bugType < 0.5) {
bugs.push(new HealerBug(Math.random() * width, Math.random() * height));
} else if (bugType < 0.6) {
bugs.push(new BuilderBug(Math.random() * width, Math.random() * height));
} else {
bugs.push(new DragonBug(Math.random() * width, Math.random() * height));
}
}
let food = [];
for (let i = 0; i < 100; i++) {
food.push({ x: Math.random() * width, y: Math.random() * height });
}
class Event {
constructor(name) {
this.name = name;
this.occurs = false; // Whether the event has occurred
}
trigger() {
this.occurs = true;
}
reset() {
this.occurs = false;
}
}
const event = new Event('Special Event');
let submarine = new Submarine(width / 2, height / 2);
let pebbles = [];
function simulate() {
pebbles.forEach(pebble => {
ctx.beginPath();
ctx.arc(pebble.x, pebble.y, 2, 0, Math.PI * 2);
ctx.fillStyle = 'brown';
ctx.fill();
ctx.closePath();
});
if (Math.random() < 0.01) { // Randomly trigger the event (adjust as needed)
event.trigger();
console.log('Event triggered!');
}
submarine.update(bugs);
submarine.draw();
ctx.clearRect(0, 0, width, height);
drawWaterBackground(); // Draw water-like background
food.forEach(f => {
ctx.beginPath();
ctx.arc(f.x, f.y, 2, 0, Math.PI * 2);
ctx.fillStyle = 'yellow';
ctx.fill();
ctx.closePath();
});
bugs = bugs.filter(bug => bug.hitPoints > 0);
const tribes = [];
bugs.forEach(bug => {
bug.decide(bugs, food);
bug.move();
food = bug.eat(food);
bug.draw();
if (bug.tribe && !tribes.includes(bug.tribe)) {
tribes.push(bug.tribe);
}
});
tribes.forEach(tribe => tribe.act());
while (food.length < 100) {
food.push({ x: Math.random() * width, y: Math.random() * height });
}
// Display communication
// Communicate about the event
let commOutput = '';
for (let i = 0; i < bugs.length; i++) {
const bug = bugs[i];
const otherBugs = bugs.filter(b => b !== bug && bug.distanceTo(b) < 50); // Communication range
otherBugs.forEach(otherBug => {
const response = bug.communicate(otherBug, event);
commOutput += `Bug ${i+1} talks to Bug ${bugs.indexOf(otherBug)+1}: ${response}\n`;
});
}
commDisplay.textContent = commOutput;
// Display bug actions
let actionOutput = '';
for (let i = 0; i < Math.min(10, bugs.length); i++) {
actionOutput += `Bug ${i+1}: ${bugs[i].lastActions.join(', ')}\n`;
}
actionDisplay.textContent = actionOutput;
requestAnimationFrame(simulate);
}
function pollBugs() {
setInterval(() => {
bugs.forEach((bug, index) => {
if (bug.hasCommunicated) {
console.log(`Bug ${index+1} has communicated about the event.`);
bug.hasCommunicated = false; // Reset communication status
} else {
console.log(`Bug ${index+1} has not communicated about the event.`);
}
});
}, 10000); // 10 seconds interval
}
const dragonBug = new DragonBug(100, 100);
let lastBossTime = Date.now();
let boss = null;
function checkBossSpawn() {
const currentTime = Date.now();
const elapsedTime = currentTime - lastBossTime;
if (elapsedTime >= 2 * 60 * 1000) { // 2 minutes in milliseconds
spawnBoss();
lastBossTime = currentTime;
}
}
function spawnBoss() {
const x = Math.random() * (width - 60); // Random x position within bounds
const y = Math.random() * (height - 60); // Random y position within bounds
boss = new Boss(x, y);
console.log("Boss has appeared!");
}
function gameLoop() {
ctx.clearRect(0, 0, width, height);
dragonBug.update();
dragonBug.draw();
if (boss) {
boss.update();
boss.draw();
}
bugs.forEach(bug => {
bug.update(bugs, food, gooDots);
bug.draw();
});
// Draw goo dots
gooDots.forEach(gooDot => {
gooDot.draw();
});
// Update other entities like food and submarine
submarine.update(bugs);
submarine.draw();
checkBossSpawn();
requestAnimationFrame(gameLoop);
}
gameLoop();
// Start the simulation and polling
simulate();
pollBugs();
</script>
</body>
</html>