🎧 Grain Rain - Listen & Interpret
by RocketGecko40490 lines15.3 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🎧 Grain Rain - Listen & Interpret</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', 'Poppins', sans-serif;
background: linear-gradient(135deg, #667eea, #764ba2);
min-height: 100vh;
padding: 20px;
}
.game-box {
max-width: 700px;
margin: 0 auto;
}
.header {
background: white;
border-radius: 30px;
padding: 20px;
margin-bottom: 25px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.score {
background: #764ba2;
padding: 10px 25px;
border-radius: 50px;
color: white;
font-weight: bold;
font-size: 1.3rem;
}
.round {
background: #667eea;
padding: 10px 25px;
border-radius: 50px;
color: white;
font-weight: bold;
}
.name-input {
display: flex;
gap: 8px;
}
.name-input input {
padding: 8px 12px;
border-radius: 50px;
border: 1px solid #ddd;
}
.name-input button {
background: #667eea;
color: white;
border: none;
padding: 8px 15px;
border-radius: 50px;
cursor: pointer;
}
.main-card {
background: white;
border-radius: 30px;
padding: 30px;
margin-bottom: 20px;
box-shadow: 0 15px 30px rgba(0,0,0,0.15);
}
.audio-section {
background: #f0f4ff;
border-radius: 20px;
padding: 20px;
text-align: center;
margin-bottom: 25px;
}
.play-sim {
background: #667eea;
color: white;
border: none;
padding: 15px 30px;
border-radius: 60px;
font-size: 1.2rem;
font-weight: bold;
cursor: pointer;
margin: 10px 0;
}
.play-sim:hover {
background: #764ba2;
}
.script {
background: #f8f9fa;
padding: 15px;
border-radius: 15px;
font-size: 0.9rem;
margin-top: 15px;
display: none;
text-align: left;
}
.script.show {
display: block;
}
.toggle-script {
background: none;
border: none;
color: #667eea;
cursor: pointer;
font-size: 0.85rem;
margin-top: 10px;
}
.interpreting-area {
margin-top: 20px;
}
textarea {
width: 100%;
padding: 15px;
border: 2px solid #e0e0e0;
border-radius: 20px;
font-size: 1rem;
font-family: inherit;
min-height: 100px;
resize: vertical;
}
.submit-btn {
background: #4caf50;
color: white;
border: none;
padding: 12px 25px;
border-radius: 50px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
margin-top: 15px;
width: 100%;
}
.submit-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.feedback {
margin-top: 20px;
padding: 15px;
border-radius: 20px;
}
.feedback.great {
background: #d4edda;
color: #155724;
border-left: 5px solid #28a745;
}
.feedback.good {
background: #fff3cd;
color: #856404;
border-left: 5px solid #ffc107;
}
.feedback.keep {
background: #f8d7da;
color: #721c24;
border-left: 5px solid #dc3545;
}
.next-btn {
background: #667eea;
color: white;
border: none;
padding: 12px;
border-radius: 50px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
margin-top: 20px;
width: 100%;
}
.leaderboard {
background: rgba(255,255,255,0.95);
border-radius: 25px;
padding: 20px;
}
.leaderboard h3 {
color: #333;
margin-bottom: 15px;
}
.leaderboard ul {
list-style: none;
}
.leaderboard li {
padding: 10px 15px;
background: #f0f0f0;
margin-bottom: 8px;
border-radius: 15px;
display: flex;
justify-content: space-between;
}
.reset-btn {
background: #e74c3c;
color: white;
border: none;
padding: 10px;
border-radius: 50px;
cursor: pointer;
margin-top: 15px;
width: 100%;
}
.end-screen {
text-align: center;
}
.end-screen h2 {
color: #667eea;
font-size: 1.8rem;
}
@media (max-width: 550px) {
.main-card { padding: 20px; }
}
</style>
</head>
<body>
<div class="game-box">
<div class="header">
<div class="score">🎧 Score: <span id="score">0</span></div>
<div class="round">Round <span id="currentRound">1</span>/<span id="totalRounds">4</span></div>
<div class="name-input">
<input type="text" id="playerName" placeholder="Your name" value="Student">
<button onclick="resetGame()">🔄 New</button>
</div>
</div>
<div class="main-card" id="mainCard">
<div id="content"></div>
</div>
<div class="leaderboard">
<h3>🏆 Best Interpreters</h3>
<ul id="leaderboardList">
<li>Complete a round to appear!</li>
</ul>
<button class="reset-btn" onclick="resetAllScores()">🗑️ Reset Scores</button>
</div>
</div>
<script>
// Simple interpreting passages with key words
const rounds = [
{
title: "🌾 What is Grain Rain?",
audioText: "Grain Rain is the sixth solar term. It is the last term of spring. The name means 'rain helps grains grow.'",
targetAnswer: "谷雨是第六个节气。它是春季的最后一个节气。名字的意思是'雨帮助谷物生长。'",
keywords: ["谷雨", "第六个", "节气", "春季", "最后", "雨", "谷物", "生长"],
maxPoints: 20
},
{
title: "🍵 Drinking Guyu Tea",
audioText: "In southern China, people drink Guyu tea. They believe it removes heat and brings good luck.",
targetAnswer: "在中国南方,人们喝谷雨茶。他们相信它可以清热并带来好运。",
keywords: ["南方", "喝", "谷雨茶", "清热", "好运"],
maxPoints: 20
},
{
title: "🥬 Eating Chinese Toon",
audioText: "In northern China, people eat Chinese toon during Grain Rain. An old saying says it is as tender as silk before the rain.",
targetAnswer: "在中国北方,人们在谷雨时节吃香椿。古语说雨前的香椿嫩如丝。",
keywords: ["北方", "香椿", "谷雨", "雨前", "嫩如丝"],
maxPoints: 20
},
{
title: "🌸 Peony Flowers",
audioText: "During Grain Rain, peony flowers bloom. The peony is called the 'Queen of All Flowers' in Chinese culture.",
targetAnswer: "谷雨期间,牡丹花盛开。牡丹在中国文化中被称为'花中之王'。",
keywords: ["谷雨", "牡丹", "开花", "花中之王", "中国文化"],
maxPoints: 20
}
];
let currentIndex = 0;
let totalScore = 0;
let answered = false;
let leaderboard = [];
function loadLeaderboard() {
const saved = localStorage.getItem("grainRainListen");
if (saved) leaderboard = JSON.parse(saved);
else leaderboard = [];
showLeaderboard();
}
function saveLeaderboard() {
localStorage.setItem("grainRainListen", JSON.stringify(leaderboard));
}
function showLeaderboard() {
const list = document.getElementById("leaderboardList");
if (!list) return;
if (leaderboard.length === 0) {
list.innerHTML = "<li>🎤 Play to become a top interpreter!</li>";
return;
}
const top8 = [...leaderboard].sort((a,b) => b.score - a.score).slice(0,8);
list.innerHTML = top8.map((entry, i) =>
`<li><span>${i+1}. ${entry.name}</span><span>🎧 ${entry.score}</span></li>`
).join("");
}
function addScore(name, score) {
leaderboard.push({ name: name, score: score });
leaderboard.sort((a,b) => b.score - a.score);
leaderboard = leaderboard.slice(0, 10);
saveLeaderboard();
showLeaderboard();
}
function updateDisplay() {
document.getElementById("score").innerText = totalScore;
document.getElementById("currentRound").innerText = currentIndex + 1;
document.getElementById("totalRounds").innerText = rounds.length;
}
function toggleScript() {
const scriptBox = document.querySelector(".script-box");
if (scriptBox) scriptBox.classList.toggle("show");
}
function checkAnswer() {
if (answered) return;
const input = document.querySelector("textarea");
const userAnswer = input.value.trim().toLowerCase();
const round = rounds[currentIndex];
// Count keywords found
let found = 0;
for (let kw of round.keywords) {
if (userAnswer.includes(kw.toLowerCase())) {
found++;
}
}
const percentage = found / round.keywords.length;
let earned = 0;
let feedbackClass = "";
let message = "";
if (percentage >= 0.7) {
earned = round.maxPoints;
feedbackClass = "great";
message = `🎉 EXCELLENT! +${earned} points! You got ${found}/${round.keywords.length} key words.`;
} else if (percentage >= 0.4) {
earned = Math.floor(round.maxPoints * 0.6);
feedbackClass = "good";
message = `👍 GOOD JOB! +${earned} points! You got ${found}/${round.keywords.length} key words.`;
} else {
earned = 0;
feedbackClass = "keep";
message = `📚 KEEP PRACTICING! +0 points. You got ${found}/${round.keywords.length} key words.<br><br>📝 Reference: ${round.targetAnswer}`;
}
totalScore += earned;
updateDisplay();
answered = true;
const container = document.getElementById("content");
const feedbackDiv = document.createElement("div");
feedbackDiv.className = `feedback ${feedbackClass}`;
feedbackDiv.innerHTML = message;
container.appendChild(feedbackDiv);
// Disable input and submit
const textarea = document.querySelector("textarea");
const submitBtn = document.querySelector(".submit-btn");
if (textarea) textarea.disabled = true;
if (submitBtn) submitBtn.disabled = true;
// Next button
const nextBtn = document.createElement("button");
nextBtn.innerText = currentIndex + 1 === rounds.length ? "🏆 FINISH" : "➡️ NEXT ROUND";
nextBtn.className = "next-btn";
nextBtn.onclick = () => {
currentIndex++;
if (currentIndex < rounds.length) {
renderRound();
} else {
endGame();
}
};
container.appendChild(nextBtn);
}
function renderRound() {
answered = false;
const round = rounds[currentIndex];
const container = document.getElementById("content");
let html = `
<div class="audio-section">
<p style="font-size: 1.2rem; margin-bottom: 10px;">🎧 ${round.title}</p>
<button class="play-sim" id="playBtn">🔊 CLICK - TEACHER READS</button>
<button class="toggle-script" onclick="toggleScript()">📄 Show/Hide Script</button>
<div class="script script-box">
<strong>📖 English Script (Teacher reads this):</strong><br>
"${round.audioText}"
</div>
</div>
<div class="interpreting-area">
<p style="margin-bottom: 10px; color: #555;">✍️ Write your Chinese interpretation below:</p>
<textarea placeholder="Type your Chinese translation here..."></textarea>
<button class="submit-btn" onclick="checkAnswer()">🎤 SUBMIT</button>
</div>
`;
container.innerHTML = html;
updateDisplay();
// Add audio simulation
const playBtn = document.getElementById("playBtn");
if (playBtn) {
playBtn.onclick = () => {
alert(`🎧 TEACHER READS ALOUD:\n\n"${round.audioText}"\n\n👂 Listen carefully! The teacher will read it twice. Take notes!`);
playBtn.style.background = "#4caf50";
playBtn.innerText = "✅ AUDIO PLAYED";
};
}
}
function endGame() {
const name = document.getElementById("playerName").value || "Student";
addScore(name, totalScore);
const container = document.getElementById("content");
container.innerHTML = `
<div class="end-screen">
<h2>🎉 Congratulations! 🎉</h2>
<p style="font-size: 1.8rem; margin: 20px;">🎧 ${totalScore} / ${rounds.length * 20}</p>
<p>${name}, great interpreting practice!</p>
<p>💡 Tip: Focus on key words when listening!</p>
<button class="next-btn" onclick="resetGame()">🔄 Play Again</button>
</div>
`;
}
function resetGame() {
currentIndex = 0;
totalScore = 0;
answered = false;
renderRound();
}
function resetAllScores() {
if (confirm("Delete all scores?")) {
leaderboard = [];
saveLeaderboard();
showLeaderboard();
}
}
loadLeaderboard();
resetGame();
</script>
</body>
</html>Game Source: 🎧 Grain Rain - Listen & Interpret
Creator: RocketGecko40
Libraries: none
Complexity: complex (490 lines, 15.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: grain-rain-listen-interpret-rocketgecko40" to link back to the original. Then publish at arcadelab.ai/publish.