rock-paper-scissors / index.html
vs4vijay's picture
undefined - Initial Deployment
ae67fb7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rock Paper Scissors for Kids</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/handpose@latest"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Comic+Neue:wght@400;700&display=swap');
body {
font-family: 'Comic Neue', cursive;
background-color: #f0f9ff;
}
.hand-animation {
animation: bounce 0.5s infinite alternate;
}
@keyframes bounce {
from { transform: translateY(0); }
to { transform: translateY(-10px); }
}
.result-bubble {
animation: pop 0.3s ease-out;
}
@keyframes pop {
0% { transform: scale(0.5); opacity: 0; }
80% { transform: scale(1.1); }
100% { transform: scale(1); opacity: 1; }
}
.emoji {
font-size: 2rem;
}
.animate-pulse {
animation: pulse 0.5s ease-in-out;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.2); }
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full bg-white rounded-3xl shadow-xl overflow-hidden">
<!-- Header -->
<div class="bg-gradient-to-r from-blue-500 to-purple-600 p-6 text-center">
<h1 class="text-4xl font-bold text-white mb-2">πŸ‘‹ Rock Paper Scissors πŸ‘‹</h1>
<p class="text-white text-lg">Show your hand to the camera to play!</p>
</div>
<!-- Game Area -->
<div class="p-6 md:p-8 grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Camera Section -->
<div class="bg-blue-50 rounded-2xl p-4 flex flex-col items-center">
<h2 class="text-2xl font-bold text-blue-800 mb-4">Your Move</h2>
<div class="relative w-full aspect-square max-w-md bg-gray-200 rounded-xl overflow-hidden mb-4">
<video id="video" class="w-full h-full object-cover" playsinline autoplay muted></video>
<div id="overlay" class="absolute inset-0"></div>
</div>
<div class="flex gap-4 mb-4">
<button id="startCamera" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded-full transition">
Start Camera
</button>
<button id="stopCamera" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded-full transition">
Stop Camera
</button>
</div>
<div class="text-center">
<p class="text-gray-700 mb-2">Show one of these to the camera:</p>
<div class="flex justify-center gap-6">
<div class="text-center">
<div class="emoji">✊</div>
<p class="text-sm">Rock</p>
</div>
<div class="text-center">
<div class="emoji">βœ‹</div>
<p class="text-sm">Paper</p>
</div>
<div class="text-center">
<div class="emoji">✌️</div>
<p class="text-sm">Scissors</p>
</div>
</div>
</div>
</div>
<!-- Game Section -->
<div class="bg-purple-50 rounded-2xl p-4 flex flex-col items-center justify-between">
<h2 class="text-2xl font-bold text-purple-800 mb-4">Game</h2>
<!-- Score Board -->
<div class="w-full bg-white rounded-xl p-4 mb-6 shadow-md">
<div class="flex justify-between items-center">
<div class="text-center">
<p class="text-sm text-gray-600">You</p>
<p id="playerScore" class="text-3xl font-bold text-blue-600">0</p>
</div>
<div class="text-2xl font-bold text-gray-500">VS</div>
<div class="text-center">
<p class="text-sm text-gray-600">Computer</p>
<p id="computerScore" class="text-3xl font-bold text-purple-600">0</p>
</div>
</div>
</div>
<!-- Moves Display -->
<div class="w-full flex justify-center gap-8 mb-8">
<div class="text-center">
<p class="text-sm text-gray-600 mb-2">You</p>
<div id="playerMove" class="text-6xl bg-white rounded-full w-20 h-20 flex items-center justify-center shadow-md">?</div>
</div>
<div class="text-center">
<p class="text-sm text-gray-600 mb-2">Computer</p>
<div id="computerMove" class="text-6xl bg-white rounded-full w-20 h-20 flex items-center justify-center shadow-md">?</div>
</div>
</div>
<!-- Result Display -->
<div id="resultDisplay" class="mb-6 text-center hidden">
<div class="result-bubble inline-block bg-yellow-100 text-yellow-800 px-6 py-3 rounded-full text-xl font-bold">
Let's Play!
</div>
</div>
<!-- Controls -->
<div class="flex flex-col items-center w-full">
<button id="playButton" class="bg-gradient-to-r from-pink-500 to-orange-500 hover:from-pink-600 hover:to-orange-600 text-white font-bold py-3 px-8 rounded-full text-xl shadow-lg transition transform hover:scale-105 mb-4">
Play!
</button>
<button id="resetButton" class="text-gray-600 hover:text-gray-800 font-medium">
Reset Game
</button>
</div>
</div>
</div>
<!-- Instructions -->
<div class="bg-gray-100 p-6">
<h3 class="text-xl font-bold text-gray-800 mb-3">How to Play:</h3>
<ol class="list-decimal pl-5 space-y-2 text-gray-700">
<li>Click "Start Camera" and allow access to your camera</li>
<li>Show your hand to the camera (rock ✊, paper βœ‹, or scissors ✌️)</li>
<li>Click "Play!" when you're ready</li>
<li>The computer will choose its move and show who won!</li>
</ol>
</div>
</div>
<script>
// Game variables
let playerScore = 0;
let computerScore = 0;
let playerMove = '';
let computerMove = '';
let gameActive = false;
let videoStream = null;
let handposeModel = null;
// DOM elements
const playerScoreElement = document.getElementById('playerScore');
const computerScoreElement = document.getElementById('computerScore');
const playerMoveElement = document.getElementById('playerMove');
const computerMoveElement = document.getElementById('computerMove');
const resultDisplay = document.getElementById('resultDisplay');
const playButton = document.getElementById('playButton');
const resetButton = document.getElementById('resetButton');
const startCameraButton = document.getElementById('startCamera');
const stopCameraButton = document.getElementById('stopCamera');
const videoElement = document.getElementById('video');
const overlayElement = document.getElementById('overlay');
// Initialize the game
function initGame() {
updateScores();
resetMoves();
// Event listeners
playButton.addEventListener('click', playRound);
resetButton.addEventListener('click', resetGame);
startCameraButton.addEventListener('click', startCamera);
stopCameraButton.addEventListener('click', stopCamera);
// Load handpose model
loadHandposeModel();
}
// Load TensorFlow handpose model
async function loadHandposeModel() {
try {
handposeModel = await handpose.load();
console.log('Handpose model loaded');
} catch (error) {
console.error('Error loading handpose model:', error);
}
}
// Start camera
async function startCamera() {
try {
videoStream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'user',
width: { ideal: 640 },
height: { ideal: 480 }
},
audio: false
});
videoElement.srcObject = videoStream;
gameActive = true;
detectHandGesture();
} catch (error) {
console.error('Error accessing camera:', error);
alert('Could not access the camera. Please make sure you have granted permission.');
}
}
// Stop camera
function stopCamera() {
if (videoStream) {
videoStream.getTracks().forEach(track => track.stop());
videoElement.srcObject = null;
gameActive = false;
}
}
// Detect hand gesture from camera
async function detectHandGesture() {
if (!gameActive || !handposeModel) return;
try {
// Predict hand landmarks
const predictions = await handposeModel.estimateHands(videoElement);
// Clear overlay
overlayElement.innerHTML = '';
if (predictions.length > 0) {
const hand = predictions[0];
const fingersUp = countFingersUp(hand.landmarks);
// Determine move based on fingers up
if (fingersUp === 0) {
playerMove = 'rock';
playerMoveElement.textContent = '✊';
} else if (fingersUp >= 4) {
playerMove = 'paper';
playerMoveElement.textContent = 'βœ‹';
} else if (fingersUp === 2) {
playerMove = 'scissors';
playerMoveElement.textContent = '✌️';
} else {
playerMove = '';
playerMoveElement.textContent = '?';
}
// Draw landmarks (for visualization)
drawLandmarks(hand.landmarks);
} else {
playerMove = '';
playerMoveElement.textContent = '?';
}
} catch (error) {
console.error('Hand detection error:', error);
}
// Continue detecting
requestAnimationFrame(detectHandGesture);
}
// Count how many fingers are up
function countFingersUp(landmarks) {
// Simplified finger detection (thumb, index, middle, ring, pinky)
const fingerTips = [4, 8, 12, 16, 20]; // Landmark indices for finger tips
const fingerPips = [2, 6, 10, 14, 18]; // Landmark indices for finger PIP joints
let count = 0;
for (let i = 1; i < 5; i++) { // Skip thumb (i=0)
const tip = landmarks[fingerTips[i]];
const pip = landmarks[fingerPips[i]];
// Check if finger is extended (tip is above PIP joint)
if (tip[1] < pip[1]) { // Compare y-coordinates
count++;
}
}
return count;
}
// Draw hand landmarks on overlay
function drawLandmarks(landmarks) {
const canvas = document.createElement('canvas');
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
const ctx = canvas.getContext('2d');
// Draw landmarks
ctx.fillStyle = 'red';
landmarks.forEach(landmark => {
ctx.beginPath();
ctx.arc(landmark[0], landmark[1], 5, 0, 2 * Math.PI);
ctx.fill();
});
// Draw connections (simplified)
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;
// Palm connections
drawConnection(ctx, landmarks, [0, 1, 2, 5, 9, 13, 17, 0]);
// Thumb
drawConnection(ctx, landmarks, [1, 2, 3, 4]);
// Index finger
drawConnection(ctx, landmarks, [5, 6, 7, 8]);
// Middle finger
drawConnection(ctx, landmarks, [9, 10, 11, 12]);
// Ring finger
drawConnection(ctx, landmarks, [13, 14, 15, 16]);
// Pinky finger
drawConnection(ctx, landmarks, [17, 18, 19, 20]);
overlayElement.innerHTML = '';
overlayElement.appendChild(canvas);
}
// Helper function to draw connections between landmarks
function drawConnection(ctx, landmarks, indices) {
ctx.beginPath();
for (let i = 0; i < indices.length; i++) {
const landmark = landmarks[indices[i]];
if (i === 0) {
ctx.moveTo(landmark[0], landmark[1]);
} else {
ctx.lineTo(landmark[0], landmark[1]);
}
}
ctx.stroke();
}
// Play a round of the game
function playRound() {
if (!playerMove) {
showResult('Show your hand to the camera! βœŠβœ‹βœŒοΈ');
playerMoveElement.classList.add('animate-pulse');
setTimeout(() => {
playerMoveElement.classList.remove('animate-pulse');
}, 1000);
return;
}
// Computer chooses random move
const moves = ['rock', 'paper', 'scissors'];
computerMove = moves[Math.floor(Math.random() * moves.length)];
// Update computer move display
computerMoveElement.textContent =
computerMove === 'rock' ? '✊' :
computerMove === 'paper' ? 'βœ‹' : '✌️';
// Determine winner
const result = determineWinner(playerMove, computerMove);
// Update scores and display result
if (result === 'win') {
playerScore++;
showResult('You win! πŸŽ‰');
} else if (result === 'lose') {
computerScore++;
showResult('Computer wins! 😒');
} else {
showResult("It's a tie! 🀝");
}
updateScores();
}
// Determine the winner of a round
function determineWinner(player, computer) {
if (player === computer) return 'tie';
if (
(player === 'rock' && computer === 'scissors') ||
(player === 'paper' && computer === 'rock') ||
(player === 'scissors' && computer === 'paper')
) {
return 'win';
}
return 'lose';
}
// Show game result
function showResult(message) {
resultDisplay.classList.remove('hidden');
const resultElement = resultDisplay.querySelector('div');
// Update styling based on result
if (message.includes('win')) {
resultElement.className = 'result-bubble inline-block bg-green-100 text-green-800 px-6 py-3 rounded-full text-xl font-bold';
} else if (message.includes('lose')) {
resultElement.className = 'result-bubble inline-block bg-red-100 text-red-800 px-6 py-3 rounded-full text-xl font-bold';
} else if (message.includes('tie')) {
resultElement.className = 'result-bubble inline-block bg-yellow-100 text-yellow-800 px-6 py-3 rounded-full text-xl font-bold';
} else {
resultElement.className = 'result-bubble inline-block bg-blue-100 text-blue-800 px-6 py-3 rounded-full text-xl font-bold';
}
resultElement.textContent = message;
}
// Update score displays
function updateScores() {
playerScoreElement.textContent = playerScore;
computerScoreElement.textContent = computerScore;
}
// Reset moves display
function resetMoves() {
playerMove = '';
computerMove = '';
playerMoveElement.textContent = '?';
computerMoveElement.textContent = '?';
showResult("Let's Play!");
}
// Reset the entire game
function resetGame() {
playerScore = 0;
computerScore = 0;
updateScores();
resetMoves();
}
// Initialize the game when the page loads
window.addEventListener('DOMContentLoaded', initGame);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=vs4vijay/rock-paper-scissors" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>