Skip to content

Instantly share code, notes, and snippets.

@shakkurcwb
Created August 26, 2023 03:43
Show Gist options
  • Save shakkurcwb/1050875b9bd4e46a78df740b1a9e6ef5 to your computer and use it in GitHub Desktop.
Save shakkurcwb/1050875b9bd4e46a78df740b1a9e6ef5 to your computer and use it in GitHub Desktop.
Predator vs Prey Game
<!DOCTYPE html>
<html>
<head>
<title>Predator and Prey</title>
<style>
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<script>
let canvas = document.getElementById("gameCanvas");
let ctx = canvas.getContext("2d");
const CREATURE_SIZE = 10;
const AVAILABLE_WIDTH = canvas.width - CREATURE_SIZE;
const AVAILABLE_HEIGHT = canvas.height - CREATURE_SIZE;
class Creature {
constructor(type, x, y, color) {
this.type = type;
this.x = x;
this.y = y;
this.color = color;
}
draw() {
// ctx.fillStyle = this.color;
// ctx.fillRect(this.x, this.y, CREATURE_SIZE, CREATURE_SIZE);
ctx.beginPath();
ctx.arc(this.x, this.y, CREATURE_SIZE, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
ctx.strokeStyle = 'white';
ctx.stroke();
ctx.closePath();
}
move(x, y) {
this.x = x;
this.y = y;
this.afterMove();
}
afterMove() {
// this.teleport();
this.bounce();
}
teleport() {
// when colliding with walls, respawn on the other side
if (this.x < 0) this.x = AVAILABLE_WIDTH;
if (this.x > AVAILABLE_WIDTH) this.x = 0;
if (this.y < 0) this.y = AVAILABLE_HEIGHT;
if (this.y > AVAILABLE_HEIGHT) this.y = 0;
}
bounce() {
// when colliding with walls, bounce back
if (this.x < 0) this.x = 0;
if (this.x > AVAILABLE_WIDTH) this.x = AVAILABLE_WIDTH;
if (this.y < 0) this.y = 0;
if (this.y > AVAILABLE_HEIGHT) this.y = AVAILABLE_HEIGHT;
}
}
const PREDATOR = "predator";
const PREY = "prey";
class Predator extends Creature {
static type = PREDATOR;
static color = "red";
constructor(x, y) {
super(Predator.type, x, y, Predator.color);
}
hunt() {
let x = this.x;
let y = this.y;
const {
closestPrey,
closestDistance
} = this.lookAround();
if (!closestPrey) {
// roam
let movement = Helper.getRandomMovement();
this.move(x + movement.x, y + movement.y);
return;
}
// run away
let movement = Helper.getMovementTowards(this.x, this.y, closestPrey.x, closestPrey.y);
this.move(x + movement.x, y + movement.y);
if (closestDistance < 10) {
this.clone();
}
}
lookAround() {
let closestPrey = null;
let closestDistance = null;
for (let i = 0; i < creatures.length; i++) {
if (creatures[i].type == Predator.type) continue;
let distance = Math.sqrt(
Math.pow(this.x - creatures[i].x, 2) + Math.pow(this.y - creatures[i].y, 2)
);
if (!closestDistance) {
closestDistance = distance;
}
if (!closestPrey) {
closestPrey = creatures[i];
}
if (distance < closestDistance) {
closestDistance = distance;
closestPrey = creatures[i];
}
}
return {
closestPrey,
closestDistance
};
}
clone() {
const {
x,
y
} = Helper.getRandomPosition();
const predator = new Predator(x, y);
creatures.push(predator);
}
}
class Prey extends Creature {
static type = PREY;
static color = "green";
constructor(x, y) {
super(Prey.type, x, y, Prey.color);
}
survive() {
let x = this.x;
let y = this.y;
const {
closestPredator,
closestDistance
} = this.lookAround();
if (!closestPredator) {
// roam
let movement = Helper.getRandomMovement();
this.move(x + movement.x, y + movement.y);
return;
}
if (closestDistance > 100) {
// roam
let movement = Helper.getRandomMovement();
this.move(x + movement.x, y + movement.y);
} else {
// run away
let movement = Helper.getMovementAway(this.x, this.y, closestPredator.x, closestPredator.y);
this.move(x + movement.x, y + movement.y);
}
if (closestDistance < 10) {
this.die();
}
}
lookAround() {
let closestPredator = null;
let closestDistance = null;
for (let i = 0; i < creatures.length; i++) {
if (creatures[i].type == Prey.type) continue;
let distance = Math.sqrt(
Math.pow(this.x - creatures[i].x, 2) + Math.pow(this.y - creatures[i].y,2)
);
if (!closestDistance) {
closestDistance = distance;
}
if (!closestPredator) {
closestPredator = creatures[i];
}
if (distance < closestDistance) {
closestDistance = distance;
closestPredator = creatures[i];
}
}
return {
closestPredator,
closestDistance
};
}
die() {
for (let i = 0; i < creatures.length; i++) {
if (creatures[i].x == this.x && creatures[i].y == this.y) {
creatures.splice(i, 1);
}
}
}
}
class Map {
constructor(width = 400, height = 400) {
this.width = width;
this.height = height;
}
clear() {
ctx.clearRect(0, 0, this.width, this.height);
}
draw() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, this.width, this.height);
}
}
class Helper {
static getRandomPosition() {
const x = Math.floor(Math.random() * AVAILABLE_WIDTH);
const y = Math.floor(Math.random() * AVAILABLE_HEIGHT);
return {
x,
y
};
}
static getRandomMovement() {
const random = Math.floor(Math.random() * 4);
if (random == 0) return {
x: 1,
y: 0
};
if (random == 1) return {
x: -1,
y: 0
};
if (random == 2) return {
x: 0,
y: 1
};
if (random == 3) return {
x: 0,
y: -1
};
return {
x: 0,
y: 0
};
}
static getDistance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
static getMovementTowards(x1, y1, x2, y2) {
let x = x1;
let y = y1;
if (x1 < x2) x += 1;
if (x1 > x2) x -= 1;
if (y1 < y2) y += 1;
if (y1 > y2) y -= 1;
return {
x,
y
};
}
static getMovementAway(x1, y1, x2, y2) {
let x = x1;
let y = y1;
if (x1 < x2) x -= 1;
if (x1 > x2) x += 1;
if (y1 < y2) y -= 1;
if (y1 > y2) y += 1;
return {
x,
y
};
}
}
class CreatureCollision {
constructor(prey, predator) {
this.prey = prey;
this.predator = predator;
}
check() {
if (this.predator.x == this.prey.x && this.predator.y == this.prey.y) {
return true;
}
return false;
}
}
const MAX_PREDATORS = 4;
const MAX_PREYS = 4;
const creatures = [];
for (let i = 0; i < MAX_PREDATORS; i++) {
const {
x,
y
} = Helper.getRandomPosition();
const predator = new Predator(x, y);
creatures.push(predator);
}
for (let i = 0; i < MAX_PREYS; i++) {
const {
x,
y
} = Helper.getRandomPosition();
const prey = new Prey(x, y);
creatures.push(prey);
}
let map = new Map(canvas.width, canvas.height);
function draw() {
map.clear();
map.draw();
for (let i = 0; i < creatures.length; i++) {
creatures[i].draw();
}
}
function update() {
for (let i = 0; i < creatures.length; i++) {
if (creatures[i].type == PREDATOR) {
creatures[i].hunt();
}
if (creatures[i].type == PREY) {
creatures[i].survive();
}
}
draw();
}
let frame = null;
function gameLoop() {
console.log('game loop');
try {
update();
} catch (e) {
console.log('error update', e);
stopGameLoop();
return;
}
frame = requestAnimationFrame(gameLoop);
console.log('frame', frame);
}
function stopGameLoop() {
console.log('stop game loop');
cancelAnimationFrame(frame);
}
gameLoop();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment