[pongwars]: misc improvements

- use tab instead of space
- warp out of bound balls
This commit is contained in:
dogeystamp 2024-01-30 15:35:49 -05:00
parent 32dba49a50
commit 2b6e9ec330

View File

@ -1,177 +1,176 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Pong wars | Koen van Gilst</title> <title>Pong wars | Koen van Gilst</title>
<link rel="canonical" href="https://pong-wars.koenvangilst.nl/" /> <link rel="canonical" href="https://pong-wars.koenvangilst.nl/" />
<style> <style>
html { html {
height: 100%; height: 100%;
} }
body { body {
height: 100%; height: 100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: linear-gradient(to bottom, #172b36 0%, #d9e8e3 100%); background: linear-gradient(to bottom, #172b36 0%, #d9e8e3 100%);
} }
#container { #container {
display: flex; display: flex;
width: 100%; width: 100%;
max-width: 750px; max-width: 750px;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
#pongCanvas { #pongCanvas {
display: block; display: block;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
margin-top: auto; margin-top: auto;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
} }
#score { #score {
font-family: monospace; font-family: monospace;
margin-top: 30px; margin-top: 30px;
font-size: 20px; font-size: 20px;
padding-left: 20px; padding-left: 20px;
color: #172b36; color: #172b36;
} }
#made { #made {
font-family: monospace; font-family: monospace;
margin-top: auto; margin-top: auto;
margin-bottom: 20px; margin-bottom: 20px;
font-size: 12px; font-size: 12px;
padding-left: 20px; padding-left: 20px;
} }
#made a { #made a {
color: #172b36; color: #172b36;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="container"> <div id="container">
<canvas id="pongCanvas" width="800" height="800"></canvas> <canvas id="pongCanvas" width="800" height="800"></canvas>
<div id="score"></div> <div id="score"></div>
<p id="made"> <p id="made">
made by <a href="https://koenvangilst.nl">Koen van Gilst</a> | source on made by <a href="https://koenvangilst.nl">Koen van Gilst</a> | source on
<a href="https://github.com/vnglst/pong-wars">github</a> <a href="https://github.com/vnglst/pong-wars">github</a>
</p> </p>
</div> </div>
</body> </body>
<script> <script>
// Based on: https://github.com/vnglst/pong-wars // Based on: https://github.com/vnglst/pong-wars
// Idea for Pong wars: https://twitter.com/nicolasdnl/status/1749715070928433161 // Idea for Pong wars: https://twitter.com/nicolasdnl/status/1749715070928433161
const canvas = document.getElementById("pongCanvas"); const canvas = document.getElementById("pongCanvas");
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
const scoreElement = document.getElementById("score"); const scoreElement = document.getElementById("score");
const startTime = new Date(); const startTime = new Date();
var suddenDeathCoeff = 0; var suddenDeathCoeff = 0;
const teams = [ const teams = [
{ {
name: "red", name: "red",
color: "#ff5555", color: "#ff5555",
backgroundColor: "#aa0000", backgroundColor: "#aa0000",
x: 256, x: 256,
y: 256, y: 256,
dx: 8, dx: 8,
dy: 8, dy: 8,
score: 0, score: 0,
}, },
{ {
name: "blue", name: "blue",
color: "#5555ff", color: "#5555ff",
backgroundColor: "#0000aa", backgroundColor: "#200199",
x: 768, x: 768,
y: 256, y: 256,
dx: -8, dx: -8,
dy: 8, dy: 8,
score: 0, score: 0,
}, },
{ {
name: "green", name: "green",
color: "#a7f070", color: "#a7f070",
backgroundColor: "#00aa00", backgroundColor: "#00aa00",
x: 256, x: 256,
y: 768, y: 768,
dx: 8, dx: 8,
dy: -8, dy: -8,
score: 0, score: 0,
}, },
{ {
name: "yellow", name: "yellow",
color: "#ffff55", color: "#ffff55",
backgroundColor: "#aa5500", backgroundColor: "#aa5500",
x: 768, x: 768,
y: 768, y: 768,
dx: -8, dx: -8,
dy: -8, dy: -8,
score: 0, score: 0,
}, },
{ {
name: "white", name: "white",
color: "#ffffff", color: "#ffffff",
backgroundColor: "#aaaaaa", backgroundColor: "#aaaaaa",
x: 400, x: 400,
y: 768, y: 768,
dx: -9, dx: -9,
dy: -9, dy: -9,
score: 0, score: 0,
}, },
{ {
name: "black", name: "black",
color: "#333333", color: "#333333",
backgroundColor: "#000000", backgroundColor: "#000000",
x: 400, x: 400,
y: 300, y: 300,
dx: -8, dx: -8,
dy: -8, dy: -8,
score: 0, score: 0,
}, },
{ {
name: "ourple", name: "ourple",
color: "#dd33dd", color: "#dd33dd",
backgroundColor: "#aa00aa", backgroundColor: "#aa00aa",
x: 400, x: 400,
y: 768, y: 768,
dx: -8, dx: -8,
dy: -8, dy: -8,
score: 0, score: 0,
}, },
{ {
name: "gray", name: "gray",
color: "#dddddd", color: "#dddddd",
backgroundColor: "#333333", backgroundColor: "#333333",
x: 400, x: 400,
y: 768, y: 768,
dx: -8, dx: -8,
dy: -8, dy: -8,
score: 0, score: 0,
}, },
]; ];
var state = teams.map((team) => ({ var state = teams.map((team) => ({
elim: false, elim: false,
boost: 1,
boostEnabled: false, boostEnabled: false,
})) }))
const squareSize = 16; const squareSize = 16;
const numSquaresX = canvas.width / squareSize; const numSquaresX = canvas.width / squareSize;
const numSquaresY = canvas.height / squareSize; const numSquaresY = canvas.height / squareSize;
let squares = []; let squares = [];
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", (event) => {
const key = parseInt(event.key) const key = parseInt(event.key)
@ -184,17 +183,17 @@
state[key].boostEnabled = false; state[key].boostEnabled = false;
}) })
for (let i = 0; i < numSquaresX; i++) { for (let i = 0; i < numSquaresX; i++) {
squares[i] = []; squares[i] = [];
const t = randInt(0, teams.length-1); const t = randInt(0, teams.length-1);
for (let j = 0; j < numSquaresY; j++) { for (let j = 0; j < numSquaresY; j++) {
squares[i][j] = t; squares[i][j] = t;
} }
} }
function randomNum(min, max) { function randomNum(min, max) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
function randInt(min, max) { function randInt(min, max) {
return Math.floor(randomNum(min, max+1)) return Math.floor(randomNum(min, max+1))
@ -204,24 +203,24 @@
return ((new Date()) - startTime)/1000 return ((new Date()) - startTime)/1000
} }
updateScoreElement(); updateScoreElement();
function drawBall(x, y, color) { function drawBall(x, y, color) {
ctx.beginPath(); ctx.beginPath();
ctx.arc(x, y, squareSize / 2, 0, Math.PI * 2, false); ctx.arc(x, y, squareSize / 2, 0, Math.PI * 2, false);
ctx.fillStyle = color; ctx.fillStyle = color;
ctx.fill(); ctx.fill();
ctx.closePath(); ctx.closePath();
} }
function drawSquares() { function drawSquares() {
for (let i = 0; i < numSquaresX; i++) { for (let i = 0; i < numSquaresX; i++) {
for (let j = 0; j < numSquaresY; j++) { for (let j = 0; j < numSquaresY; j++) {
ctx.fillStyle = teams[squares[i][j]].backgroundColor; ctx.fillStyle = teams[squares[i][j]].backgroundColor;
ctx.fillRect(i * squareSize, j * squareSize, squareSize, squareSize); ctx.fillRect(i * squareSize, j * squareSize, squareSize, squareSize);
} }
} }
} }
function clamp(min, max, num) { function clamp(min, max, num) {
return Math.min(max, Math.max(min, num)) return Math.min(max, Math.max(min, num))
@ -231,115 +230,132 @@
return v*x + (1-v)*y; return v*x + (1-v)*y;
} }
function updateSquareAndBounce(x, y, dx, dy, color) { function updateSquareAndBounce(x, y, dx, dy, color) {
if (state[color].elim) return if (state[color].elim) return
if (Math.max(Math.abs(dx), Math.abs(dy)) < 0.02) state[color].elim = true if (Math.max(Math.abs(dx), Math.abs(dy)) < 0.02) state[color].elim = true
let updatedDx = dx;
let updatedDy = dy;
// Check multiple points around the ball's circumference if (Math.min(x, y) < 0 || isNaN(x) || isNaN(y)) {
for (let angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) { console.log(`warped ${teams[color].name}`)
let checkX = x + Math.cos(angle) * (squareSize / 2); teams[color].x = canvas.width * 0.1;
let checkY = y + Math.sin(angle) * (squareSize / 2); teams[color].y = canvas.height * 0.1;
dx = 4;
dy = 4;
}
let i = Math.floor(checkX / squareSize); if (x > canvas.width || y > canvas.height) {
let j = Math.floor(checkY / squareSize); console.log(`warped ${teams[color].name}`)
teams[color].x = canvas.width * 0.9;
teams[color].y = canvas.height * 0.9;
dx = -4;
dy = -4;
}
if (i >= 0 && i < numSquaresX && j >= 0 && j < numSquaresY) { let updatedDx = dx;
if (squares[i][j] !== color) { let updatedDy = dy;
squares[i][j] = color;
// Determine bounce direction based on the angle // Check multiple points around the ball's circumference
if (Math.abs(Math.cos(angle)) > Math.abs(Math.sin(angle))) { for (let angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) {
updatedDx = -updatedDx; let checkX = x + Math.cos(angle) * (squareSize / 2);
} else { let checkY = y + Math.sin(angle) * (squareSize / 2);
updatedDy = -updatedDy;
}
updatedDx += randomNum(-0.15, mix(0.153, 0.15, suddenDeathCoeff)); let i = Math.floor(checkX / squareSize);
updatedDy += randomNum(-0.15, 0.15); let j = Math.floor(checkY / squareSize);
const nTeams = state.map(t => !t.elim).filter(Boolean).length
const coeff = Math.min((teams[color].score/(squares.length**2/nTeams/mix(1.2, 4, suddenDeathCoeff))), 1.00);
updatedDx *= coeff;
updatedDy *= coeff;
const speedLim = mix(100, 18, suddenDeathCoeff)
const norm = (updatedDx**2 + updatedDy**2)**(1/2)
const scalar = Math.max(norm/speedLim, 1)
updatedDx *= scalar;
updatedDy *= scalar;
} if (i >= 0 && i < numSquaresX && j >= 0 && j < numSquaresY) {
} if (squares[i][j] !== color) {
} squares[i][j] = color;
const theta = (Math.PI/180)*10; // Determine bounce direction based on the angle
const ct = Math.cos(theta); if (Math.abs(Math.cos(angle)) > Math.abs(Math.sin(angle))) {
const st = Math.sin(theta); updatedDx = -updatedDx;
if (state[color].boostEnabled) { } else {
const rotDx = (ct * updatedDx - st * updatedDy); updatedDy = -updatedDy;
const rotDy = (st * updatedDx + ct * updatedDy); }
updatedDx = rotDx;
updatedDy = rotDy;
}
return { dx: updatedDx, dy: updatedDy }; updatedDx += randomNum(-0.15, mix(0.153, 0.15, suddenDeathCoeff));
} updatedDy += randomNum(-0.15, 0.15);
const nTeams = state.map(t => !t.elim).filter(Boolean).length
const coeff = Math.min((teams[color].score/(squares.length**2/nTeams/mix(1.2, 4, suddenDeathCoeff))), 1.00);
updatedDx *= coeff;
updatedDy *= coeff;
const speedLim = mix(100, 18, suddenDeathCoeff)
const norm = (updatedDx**2 + updatedDy**2)**(1/2)
const scalar = Math.max(norm/speedLim, 1)
updatedDx *= scalar;
updatedDy *= scalar;
function updateScoreElement() { }
if (!squares) { }
return; }
}
teams.forEach((t) => (t.score = 0));
for (let i = 0; i < numSquaresX; i++) {
for (let j = 0; j < numSquaresY; j++) {
teams[squares[i][j]].score++;
}
}
for (let i = 0; i < teams.length; i++) {
if (teams[i].score <= 4) {
state[i].elim = true
}
}
scoreElement.textContent = teams const theta = (Math.PI/180)*10;
.map((t) => `${t.name} ${t.score}`) const ct = Math.cos(theta);
.join(" | ") + (suddenDeathCoeff > 0.1 ? " | sudden death!" : ""); const st = Math.sin(theta);
} if (state[color].boostEnabled) {
const rotDx = (ct * updatedDx - st * updatedDy);
const rotDy = (st * updatedDx + ct * updatedDy);
updatedDx = rotDx;
updatedDy = rotDy;
}
function draw() { return { dx: updatedDx, dy: updatedDy };
ctx.clearRect(0, 0, canvas.width, canvas.height); }
drawSquares();
for (let i = 0; i < teams.length; i++) { function updateScoreElement() {
if (state[i].elim) continue if (!squares) {
const t = teams[i]; return;
drawBall(t.x, t.y, t.color); }
let bounce = updateSquareAndBounce(t.x, t.y, t.dx, t.dy, i); teams.forEach((t) => (t.score = 0));
t.dx = bounce.dx; for (let i = 0; i < numSquaresX; i++) {
t.dy = bounce.dy; for (let j = 0; j < numSquaresY; j++) {
teams[squares[i][j]].score++;
}
}
for (let i = 0; i < teams.length; i++) {
if (teams[i].score <= 4) {
state[i].elim = true
}
}
if ( scoreElement.textContent = teams
t.x + t.dx > canvas.width - squareSize / 2 || .map((t) => `${t.name} ${t.score}`)
t.x + t.dx < squareSize / 2 .join(" | ") + (suddenDeathCoeff > 0.1 ? " | sudden death!" : "");
) { }
t.dx = -t.dx;
}
if (
t.y + t.dy > canvas.height - squareSize / 2 ||
t.y + t.dy < squareSize / 2
) {
t.dy = -t.dy;
}
t.x += t.dx; function draw() {
t.y += t.dy; ctx.clearRect(0, 0, canvas.width, canvas.height);
} drawSquares();
updateScoreElement(); for (let i = 0; i < teams.length; i++) {
// suddenDeathCoeff = Math.min((elapsedSec()/60/3)**15, 1); if (state[i].elim) continue
requestAnimationFrame(draw); const t = teams[i];
} drawBall(t.x, t.y, t.color);
let bounce = updateSquareAndBounce(t.x, t.y, t.dx, t.dy, i);
t.dx = bounce.dx;
t.dy = bounce.dy;
requestAnimationFrame(draw); if (
</script> t.x + t.dx > canvas.width - squareSize / 2 ||
t.x + t.dx < squareSize / 2
) {
t.dx = -t.dx;
}
if (
t.y + t.dy > canvas.height - squareSize / 2 ||
t.y + t.dy < squareSize / 2
) {
t.dy = -t.dy;
}
t.x += t.dx;
t.y += t.dy;
}
updateScoreElement();
// suddenDeathCoeff = Math.min((elapsedSec()/60/3)**15, 1);
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
</script>
</html> </html>