anycoder-c9f6c7f8 / index.html
rmz92002's picture
Upload folder using huggingface_hub
20e3ce5 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hyphen Hunter: Interactive Exercise</title>
<!-- Importing FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #ec4899;
--bg-dark: #0f172a;
--bg-card: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--success: #22c55e;
--error: #ef4444;
--hyphen-color: #fbbf24;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
/* --- Header --- */
header {
background: rgba(30, 41, 59, 0.8);
backdrop-filter: blur(10px);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(to right, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 10px;
}
.built-with {
font-size: 0.9rem;
color: var(--text-muted);
text-decoration: none;
transition: color 0.3s;
}
.built-with:hover {
color: var(--primary);
}
/* --- Main Content --- */
main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
position: relative;
}
/* --- Game Container --- */
.game-container {
width: 100%;
max-width: 900px;
background: var(--bg-card);
border-radius: 20px;
padding: 3rem;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
border: 1px solid rgba(255, 255, 255, 0.05);
text-align: center;
position: relative;
overflow: hidden;
}
/* --- Start Screen --- */
.screen {
display: none;
flex-direction: column;
align-items: center;
gap: 2rem;
animation: fadeIn 0.5s ease-out;
}
.screen.active {
display: flex;
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.instruction {
color: var(--text-muted);
font-size: 1.1rem;
max-width: 600px;
line-height: 1.6;
}
.highlight {
color: var(--hyphen-color);
font-weight: bold;
font-size: 1.2em;
}
/* --- Word Display --- */
.word-display {
font-size: 4rem;
font-weight: 800;
letter-spacing: 2px;
margin: 2rem 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 120px;
position: relative;
cursor: pointer;
transition: transform 0.2s;
user-select: none;
}
.word-display:active {
transform: scale(0.98);
}
.letter {
display: inline-block;
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.letter.correct {
color: var(--success);
text-shadow: 0 0 15px rgba(34, 197, 94, 0.5);
}
.letter.wrong {
color: var(--error);
text-decoration: line-through;
opacity: 0.5;
}
/* --- Controls --- */
.controls {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.btn {
padding: 1rem 2rem;
border: none;
border-radius: 50px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(99, 102, 241, 0.6);
}
.btn-primary:active {
transform: translateY(1px);
}
.btn-outline {
background: transparent;
border: 2px solid var(--text-muted);
color: var(--text-muted);
}
.btn-outline:hover {
border-color: var(--text-main);
color: var(--text-main);
}
/* --- Feedback Area --- */
.feedback-area {
height: 40px;
margin-top: 1rem;
font-weight: 600;
font-size: 1.2rem;
opacity: 0;
transition: opacity 0.3s;
}
.feedback-area.visible {
opacity: 1;
}
.feedback-area.success { color: var(--success); }
.feedback-area.error { color: var(--error); }
/* --- Stats --- */
.stats-bar {
display: flex;
justify-content: space-between;
width: 100%;
padding: 1rem;
background: rgba(0,0,0,0.2);
border-radius: 12px;
margin-bottom: 2rem;
font-size: 1.1rem;
}
.stat-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.stat-value {
font-weight: bold;
color: var(--primary);
}
/* --- Confetti Canvas --- */
#confetti-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10;
}
/* --- Animations --- */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-10px); }
75% { transform: translateX(10px); }
}
.shake {
animation: shake 0.4s ease-in-out;
}
/* --- Responsive --- */
@media (max-width: 600px) {
.game-container {
padding: 1.5rem;
}
.word-display {
font-size: 2.5rem;
}
h1 {
font-size: 1.8rem;
}
.controls {
flex-direction: column;
width: 100%;
}
.btn {
width: 100%;
justify-content: center;
}
}
</style>
</head>
<body>
<header>
<div class="logo">
<i class="fa-solid fa-asterisk"></i> Hyphen Hunter
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square"></i>
</a>
</header>
<main>
<div class="game-container">
<canvas id="confetti-canvas"></canvas>
<!-- Stats Bar -->
<div class="stats-bar">
<div class="stat-item">
<i class="fa-solid fa-heart" style="color: var(--error)"></i>
<span>Score: <span id="score" class="stat-value">0</span></span>
</div>
<div class="stat-item">
<i class="fa-solid fa-trophy" style="color: var(--hyphen-color)"></i>
<span>Streak: <span id="streak" class="stat-value">0</span></span>
</div>
</div>
<!-- Start Screen -->
<div id="start-screen" class="screen active">
<div style="font-size: 4rem; color: var(--hyphen-color); margin-bottom: 1rem;">
<i class="fa-solid fa-scissors"></i>
</div>
<h1>Identify the Hyphen</h1>
<p class="instruction">
Your goal is to spot the <span class="highlight">hyphen (-)</span> inside the word.<br>
Tap the letter that contains the hyphen to score points.
</p>
<button class="btn btn-primary" onclick="startGame()">
<i class="fa-solid fa-play"></i> Start Game
</button>
</div>
<!-- Game Screen -->
<div id="game-screen" class="screen">
<div class="word-display" id="word-display">
<!-- Letters injected here -->
</div>
<div id="feedback" class="feedback-area"></div>
<div class="controls">
<button class="btn btn-outline" onclick="skipWord()" id="skip-btn">
<i class="fa-solid fa-forward"></i> Skip
</button>
<button class="btn btn-primary" onclick="checkAnswer()" id="check-btn">
<i class="fa-solid fa-check"></i> Confirm
</button>
</div>
</div>
<!-- Game Over Screen -->
<div id="game-over-screen" class="screen">
<div style="font-size: 4rem; color: var(--primary); margin-bottom: 1rem;">
<i class="fa-solid fa-flag-checkered"></i>
</div>
<h1>Game Over</h1>
<p class="instruction">
Great effort! You finished the set.
</p>
<div style="font-size: 2rem; margin: 1rem 0;">
Final Score: <span id="final-score" class="stat-value">0</span>
</div>
<div class="controls">
<button class="btn btn-primary" onclick="startGame()">
<i class="fa-solid fa-rotate-right"></i> Play Again
</button>
</div>
</div>
</div>
</main>
<script>
// --- Game Data ---
const words = [
{ text: "mother-in-law", index: 3 },
{ text: "well-being", index: 4 },
{ text: "state-of-the-art", index: 7 },
{ text: "decision-making", index: 5 },
{ text: "full-time", index: 3 },
{ text: "twenty-one", index: 5 },
{ text: "high-level", index: 4 },
{ text: "copy-editing", index: 3 },
{ text: "fourth-of-july", index: 6 },
{ text: "decision-making", index: 5 },
{ text: "co-operate", index: 2 },
{ text: "eco-friendly", index: 2 },
{ text: "self-control", index: 3 },
{ text: "high-tech", index: 3 },
{ text: "long-term", index: 3 },
{ text: "decision-making", index: 5 }
];
// --- State ---
let currentWord = "";
let currentHyphenIndex = -1;
let score = 0;
let streak = 0;
let isProcessing = false;
let audioCtx = null;
// --- Elements ---
const screens = {
start: document.getElementById('start-screen'),
game: document.getElementById('game-screen'),
over: document.getElementById('game-over-screen')
};
const wordDisplay = document.getElementById('word-display');
const scoreEl = document.getElementById('score');
const streakEl = document.getElementById('streak');
const feedbackEl = document.getElementById('feedback');
const finalScoreEl = document.getElementById('final-score');
// --- Audio System (Web Audio API) ---
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
}
function playSound(type) {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
const now = audioCtx.currentTime;
if (type === 'success') {
osc.type = 'sine';
osc.frequency.setValueAtTime(500, now);
osc.frequency.exponentialRampToValueAtTime(1000, now + 0.1);
gain.gain.setValueAtTime(0.3, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3);
osc.start(now);
osc.stop(now + 0.3);
} else if (type === 'error') {
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(200, now);
osc.frequency.linearRampToValueAtTime(100, now + 0.2);
gain.gain.setValueAtTime(0.3, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3);
osc.start(now);
osc.stop(now + 0.3);
} else if (type === 'hover') {
osc.type = 'triangle';
osc.frequency.setValueAtTime(800, now);
gain.gain.setValueAtTime(0.05, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.05);
osc.start(now);
osc.stop(now + 0.05);
}
}
// --- Game Logic ---
function switchScreen(screenName) {
Object.values(screens).forEach(s => s.classList.remove('active'));
screens[screenName].classList.add('active');
}
function startGame() {
score = 0;
streak = 0;
updateStats();
switchScreen('game');
loadNewWord();
}
function loadNewWord() {
isProcessing = false;
feedbackEl.className = 'feedback-area';
feedbackEl.innerText = '';
// Pick random word
const randomIndex = Math.floor(Math.random() * words.length);
currentWord = words[randomIndex].text;
currentHyphenIndex = words[randomIndex].index;
renderWord();
}
function renderWord() {
wordDisplay.innerHTML = '';
currentWord.split('').forEach((char, index) => {
const span = document.createElement('span');
span.className = 'letter';
span.innerText = char;
span.dataset.index = index;
// Interaction
span.addEventListener('click', () => handleLetterClick(span));
span.addEventListener('mouseenter', () => playSound('hover'));
wordDisplay.appendChild(span);
});
}
function handleLetterClick(element) {
if (isProcessing) return;
const index = parseInt(element.dataset.index);
const letter = currentWord[index];
// Visual feedback immediately on click
if (letter === '-') {
element.classList.add('correct');
// Don't change color to green yet, wait for confirm
} else {
element.classList.add('wrong');
playSound('error');
streak = 0;
updateStats();
triggerShake();
}
}
function checkAnswer() {
if (isProcessing) return;
// Find the selected hyphen
const selected = wordDisplay.querySelector('.letter.correct');
if (selected) {
const clickedIndex = parseInt(selected.dataset.index);
if (clickedIndex === currentHyphenIndex) {
handleSuccess();
} else {
handleFailure(selected);
}
} else {
// No selection made
feedbackEl.innerText = "Please select a letter first!";
feedbackEl.classList.add('visible', 'error');
}
}
function handleSuccess() {
isProcessing = true;
playSound('success');
// Highlight the correct letter green permanently
const correctSpan = wordDisplay.querySelectorAll('.letter')[currentHyphenIndex];
correctSpan.classList.remove('correct'); // remove temp color
correctSpan.style.color = 'var(--success)';
score += 10 + (streak * 2);
streak++;
updateStats();
feedbackEl.innerText = "Correct! +Points";
feedbackEl.className = 'feedback-area visible success';
// Confetti effect
fireConfetti();
setTimeout(() => {
loadNewWord();
}, 1500);
}
function handleFailure(wrongSpan) {
isProcessing = true;
playSound('error');
streak = 0;
updateStats();
wrongSpan.classList.remove('wrong');
wrongSpan.style.color = 'var(--error)';
feedbackEl.innerText = "Wrong! That wasn't the hyphen.";
feedbackEl.className = 'feedback-area visible error';
triggerShake();
setTimeout(() => {
// Reset visual state
wrongSpan.style.color = '';
loadNewWord();
}, 1000);
}
function skipWord() {
if (isProcessing) return;
streak = 0;
updateStats();
loadNewWord();
}
function triggerShake() {
wordDisplay.classList.add('shake');
setTimeout(() => wordDisplay.classList.remove('shake'), 400);
}
function updateStats() {
scoreEl.innerText = score;
streakEl.innerText = streak;
}
// --- Confetti System (Canvas) ---
const canvas = document.getElementById('confetti-canvas');
const ctx = canvas.getContext('2d');
let particles = [];
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
function fireConfetti() {
const colors = ['#6366f1', '#ec4899', '#22c55e', '#fbbf24'];
for (let i = 0; i < 100; i++) {
particles.push({
x: canvas.width / 2,
y: canvas.height / 2,
vx: (Math.random() - 0.5) * 15,
vy: (Math.random() - 0.5) * 15 - 5,
life: 1,
color: colors[Math.floor(Math.random() * colors.length)],
size: Math.random() * 5 + 2
});
}
animateConfetti();
}
function animateConfetti() {
if (particles.length === 0) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < particles.length; i++) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += 0.2; // Gravity
p.life -= 0.02;
ctx.fillStyle = p.color;
ctx.globalAlpha = p.life;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
}
particles = particles.filter(p => p.life > 0);
if (particles.length > 0) {
requestAnimationFrame(animateConfetti);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
</script>
</body>
</html>