codelion's picture
Upload flappy_bird.html with huggingface_hub
2e4488d verified
Raw
History Blame Contribute Delete
7.87 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #1a1a2e;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
border: 4px solid #2c3e50;
border-radius: 8px;
background: #4ec0ca;
box-shadow: 0 8px 16px rgba(0,0,0,0.3);
cursor: pointer;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="600"></canvas>
<script>
// ========== CONSTANTS AND VARIABLES ==========
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
const BIRD_X = 100;
const BIRD_RADIUS = 12;
const BIRD_COLOR = '#ffcc00';
const BIRD_ACCELERATION = 0.45;
const BIRD_JUMP_VELOCITY = -8;
const GRAVITY = 0.5;
const PIPE_WIDTH = 60;
const PIPE_GAP = 140;
const PIPE_SPACING = 220;
const PIPE_SPEED = 2.4;
const GROUND_HEIGHT = 80;
const GROUND_COLOR = '#8b5a2b';
const PIPE_COLOR = '#4a9e4a';
const PIPE_BORDER_COLOR = '#2d7a2d';
const SCORE_COLOR = '#ffffff';
const GAME_OVER_COLOR = '#ff0000';
const RESTART_COLOR = '#ffffff';
const GAME_OVER_FONT = 'bold 28px sans-serif';
const SCORE_FONT = 'bold 20px sans-serif';
const RESTART_FONT = 'bold 16px sans-serif';
// ========== STATE ==========
let bird = { x: 80, y: 250, vy: 0, width: 34, height: 24 };
let pipes = [];
let groundY = 560; // top of ground line
let pipeGap = 140;
let pipeSpacing = 200;
let score = 0;
let gameOver = false;
let gameOverScreen = false;
let frames = 0;
let restartRequested = false;
let lastTime = 0;
let pipeSpawnCounter = 0;
let pipeSpawnInterval = 90; // frames between pipes
let birdFlap = false;
let birdFlapTime = 0;
// Ground line
const GROUND_Y = 560;
const BIRD_HEIGHT = 20;
const BIRD_WIDTH = 20;
// ========== GAME STATE ==========
let gameState = 'play'; // 'play' | 'gameover'
let pipeFrameCounter = 0;
let birdY = 250;
let birdVelocity = 0;
let pipeList = [];
let frameCount = 0;
let gameOverScreenShown = false;
// ========== CONSTANTS (reuse) ==========
const CANVAS_WIDTH = 400;
const CANVAS_HEIGHT = 600;
const JUMP_VELOCITY = -8;
// ========== INITIALIZE ==========
canvas.width = 400;
canvas.height = 600;
// ========== GAME STATE ==========
let frame = 0;
let pipeInterval = 90; // frames between pipes
// ========== RESTART ==========
function restart() {
bird = { x: BIRD_X, y: 300, vy: 0, width: BIRD_WIDTH, height: BIRD_HEIGHT };
pipes = [];
score = 0;
gameOver = false;
gameOverScreen = false;
frame = 0;
pipeSpawnCounter = 0;
// reset any other state
cancelAnimationFrame(lastFrameId);
requestAnimationFrame(gameLoop);
}
// --- shims: the model declared these constants in UPPER_CASE and then
// --- referenced the camelCase forms, and called a helper it never defined.
const gravity = GRAVITY;
const pipeSpeed = PIPE_SPEED;
const pipeWidth = PIPE_WIDTH;
let gameOverScreenActive = false;
let pipeCounter = 0;
function randomRange(lo, hi) { return Math.random() * (hi - lo) + lo; }
// ========== GAME LOOP ==========
let lastFrameId;
function gameLoop(timestamp) {
if (!lastTime) lastTime = timestamp;
let delta = timestamp - lastTime;
lastTime = timestamp;
if (delta > 200) delta = 200; // avoid huge jumps
// Update
update(delta / 1000);
// Draw
draw();
// Request next frame
requestAnimationFrame(gameLoop);
}
// ========== UPDATE ==========
function update(dt) {
frame++;
// Bird physics
bird.vy += 0.45 * dt * 60;
bird.y += bird.vy * dt * 60;
if (bird.y > groundY) {
bird.y = groundY;
if (!gameOver) {
gameOver = true;
gameOverScreenActive = true;
}
}
// Pipes
for (let i = 0; i < pipes.length; i++) {
const p = pipes[i];
p.x -= p.speed * dt * 60;
if (p.x + p.width < 0) {
pipes.splice(i, 1);
i--;
score++;
}
}
// Spawn
if (frame % 100 === 0) {
const top = randomRange(50, 300);
const gap = 140;
const width = 60;
pipes.push({
x: canvas.width,
top: top,
bottom: top + gap,
width: width,
speed: 2.4,
passed: false
});
}
// Collision
if (!gameOver) {
for (let i = 0; i < pipes.length; i++) {
const p = pipes[i];
if (bird.x + 20 > p.x && bird.x + 20 < p.x + p.width &&
(bird.y < p.top || bird.y + 20 > p.bottom)) {
gameOver = true;
break;
}
}
if (bird.y + 20 > groundY) {
gameOver = true;
}
}
// Score
if (!gameOver) {
for (const p of pipes) {
if (p.passed === false && bird.x > p.x + p.width) {
score++;
p.passed = true;
}
}
}
}
// ========== DRAW ==========
function draw() {
// Clear
ctx.clearRect(0, 0, WIDTH, HEIGHT);
// Sky
ctx.fillStyle = '#87CEEB';
ctx.fillRect(0, 0, WIDTH, 200);
// Ground
ctx.fillStyle = '#c9a96e';
ctx.fillRect(0, HEIGHT - 80, WIDTH, 80);
// Ground lines
ctx.strokeStyle = '#7a5c3c';
ctx.lineWidth = 2;
for (let i = 0; i < 20; i++) {
ctx.beginPath();
ctx.moveTo(i * 40, HEIGHT - 40);
ctx.lineTo(i * 40 + 20, HEIGHT - 80);
ctx.stroke();
}
// Pipes
for (let i = 0; i < pipes.length; i++) {
const p = pipes[i];
const gap = 150;
const topHeight = p.top;
const bottomY = p.bottom;
// top pipe
ctx.fillStyle = '#4a9e4a';
ctx.fillRect(p.x, 0, p.width, topHeight);
ctx.fillStyle = '#2d7a2d';
ctx.fillRect(p.x - 5, topHeight - 20, p.width + 10, 20);
// bottom pipe
ctx.fillStyle = '#4a9e4a';
ctx.fillRect(p.x, bottomY, p.width, HEIGHT - bottomY);
ctx.fillStyle = '#2d7a2d';
ctx.fillRect(p.x - 5, bottomY, p.width + 10, 20);
// pipe highlight
ctx.fillStyle = '#6ccb6c';
ctx.fillRect(p.x + 5, 0, 10, topHeight - 20);
ctx.fillRect(p.x + 5, bottomY, 10, HEIGHT - bottomY);
}
// Ground
ctx.fillStyle = '#c9a96e';
ctx.fillRect(0, HEIGHT - 80, WIDTH, 80);
// Grass lines
ctx.strokeStyle = '#8b6f3e';
ctx.lineWidth = 2;
for (let i = 0; i < WIDTH; i += 15) {
ctx.beginPath();
ctx.moveTo(i, HEIGHT - 80);
ctx.lineTo(i + 10, HEIGHT - 70);
ctx.stroke();
}
// Bird
ctx.fillStyle = '#ffcc00';
ctx.strokeStyle = '#b8860b';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(BIRD_X, bird.y, 12, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Eye
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(BIRD_X + 5, bird.y - 5, 3, 0, Math.PI * 2);
ctx.fill();
// Beak
ctx.fillStyle = '#ff6600';
ctx.beginPath();
ctx.moveTo(BIRD_X + 10, bird.y - 4);
ctx.lineTo(BIRD_X + 18, bird.y);
ctx.lineTo(BIRD_X + 10, bird.y + 4);
ctx.fill();
// Score
ctx.font = '20px Arial';
ctx.fillStyle = 'white';
ctx.fillText(score, 20, 40);
// Game over
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.font = '30px Arial';
ctx.fillStyle = 'white';
ctx.fillText('Game Over', 80, 250);
ctx.font = '16px Arial';
ctx.fillText('Press Space or Click to restart', 70, 300);
}
}
// ========== RESTART ==========
function restart() {
bird.y = 150;
bird.vy = 0;
pipes = [];
score = 0;
gameOver = false;
frame = 0;
// Reset any other state
}
// ========== KEY/CLICK ==========
document.addEventListener('keydown', function(e) {
if (e.code === 'Space') {
e.preventDefault();
if (gameOver) {
restart();
} else {
bird.vy = -4.2;
}
}
});
canvas.addEventListener('click', function(e) {
if (gameOver) {
restart();
} else {
bird.vy = -4.2;
}
});
// start the game
restart();
requestAnimationFrame(gameLoop);</script>
</body>
</html>