5w1h
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>English Game: Asking & Giving Information</title>
<!-- Tailwind CSS for Styling -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- React & Babel for Game Logic -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- Animate.css for Animations -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Fredoka+One&family=Nunito:wght@400;700;900&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Nunito', sans-serif;
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
min-height: 100vh;
overflow-x: hidden;
margin: 0;
padding: 0;
}
.title-font {
font-family: 'Fredoka One', cursive;
}
.word-btn {
transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 6px 0 rgba(0,0,0,0.15);
}
.word-btn:active {
transform: translateY(6px);
box-shadow: 0 0 0 rgba(0,0,0,0.15);
}
.drop-zone {
min-height: 90px;
border: 3px dashed rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.3);
border-radius: 1.5rem;
}
.glass-panel {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 2rem;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef } = React;
// Data berdasarkan materi yang Anda berikan
const GAME_DATA = [
{ id: 1, sentence: "What is your favorite subject", translation: "Apa mata pelajaran favoritmu?" },
{ id: 2, sentence: "Where do you study", translation: "Di mana kamu belajar?" },
{ id: 3, sentence: "When is your birthday", translation: "Kapan ulang tahunmu?" },
{ id: 4, sentence: "Who is your English teacher", translation: "Siapa guru bahasa Inggrismu?" },
{ id: 5, sentence: "Why do you like English", translation: "Mengapa kamu suka bahasa Inggris?" },
{ id: 6, sentence: "How do you go to school", translation: "Bagaimana kamu pergi ke sekolah?" },
{ id: 7, sentence: "Which book is yours", translation: "Buku yang mana milikmu?" },
{ id: 8, sentence: "Whose bag is this", translation: "Tas milik siapa ini?" },
{ id: 9, sentence: "What is your address", translation: "Di mana alamatmu?" },
{ id: 10, sentence: "What is your name", translation: "Siapa namamu?" },
{ id: 11, sentence: "What is your age", translation: "Berapa usiamu?" },
{ id: 12, sentence: "Where is your school", translation: "Di mana sekolahmu?" },
{ id: 13, sentence: "Who is your teacher", translation: "Siapa gurumu?" },
{ id: 14, sentence: "What is your favorite color", translation: "Apa warna favoritmu?" },
{ id: 15, sentence: "Write the answer in your book", translation: "Tulis jawaban di bukumu." },
{ id: 16, sentence: "Listen to the question", translation: "Dengarkan pertanyaan itu." },
{ id: 17, sentence: "I want to ask you a question", translation: "Saya ingin bertanya kepadamu." },
{ id: 18, sentence: "Give me some information about you", translation: "Beri saya informasi tentangmu." },
{ id: 19, sentence: "Math is my favorite subject", translation: "Matematika adalah pelajaran favoritku." },
{ id: 20, sentence: "My birthday is in May", translation: "Ulang tahunku di bulan Mei." }
];
const App = () => {
const [gameState, setGameState] = useState('LOGIN'); // LOGIN, PLAYING, RESULT
const [playerName, setPlayerName] = useState('');
const [currentLevel, setCurrentLevel] = useState(0);
const [score, setScore] = useState(0);
// Gameplay states
const [timer, setTimer] = useState(30);
const [scrambledWords, setScrambledWords] = useState([]);
const [userAnswer, setUserAnswer] = useState([]);
const [feedback, setFeedback] = useState(null);
useEffect(() => {
let interval;
if (gameState === 'PLAYING' && timer > 0 && !feedback) {
interval = setInterval(() => {
setTimer(prev => prev - 1);
}, 1000);
} else if (timer === 0 && gameState === 'PLAYING' && !feedback) {
handleTimeOut();
}
return () => clearInterval(interval);
}, [gameState, timer, feedback]);
const startGame = () => {
if (playerName.trim().length > 0) {
setGameState('PLAYING');
setScore(0);
setCurrentLevel(0);
setupLevel(0);
}
};
const setupLevel = (levelIndex) => {
let words = GAME_DATA[levelIndex].sentence.split(' ');
if (levelIndex < 14) words.push('?');
else words.push('.');
const shuffled = [...words].sort(() => Math.random() - 0.5);
setScrambledWords(shuffled);
setUserAnswer([]);
setTimer(30);
setFeedback(null);
};
const handleWordClick = (word, index, isFromScrambled) => {
if (feedback) return;
if (isFromScrambled) {
setUserAnswer([...userAnswer, word]);
setScrambledWords(scrambledWords.filter((_, i) => i !== index));
} else {
setScrambledWords([...scrambledWords, word]);
setUserAnswer(userAnswer.filter((_, i) => i !== index));
}
};
const checkAnswer = () => {
let expectedSentence = GAME_DATA[currentLevel].sentence;
let userSentence = userAnswer.join(' ');
let punctuation = currentLevel < 14 ? ' ?' : ' .';
if (userSentence === expectedSentence + punctuation) {
setFeedback('correct');
const pointsEarned = 100 + (timer * 10); // Bonus skor berdasarkan sisa waktu
setScore(prev => prev + pointsEarned);
setTimeout(() => nextLevel(), 1500);
} else {
setFeedback('wrong');
setTimeout(() => {
const allWords = [...scrambledWords, ...userAnswer].sort(() => Math.random() - 0.5);
setScrambledWords(allWords);
setUserAnswer([]);
setFeedback(null);
}, 1500);
}
};
const handleTimeOut = () => {
setFeedback('timeout');
setTimeout(() => nextLevel(), 2000);
};
const nextLevel = () => {
if (currentLevel + 1 < GAME_DATA.length) {
setCurrentLevel(prev => prev + 1);
setupLevel(currentLevel + 1);
} else {
setGameState('RESULT');
}
};
if (gameState === 'LOGIN') {
return (
<div className="flex flex-col items-center justify-center min-h-screen p-4 sm:p-6">
<div className="glass-panel p-8 sm:p-12 shadow-2xl w-full max-w-md animate__animated animate__zoomIn text-center relative overflow-hidden">
<div className="absolute top-[-50px] right-[-50px] w-32 h-32 bg-yellow-300 rounded-full opacity-50 z-0"></div>
<div className="relative z-10">
<div className="mb-6">
<span className="text-6xl drop-shadow-md">🧩</span>
</div>
<h1 className="title-font text-4xl sm:text-5xl text-indigo-600 mb-2 drop-shadow-sm">Word Scramble</h1>
<p className="text-gray-700 font-bold mb-8 bg-indigo-100 inline-block px-4 py-1 rounded-full text-sm sm:text-base">Unit 1: WH-Questions</p>
<div className="space-y-6 text-left">
<div>
<label className="block text-gray-700 font-bold mb-3 text-lg ml-2">Siapa namamu?</label>
<input
type="text"
maxLength="15"
className="w-full border-4 border-indigo-200 rounded-2xl px-5 py-4 text-xl focus:border-indigo-500 outline-none transition font-bold text-gray-700 bg-white"
placeholder="Ketik namamu di sini..."
value={playerName}
onChange={(e) => setPlayerName(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && startGame()}
/>
</div>
<button
onClick={startGame}
className="w-full bg-gradient-to-r from-yellow-400 to-orange-400 hover:from-yellow-500 hover:to-orange-500 text-white title-font text-2xl py-4 rounded-2xl transition transform hover:scale-105 shadow-[0_6px_0_#d97706] active:shadow-[0_0px_0_#d97706] active:translate-y-[6px]"
>
MULAI BERMAIN!
</button>
</div>
</div>
</div>
</div>
);
}
if (gameState === 'RESULT') {
return (
<div className="flex flex-col items-center justify-center min-h-screen p-4 sm:p-6">
<div className="glass-panel p-10 shadow-2xl w-full max-w-md text-center animate__animated animate__tada">
<h2 className="title-font text-4xl text-indigo-600 mb-2">Kerja Bagus!</h2>
<p className="text-xl text-gray-700 font-bold mb-6">{playerName}, kamu berhasil menyelesaikan Unit 1!</p>
<div className="bg-gradient-to-br from-indigo-500 to-purple-600 rounded-3xl p-8 mb-8 text-white shadow-inner relative overflow-hidden">
<div className="absolute top-0 right-0 text-6xl opacity-20 transform rotate-12 mt-2 mr-2">🌟</div>
<p className="text-indigo-200 text-lg font-bold uppercase tracking-wider mb-1">Total Skor Kamu</p>
<p className="title-font text-6xl drop-shadow-md">{score}</p>
</div>
<button
onClick={() => window.location.reload()}
className="w-full bg-green-400 hover:bg-green-500 text-white title-font text-2xl py-4 rounded-2xl transition transform hover:scale-105 shadow-[0_6px_0_#16a34a] active:shadow-none active:translate-y-[6px]"
>
MAIN LAGI
</button>
</div>
</div>
);
}
const progressColor = timer > 15 ? 'bg-green-400' : timer > 5 ? 'bg-yellow-400' : 'bg-red-500';
const progressWidth = `${(timer / 30) * 100}%`;
return (
<div className="max-w-4xl mx-auto p-4 sm:p-6 min-h-screen flex flex-col justify-center">
{/* Header Game */}
<div className="flex justify-between items-center glass-panel p-4 mb-6 shadow-md">
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-4">
<div className="bg-indigo-600 text-white px-4 py-1.5 rounded-full font-bold text-sm">
LEVEL {currentLevel + 1} / {GAME_DATA.length}
</div>
<span className="font-bold text-gray-700 flex items-center gap-2">
👤 {playerName}
</span>
</div>
<div className="text-right flex flex-col items-end">
<span className="text-xs font-bold text-gray-500 tracking-wider">SKOR</span>
<span className="title-font text-2xl sm:text-3xl text-indigo-600">{score}</span>
</div>
</div>
{/* Bar Waktu (Timer) */}
<div className="w-full bg-white/50 rounded-full h-5 mb-8 shadow-inner overflow-hidden border-2 border-white/60 backdrop-blur-sm">
<div
className={`h-full transition-all duration-1000 ease-linear ${progressColor} flex justify-end pr-2 items-center`}
style={{ width: progressWidth }}
>
<span className="text-xs font-bold text-white drop-shadow-md">{timer}s</span>
</div>
</div>
{/* Area Permainan Utama */}
<div className="flex-grow flex flex-col items-center w-full">
{/* Petunjuk Bahasa Indonesia */}
<div className="text-center mb-8 w-full">
<div className="bg-white/70 inline-block px-6 py-2 rounded-2xl mb-3 shadow-sm">
<span className="text-gray-600 font-bold text-sm uppercase tracking-wide">Susun kata-kata untuk:</span>
</div>
<h3 className="text-2xl sm:text-3xl font-black text-gray-800 drop-shadow-sm px-4">
"{GAME_DATA[currentLevel].translation}"
</h3>
</div>
{/* Kotak Jawaban (Drop Zone) */}
<div className={`w-full p-6 drop-zone flex flex-wrap justify-center items-center gap-3 mb-8 transition-colors duration-300 relative
${feedback === 'correct' ? 'bg-green-400/50 border-green-500' :
feedback === 'wrong' ? 'bg-red-400/50 border-red-500 animate__animated animate__shakeX' :
feedback === 'timeout' ? 'bg-gray-400/50 border-gray-500' : ''}`}
>
{userAnswer.length === 0 && (
<span className="absolute inset-0 flex items-center justify-center text-gray-600/60 font-bold text-lg sm:text-xl select-none text-center px-4">
Tekan kata di bawah untuk menyusun kalimat
</span>
)}
{userAnswer.map((word, i) => (
<button
key={`ans-${i}`}
onClick={() => handleWordClick(word, i, false)}
className="word-btn bg-white text-indigo-700 border-2 border-indigo-200 px-5 py-3 rounded-2xl font-black text-xl sm:text-2xl z-10 animate__animated animate__bounceIn"
>
{word}
</button>
))}
{feedback === 'correct' && <div className="absolute -top-6 -right-2 text-6xl animate__animated animate__bounceIn">🎯</div>}
{feedback === 'wrong' && <div className="absolute -top-6 -right-2 text-6xl animate__animated animate__bounceIn">❌</div>}
{feedback === 'timeout' && <div className="absolute -top-6 -right-2 text-6xl animate__animated animate__bounceIn">⏰</div>}
</div>
{/* Bank Kata Acak */}
<div className="flex flex-wrap justify-center gap-3 sm:gap-4 w-full p-6 glass-panel min-h-[140px] shadow-lg">
{scrambledWords.map((word, i) => (
<button
key={`scram-${i}`}
onClick={() => handleWordClick(word, i, true)}
className="word-btn bg-indigo-500 text-white px-5 py-3 rounded-2xl font-black text-xl sm:text-2xl animate__animated animate__fadeInUp"
>
{word}
</button>
))}
</div>
{/* Tombol Periksa Jawaban */}
<div className="mt-8 mb-4 w-full sm:w-2/3 md:w-1/2">
<button
onClick={checkAnswer}
disabled={userAnswer.length === 0 || feedback !== null}
className={`w-full title-font text-2xl py-4 rounded-2xl transition transform shadow-[0_6px_0_rgba(0,0,0,0.2)]
${userAnswer.length > 0 && !feedback
? 'bg-blue-500 hover:bg-blue-400 text-white active:translate-y-[6px] active:shadow-none cursor-pointer'
: 'bg-white/50 text-gray-400 opacity-80 cursor-not-allowed border-2 border-transparent'}`}
>
PERIKSA JAWABAN
</button>
</div>
</div>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>

