WaterBugs the Simulator

Paste ID: c118c0c8

Created at: 2024-08-18 00:46:48

javascript code simulator simulation full page
<!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>

Share this Paste