anycoder-ee4554f5 / index.html
Annu72772's picture
Upload folder using huggingface_hub
4d0bd1b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carrom Board Game</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-red: #8B0000;
--light-brown: #D2B48C;
--orange: #FFA500;
--black: #000000;
--purple: #800080;
--green: #008000;
--white: #FFFFFF;
--gold: #FFD700;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
body {
background: linear-gradient(to bottom, var(--primary-red), #A0522D);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
color: white;
overflow-x: hidden;
}
.header {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 0;
margin-bottom: 10px;
}
.player-info {
display: flex;
align-items: center;
gap: 10px;
}
.player-avatar {
width: 50px;
height: 50px;
border-radius: 8px;
object-fit: cover;
border: 2px solid var(--gold);
}
.player-name {
font-size: 18px;
font-weight: bold;
}
.player-score {
font-size: 24px;
font-weight: bold;
}
.disc-icon {
width: 20px;
height: 20px;
border-radius: 50%;
margin-left: 5px;
}
.pot-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
}
.pot-icon {
font-size: 30px;
color: var(--gold);
text-shadow: 0 0 5px rgba(255, 215, 0, 0.7);
}
.pot-amount {
font-size: 18px;
font-weight: bold;
}
.game-container {
width: 100%;
max-width: 500px;
aspect-ratio: 1/1;
position: relative;
background-color: var(--light-brown);
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
overflow: hidden;
background-image:
radial-gradient(circle at center, rgba(0,0,0,0.1) 1px, transparent 1px),
linear-gradient(45deg, rgba(0,0,0,0.05) 1px, transparent 1px),
linear-gradient(-45deg, rgba(0,0,0,0.05) 1px, transparent 1px);
background-size: 100px 100px, 20px 20px, 20px 20px;
}
.board-border {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 15px solid var(--orange);
border-radius: 10px;
pointer-events: none;
}
.pocket {
position: absolute;
width: 40px;
height: 40px;
background-color: var(--black);
border-radius: 50%;
z-index: 10;
}
.pocket::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
}
.bumper {
position: absolute;
width: 20px;
height: 20px;
background-color: var(--orange);
border-radius: 50%;
z-index: 5;
}
.center-circle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80px;
height: 80px;
border: 2px dashed rgba(0, 0, 0, 0.3);
border-radius: 50%;
z-index: 2;
}
.striker-zone {
position: absolute;
bottom: 20px;
left: 20px;
width: 80px;
height: 40px;
border: 2px dashed rgba(255, 255, 255, 0.7);
border-radius: 40px 40px 0 0;
background-color: rgba(255, 255, 255, 0.1);
z-index: 3;
}
.striker {
position: absolute;
width: 25px;
height: 25px;
background-color: var(--white);
border-radius: 50%;
background-image: radial-gradient(circle, rgba(255,255,255,0.8) 2px, transparent 2px);
background-size: 5px 5px;
z-index: 4;
cursor: grab;
touch-action: none;
box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
}
.disc {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
z-index: 2;
transition: transform 0.1s linear;
}
.purple-disc {
background-color: var(--purple);
box-shadow: 0 0 5px rgba(128, 0, 128, 0.7);
}
.green-disc {
background-color: var(--green);
box-shadow: 0 0 5px rgba(0, 128, 0, 0.7);
}
.white-disc {
background-color: var(--white);
box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
}
.queen-disc {
background-color: #FF0000;
box-shadow: 0 0 10px rgba(255, 0, 0, 0.9);
}
.trajectory-line {
position: absolute;
height: 2px;
background-color: rgba(255, 255, 255, 0.7);
z-index: 1;
pointer-events: none;
}
.trajectory-arrow {
position: absolute;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 12px solid var(--orange);
z-index: 1;
pointer-events: none;
}
.controls {
margin-top: 20px;
display: flex;
gap: 15px;
flex-wrap: wrap;
justify-content: center;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 5px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
background-color: var(--orange);
color: white;
}
.btn:hover {
background-color: #FF8C00;
transform: scale(1.05);
}
.btn:active {
transform: scale(0.95);
}
.game-status {
margin-top: 15px;
padding: 10px;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 5px;
text-align: center;
font-size: 16px;
}
.anycoder-link {
position: fixed;
bottom: 10px;
right: 10px;
color: white;
text-decoration: none;
font-size: 12px;
background-color: rgba(0, 0, 0, 0.3);
padding: 5px 10px;
border-radius: 5px;
z-index: 100;
}
@media (max-width: 400px) {
.player-info {
flex-direction: column;
align-items: flex-start;
}
.player-avatar {
width: 40px;
height: 40px;
}
.player-name {
font-size: 16px;
}
.player-score {
font-size: 20px;
}
.pot-icon {
font-size: 25px;
}
}
</style>
</head>
<body>
<div class="header">
<div class="player-info">
<img src="https://via.placeholder.com/50x50/808080/FFFFFF?text=A" alt="Ash Avatar" class="player-avatar">
<div>
<div class="player-name">Ash</div>
<div class="player-score">0 <div class="disc-icon" style="background-color: var(--purple);"></div></div>
</div>
</div>
<div class="pot-container">
<i class="fas fa-coins pot-icon"></i>
<div class="pot-amount">1000</div>
</div>
<div class="player-info">
<div>
<div class="player-name">MD</div>
<div class="player-score">1 <div class="disc-icon" style="background-color: var(--green);"></div></div>
</div>
<img src="https://via.placeholder.com/50x50/D2B48C/000000?text=MD" alt="MD Avatar" class="player-avatar">
</div>
</div>
<div class="game-container">
<div class="board-border"></div>
<!-- Pockets -->
<div class="pocket" style="top: 10px; left: 10px;"></div>
<div class="pocket" style="top: 10px; right: 10px;"></div>
<div class="pocket" style="bottom: 10px; left: 10px;"></div>
<div class="pocket" style="bottom: 10px; right: 10px;"></div>
<!-- Bumpers -->
<div class="bumper" style="top: 30px; left: 50%; transform: translateX(-50%);"></div>
<div class="bumper" style="top: 50%; left: 30px; transform: translateY(-50%);"></div>
<div class="bumper" style="bottom: 30px; left: 50%; transform: translateX(-50%);"></div>
<div class="bumper" style="top: 50%; right: 30px; transform: translateY(-50%);"></div>
<!-- Center Circle -->
<div class="center-circle"></div>
<!-- Striker Zone -->
<div class="striker-zone"></div>
<!-- Striker -->
<div class="striker" id="striker"></div>
<!-- Trajectory Line and Arrow -->
<div class="trajectory-line" id="trajectoryLine"></div>
<div class="trajectory-arrow" id="trajectoryArrow"></div>
<!-- Discs will be added by JavaScript -->
</div>
<div class="controls">
<button class="btn" id="resetBtn">Reset Game</button>
<button class="btn" id="aiBtn">AI Opponent</button>
<button class="btn" id="shootBtn">Shoot</button>
</div>
<div class="game-status" id="gameStatus">
Player Ash's turn. Drag the striker to aim and shoot!
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">Built with anycoder</a>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Game state
const gameState = {
currentPlayer: 'Ash',
scores: {
Ash: 0,
MD: 0
},
discs: [],
striker: {
x: 30,
y: 85,
angle: 0,
power: 0,
isDragging: false
},
queenPocketed: false,
gameOver: false
};
// DOM elements
const gameContainer = document.querySelector('.game-container');
const striker = document.getElementById('striker');
const trajectoryLine = document.getElementById('trajectoryLine');
const trajectoryArrow = document.getElementById('trajectoryArrow');
const gameStatus = document.getElementById('gameStatus');
const resetBtn = document.getElementById('resetBtn');
const aiBtn = document.getElementById('aiBtn');
const shootBtn = document.getElementById('shootBtn');
const ashScore = document.querySelector('.player-info:first-child .player-score');
const mdScore = document.querySelector('.player-info:last-child .player-score');
// Initialize the game
function initGame() {
// Clear existing discs
document.querySelectorAll('.disc').forEach(disc => disc.remove());
gameState.discs = [];
// Create discs
createDiscs();
// Reset striker position
striker.style.left = `${gameState.striker.x}%`;
striker.style.top = `${gameState.striker.y}%`;
// Reset game state
gameState.currentPlayer = 'Ash';
gameState.queenPocketed = false;
gameState.gameOver = false;
// Update UI
updateGameStatus();
updateScores();
}
// Create discs
function createDiscs() {
const centerX = 50;
const centerY = 50;
const radius = 15;
const discSize = 20;
// Create queen disc (red)
const queen = document.createElement('div');
queen.className = 'disc queen-disc';
queen.style.left = `${centerX}%`;
queen.style.top = `${centerY}%`;
queen.style.width = `${discSize}px`;
queen.style.height = `${discSize}px`;
queen.style.transform = 'translate(-50%, -50%)';
queen.dataset.type = 'queen';
gameContainer.appendChild(queen);
gameState.discs.push({
element: queen,
x: centerX,
y: centerY,
type: 'queen',
pocketed: false
});
// Create player discs in a circle around the queen
const discCount = 9;
const angleStep = (2 * Math.PI) / discCount;
for (let i = 0; i < discCount; i++) {
const angle = i * angleStep;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
// Alternate between purple and green/white discs
const discType = i % 2 === 0 ? 'purple' : (i % 3 === 0 ? 'green' : 'white');
const disc = document.createElement('div');
disc.className = `disc ${discType}-disc`;
disc.style.left = `${x}%`;
disc.style.top = `${y}%`;
disc.style.width = `${discSize}px`;
disc.style.height = `${discSize}px`;
disc.style.transform = 'translate(-50%, -50%)';
disc.dataset.type = discType;
gameContainer.appendChild(disc);
gameState.discs.push({
element: disc,
x: x,
y: y,
type: discType,
pocketed: false
});
}
}
// Update game status display
function updateGameStatus() {
if (gameState.gameOver) {
const winner = gameState.scores.Ash > gameState.scores.MD ? 'Ash' : 'MD';
gameStatus.textContent = `Game Over! ${winner} wins!`;
return;
}
gameStatus.textContent = `Player ${gameState.currentPlayer}'s turn. Drag the striker to aim and shoot!`;
}
// Update scores display
function updateScores() {
ashScore.innerHTML = `${gameState.scores.Ash} <div class="disc-icon" style="background-color: var(--purple);"></div>`;
mdScore.innerHTML = `${gameState.scores.MD} <div class="disc-icon" style="background-color: var(--green);"></div>`;
}
// Check if a disc is pocketed
function checkPocketed(disc) {
const pockets = [
{ x: 5, y: 5 }, // Top-left
{ x: 95, y: 5 }, // Top-right
{ x: 5, y: 95 }, // Bottom-left
{ x: 95, y: 95 } // Bottom-right
];
for (const pocket of pockets) {
const distance = Math.sqrt(
Math.pow(disc.x - pocket.x, 2) +
Math.pow(disc.y - pocket.y, 2)
);
if (distance < 3) { // Pocket radius is about 3% of container
return true;
}
}
return false;
}
// Handle disc movement physics
function moveDiscs() {
const friction = 0.98;
const gravity = 0.1;
let allStopped = true;
gameState.discs.forEach(disc => {
if (disc.pocketed) return;
const element = disc.element;
// Get current velocity from data attributes
let vx = parseFloat(element.dataset.vx) || 0;
let vy = parseFloat(element.dataset.vy) || 0;
// Apply friction
vx *= friction;
vy *= friction;
// Apply gravity (slight downward pull)
vy += gravity;
// Update position
disc.x += vx;
disc.y += vy;
// Check boundaries
const containerWidth = gameContainer.offsetWidth;
const containerHeight = gameContainer.offsetHeight;
const discSize = parseInt(element.style.width);
// Bounce off walls
if (disc.x - discSize/2 < 5 || disc.x + discSize/2 > 95) {
vx *= -0.8; // Bounce with some energy loss
}
if (disc.y - discSize/2 < 5 || disc.y + discSize/2 > 95) {
vy *= -0.8; // Bounce with some energy loss
}
// Check if pocketed
if (checkPocketed(disc)) {
disc.pocketed = true;
element.style.display = 'none';
// Update score
if (disc.type === 'queen') {
gameState.queenPocketed = true;
} else if (disc.type === 'purple' && gameState.currentPlayer === 'Ash') {
gameState.scores.Ash++;
} else if ((disc.type === 'green' || disc.type === 'white') && gameState.currentPlayer === 'MD') {
gameState.scores.MD++;
}
updateScores();
}
// Update element position
element.style.left = `${disc.x}%`;
element.style.top = `${disc.y}%`;
// Store velocity for next frame
element.dataset.vx = vx;
element.dataset.vy = vy;
// Check if any disc is still moving
if (Math.abs(vx) > 0.01 || Math.abs(vy) > 0.01) {
allStopped = false;
}
});
// If all discs stopped, switch player
if (allStopped && !gameState.gameOver) {
gameState.currentPlayer = gameState.currentPlayer === 'Ash' ? 'MD' : 'Ash';
updateGameStatus();
// Check for game over conditions
if (gameState.queenPocketed) {
// Simple game over condition: queen pocketed and player has at least one disc
const currentPlayerDiscs = gameState.discs.filter(d =>
!d.pocketed &&
((d.type === 'purple' && gameState.currentPlayer === 'Ash') ||
((d.type === 'green' || d.type === 'white') && gameState.currentPlayer === 'MD'))
);
if (currentPlayerDiscs.length === 0) {
gameState.gameOver = true;
updateGameStatus();
}
}
} else if (!allStopped) {
// Continue animation if discs are still moving
requestAnimationFrame(moveDiscs);
}
}
// Handle striker drag
function handleStrikerDrag(e) {
if (gameState.gameOver) return;
const rect = gameContainer.getBoundingClientRect();
const x = ((e.clientX || e.touches[0].clientX) - rect.left) / rect.width * 100;
const y = ((e.clientY || e.touches[0].clientY) - rect.top) / rect.height * 100;
// Calculate angle and distance from striker zone center
const zoneCenterX = 30;
const zoneCenterY = 85;
const dx = x - zoneCenterX;
const dy = y - zoneCenterY;
const distance = Math.sqrt(dx * dx + dy * dy);
// Limit striker movement to within the striker zone
if (distance <= 15) { // 15% radius for striker zone
striker.style.left = `${x}%`;
striker.style.top = `${y}%`;
// Update striker position in game state
gameState.striker.x = x;
gameState.striker.y = y;
// Calculate angle for trajectory
const angle = Math.atan2(dy, dx);
gameState.striker.angle = angle;
// Calculate power based on distance from center
const power = Math.min(distance / 15 * 100, 100);
gameState.striker.power = power;
// Update trajectory line
updateTrajectory();
}
}
// Update trajectory line
function updateTrajectory() {
const angle = gameState.striker.angle;
const power = gameState.striker.power;
// Calculate end point of trajectory
const length = 50 + power * 0.5; // Base length + power factor
const endX = gameState.striker.x + Math.cos(angle) * length;
const endY = gameState.striker.y + Math.sin(angle) * length;
// Update trajectory line
trajectoryLine.style.left = `${gameState.striker.x}%`;
trajectoryLine.style.top = `${gameState.striker.y}%`;
trajectoryLine.style.width = `${length}%`;
trajectoryLine.style.transform = `rotate(${angle}rad)`;
trajectoryLine.style.transformOrigin = '0 50%';
// Update trajectory arrow
trajectoryArrow.style.left = `${endX}%`;
trajectoryArrow.style.top = `${endY}%`;
trajectoryArrow.style.transform = `rotate(${angle + Math.PI/2}rad) translateY(-50%)`;
}
// Handle shoot
function handleShoot() {
if (gameState.gameOver) return;
// Calculate velocity based on angle and power
const angle = gameState.striker.angle;
const power = gameState.striker.power / 20; // Scale down power
const vx = Math.cos(angle) * power;
const vy = Math.sin(angle) * power;
// Apply velocity to striker
striker.dataset.vx = vx;
striker.dataset.vy = vy;
// Add striker to discs for physics
gameState.discs.push({
element: striker,
x: gameState.striker.x,
y: gameState.striker.y,
type: 'striker',
pocketed: false
});
// Start disc movement
moveDiscs();
// Hide trajectory
trajectoryLine.style.display = 'none';
trajectoryArrow.style.display = 'none';
}
// AI opponent move
function aiMove() {
if (gameState.currentPlayer !== 'MD' || gameState.gameOver) return;
gameStatus.textContent = "AI is thinking...";
// Simple AI: target a random disc
setTimeout(() => {
const targetDisc = gameState.discs.find(d =>
!d.pocketed &&
(d.type === 'green' || d.type === 'white') &&
d.element.style.display !== 'none'
);
if (targetDisc) {
// Calculate angle to target disc
const dx = targetDisc.x - 30; // Striker zone center X
const dy = targetDisc.y - 85; // Striker zone center Y
const angle = Math.atan2(dy, dx);
// Set striker position (slightly offset from center)
const offsetX = 30 + Math.cos(angle) * 5;
const offsetY = 85 + Math.sin(angle) * 5;
striker.style.left = `${offsetX}%`;
striker.style.top = `${offsetY}%`;
gameState.striker.x = offsetX;
gameState.striker.y = offsetY;
gameState.striker.angle = angle;
gameState.striker.power = 50 + Math.random() * 50; // Random power
updateTrajectory();
// Shoot after a short delay
setTimeout(handleShoot, 1000);
} else {
// No discs to target, end turn
gameState.currentPlayer = 'Ash';
updateGameStatus();
}
}, 1500);
}
// Event listeners
striker.addEventListener('mousedown', () => {
gameState.striker.isDragging = true;
trajectoryLine.style.display = 'block';
trajectoryArrow.style.display = 'block';
});
striker.addEventListener('touchstart', (e) => {
e.preventDefault();
gameState.striker.isDragging = true;
trajectoryLine.style.display = 'block';
trajectoryArrow.style.display = 'block';
});
document.addEventListener('mousemove', (e) => {
if (gameState.striker.isDragging) {
handleStrikerDrag(e);
}
});
document.addEventListener('touchmove', (e) => {
if (gameState.striker.isDragging) {
e.preventDefault();
handleStrikerDrag(e);
}
});
document.addEventListener('mouseup', () => {
gameState.striker.isDragging = false;
});
document.addEventListener('touchend', () => {
gameState.striker.isDragging = false;
});
shootBtn.addEventListener('click', handleShoot);
resetBtn.addEventListener('click', initGame);
aiBtn.addEventListener('click', () => {
if (gameState.currentPlayer === 'MD') {
aiMove();
} else {
gameStatus.textContent = "It's not the AI's turn yet!";
}
});
// Initialize the game
initGame();
});
</script>
</body>
</html>