noras-hangman / index.html
ThetaPhiPsi's picture
Add 1 files
a35d7b3 verified
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Noras Wortratespiel</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.hangman-part {
opacity: 0;
transition: opacity 0.3s ease;
}
.hangman-part.visible {
opacity: 1;
}
.letter-tile {
transition: all 0.3s ease;
}
.letter-tile:hover:not(.guessed) {
transform: scale(1.1);
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.bounce {
animation: bounce 0.5s;
}
.confetti {
position: absolute;
width: 10px;
height: 10px;
background-color: #f00;
opacity: 0;
}
.hangman-face {
transition: all 0.3s ease;
}
.happy-face {
transform: rotate(0deg);
}
.sad-face {
transform: rotate(180deg);
}
.final-stats {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-radius: 1rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.progress-ring {
transition: stroke-dashoffset 0.5s;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
</style>
</head>
<body class="bg-blue-50 min-h-screen flex flex-col items-center font-sans">
<header class="w-full bg-blue-600 text-white py-4 shadow-md">
<div class="container mx-auto px-4">
<h1 class="text-3xl font-bold text-center">Noras Wortratespiel</h1>
</div>
</header>
<main class="container mx-auto px-4 py-8 flex-grow flex flex-col items-center">
<div id="game-container" class="w-full max-w-4xl bg-white rounded-xl shadow-lg p-6 mb-8">
<div class="flex flex-col md:flex-row gap-8">
<!-- Hangman Drawing -->
<div class="w-full md:w-1/2 flex flex-col items-center">
<div class="relative w-64 h-64 mb-6">
<!-- Gallows -->
<div class="absolute top-0 left-1/2 transform -translate-x-1/2 w-1 h-10 bg-gray-800 rounded"></div>
<div class="absolute top-0 left-1/4 w-1/2 h-1 bg-gray-800 rounded"></div>
<div class="absolute top-0 left-1/4 w-1 h-64 bg-gray-800 rounded"></div>
<div class="absolute bottom-0 left-0 w-40 h-1 bg-gray-800 rounded"></div>
<!-- Hangman Parts -->
<div id="head" class="hangman-part absolute top-10 left-1/2 transform -translate-x-1/2 w-12 h-12 rounded-full border-4 border-gray-800">
<div id="face" class="hangman-face happy-face absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-8 h-4">
<div class="absolute top-0 left-0 w-2 h-2 bg-gray-800 rounded-full"></div>
<div class="absolute top-0 right-0 w-2 h-2 bg-gray-800 rounded-full"></div>
<div class="absolute bottom-0 left-1/2 transform -translate-x-1/2 w-4 h-1 bg-gray-800 rounded-full"></div>
</div>
</div>
<div id="body" class="hangman-part absolute top-22 left-1/2 transform -translate-x-1/2 w-1 h-16 bg-gray-800 rounded"></div>
<div id="left-arm" class="hangman-part absolute top-24 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-1 h-10 bg-gray-800 rounded origin-top rotate-45"></div>
<div id="right-arm" class="hangman-part absolute top-24 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-1 h-10 bg-gray-800 rounded origin-top -rotate-45"></div>
<div id="left-leg" class="hangman-part absolute top-38 left-1/2 transform -translate-x-1/2 w-1 h-10 bg-gray-800 rounded origin-top rotate-30"></div>
<div id="right-leg" class="hangman-part absolute top-38 left-1/2 transform -translate-x-1/2 w-1 h-10 bg-gray-800 rounded origin-top -rotate-30"></div>
</div>
<div class="text-center">
<p class="text-lg font-semibold mb-2">Fehler: <span id="mistakes">0</span>/8</p>
<div class="w-full bg-gray-200 rounded-full h-4">
<div id="progress-bar" class="bg-green-500 h-4 rounded-full" style="width: 100%"></div>
</div>
</div>
</div>
<!-- Game Area -->
<div class="w-full md:w-1/2 flex flex-col">
<div class="mb-6">
<p class="text-gray-600 mb-2">Kategorie: <span id="category" class="font-semibold">Tiere</span></p>
<p class="text-gray-600 mb-2">Wörter übrig: <span id="words-left" class="font-semibold">10</span></p>
<div id="word-display" class="flex justify-center gap-2 mb-6 flex-wrap"></div>
</div>
<div class="mb-6">
<p class="text-gray-600 mb-2">Verfügbare Buchstaben:</p>
<div id="keyboard" class="flex flex-wrap gap-2 justify-center"></div>
</div>
<div id="hint-area" class="mb-4 p-3 bg-yellow-50 rounded-lg border border-yellow-200 hidden">
<p class="text-yellow-800 font-medium"><i class="fas fa-lightbulb mr-2"></i>Tipp: <span id="hint-text"></span></p>
</div>
<button id="hint-button" class="mb-6 bg-yellow-400 hover:bg-yellow-500 text-yellow-800 font-medium py-2 px-4 rounded-lg transition-colors">
<i class="fas fa-lightbulb mr-2"></i>Tipp anzeigen
</button>
<div id="message" class="hidden mb-4 p-3 rounded-lg text-center font-bold"></div>
<button id="new-game-button" class="mt-auto bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg transition-colors hidden">
Nächstes Wort
</button>
</div>
</div>
</div>
<!-- Game History -->
<div id="game-history" class="w-full max-w-4xl bg-white rounded-xl shadow-lg p-6 mb-8 hidden">
<h2 class="text-xl font-bold mb-4 text-center">Deine Spiele</h2>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="bg-gray-100">
<th class="px-4 py-2 text-left">Datum</th>
<th class="px-4 py-2 text-left">Wort</th>
<th class="px-4 py-2 text-left">Ergebnis</th>
<th class="px-4 py-2 text-left">Fehler</th>
</tr>
</thead>
<tbody id="history-table">
<!-- Filled by JavaScript -->
</tbody>
</table>
</div>
<div class="mt-4 flex justify-between items-center">
<div>
<p class="font-semibold">Statistik:</p>
<p>Gewonnen: <span id="wins">0</span> | Verloren: <span id="losses">0</span></p>
</div>
<button id="toggle-history" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors">
<i class="fas fa-eye-slash mr-2"></i>Verstecken
</button>
</div>
</div>
<!-- Final Statistics -->
<div id="final-stats" class="w-full max-w-4xl final-stats p-8 mb-8 hidden">
<h2 class="text-3xl font-bold text-center mb-6">Spiel beendet! 🎉</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
<div>
<h3 class="text-xl font-semibold mb-4">Zusammenfassung</h3>
<div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-gray-700">Gewonnene Spiele:</span>
<span id="final-wins" class="font-bold text-green-600">0</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-700">Verlorene Spiele:</span>
<span id="final-losses" class="font-bold text-red-600">0</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-700">Wörter erraten:</span>
<span id="words-guessed" class="font-bold text-blue-600">0/0</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-700">Durchschnittliche Fehler:</span>
<span id="avg-mistakes" class="font-bold text-purple-600">0</span>
</div>
</div>
</div>
<div class="flex flex-col items-center">
<svg width="200" height="200" viewBox="0 0 200 200" class="mb-4">
<circle cx="100" cy="100" r="90" fill="none" stroke="#e5e7eb" stroke-width="10"/>
<circle id="win-ring" cx="100" cy="100" r="90" fill="none" stroke="#10b981" stroke-width="10" stroke-dasharray="565.48" stroke-dashoffset="565.48" class="progress-ring"/>
</svg>
<p class="text-center text-gray-700">Erfolgsquote: <span id="success-rate" class="font-bold">0</span>%</p>
</div>
</div>
<div class="mt-8 text-center">
<button id="restart-game-button" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg transition-colors">
<i class="fas fa-redo mr-2"></i>Neues Spiel starten
</button>
</div>
</div>
<div class="w-full max-w-4xl bg-white rounded-xl shadow-lg p-6">
<h2 class="text-xl font-bold mb-4 text-center">Wie spielt man?</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-blue-100 p-4 rounded-lg">
<div class="flex items-center mb-2">
<div class="bg-blue-600 text-white rounded-full w-8 h-8 flex items-center justify-center mr-3">1</div>
<h3 class="font-semibold">Wort erraten</h3>
</div>
<p class="text-gray-700">Klicke auf die Buchstaben, um das geheime Wort zu erraten.</p>
</div>
<div class="bg-green-100 p-4 rounded-lg">
<div class="flex items-center mb-2">
<div class="bg-green-600 text-white rounded-full w-8 h-8 flex items-center justify-center mr-3">2</div>
<h3 class="font-semibold">Achtung Fehler</h3>
</div>
<p class="text-gray-700">Jeder falsche Buchstabe bringt den Strichmännchen näher zum Galgen.</p>
</div>
<div class="bg-yellow-100 p-4 rounded-lg">
<div class="flex items-center mb-2">
<div class="bg-yellow-600 text-white rounded-full w-8 h-8 flex items-center justify-center mr-3">3</div>
<h3 class="font-semibold">Tipps nutzen</h3>
</div>
<p class="text-gray-700">Wenn du nicht weiter weißt, klicke auf den Tipp-Button für Hilfe.</p>
</div>
</div>
</div>
</main>
<footer class="w-full bg-blue-600 text-white py-4 mt-8">
<div class="container mx-auto px-4 text-center">
<p>Viel Spaß beim Spielen, liebe Nora! ❤️</p>
</div>
</footer>
<audio id="correct-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-correct-answer-tone-2870.mp3" preload="auto"></audio>
<audio id="wrong-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-wrong-answer-fail-notification-946.mp3" preload="auto"></audio>
<audio id="win-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3" preload="auto"></audio>
<audio id="lose-sound" src="https://assets.mixkit.co/sfx/preview/mixkit-sad-game-over-675.mp3" preload="auto"></audio>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Game words with categories and hints (easy German words)
const wordCategories = {
tiere: {
name: "Tiere",
words: [
{ word: "HUND", hint: "Bellt und ist der beste Freund des Menschen" },
{ word: "KATZE", hint: "Schnurrt und hat weiche Pfoten" },
{ word: "FISCH", hint: "Schwimmt im Wasser und atmet durch Kiemen" },
{ word: "VOGEL", hint: "Hat Federn und kann fliegen" },
{ word: "MAUS", hint: "Klein, schnell und mag Käse" },
{ word: "PFERD", hint: "Groß, hat eine Mähne und man kann darauf reiten" },
{ word: "KUH", hint: "Gibt Milch und sagt 'Muh'" },
{ word: "HASE", hint: "Hat lange Ohren und hoppelt" },
{ word: "ENTE", hint: "Schwimmt auf dem Teich und sagt 'Quak'" },
{ word: "SCHWEIN", hint: "Ist rosa und wühlt im Schlamm" }
]
},
schule: {
name: "Schule",
words: [
{ word: "BUCH", hint: "Hat viele Seiten mit Geschichten oder Wissen" },
{ word: "STIFT", hint: "Damit schreibt oder malt man" },
{ word: "TASCHE", hint: "Trägt man auf dem Rücken mit Schulmaterial" },
{ word: "LEHRER", hint: "Bringt Kindern etwas bei" },
{ word: "KREIDE", hint: "Damit schreibt man an die Tafel" },
{ word: "LINEAL", hint: "Gerades Werkzeug zum Zeichnen von Linien" },
{ word: "RADIERER", hint: "Damit entfernt man Bleistiftstriche" },
{ word: "HEFT", hint: "Hat leere Seiten zum Schreiben" },
{ word: "FARBE", hint: "Damit malt man bunte Bilder" },
{ word: "SCHERE", hint: "Damit schneidet man Papier" }
]
},
spielzeug: {
name: "Spielzeug",
words: [
{ word: "BALL", hint: "Rund und man kann damit werfen oder kicken" },
{ word: "PUPPE", hint: "Sieht aus wie ein Baby oder eine Person" },
{ word: "AUTO", hint: "Hat Räder und man kann es schieben" },
{ word: "BAUER", hint: "Damit kann man Türme und Häuser bauen" },
{ word: "PUZZLE", hint: "Viele Teile, die zusammengesetzt werden müssen" },
{ word: "SEIL", hint: "Langes Ding zum Springen oder Klettern" },
{ word: "SPIELKARTEN", hint: "Damit kann man viele verschiedene Spiele spielen" },
{ word: "TEDDY", hint: "Weicher Bär zum Kuscheln" },
{ word: "SPIELUHR", hint: "Spielt Musik wenn man sie aufzieht" },
{ word: "KNETE", hint: "Weiches Material zum Formen von Figuren" }
]
},
essen: {
name: "Essen",
words: [
{ word: "APFEL", hint: "Rund, rot oder grün und sehr gesund" },
{ word: "BANANE", hint: "Länglich, gelb und kommt von Bäumen" },
{ word: "BROT", hint: "Wird aus Mehl gebacken und ist meistens braun" },
{ word: "KAESE", hint: "Kommt von der Kuh und ist oft gelb" },
{ word: "EI", hint: "Kommt von Hühnern und hat eine harte Schale" },
{ word: "SUPPE", hint: "Flüssiges Essen, das man löffelt" },
{ word: "NUDELN", hint: "Lange, dünne Teigwaren aus Italien" },
{ word: "EIS", hint: "Kalt, süß und schmilzt in der Sonne" },
{ word: "KEKS", hint: "Klein, knusprig und oft mit Schokolade" },
{ word: "MARMELADE", hint: "Süßer Brotaufstrich aus Früchten" }
]
},
familie: {
name: "Familie",
words: [
{ word: "MAMA", hint: "Die wichtigste Frau in deinem Leben" },
{ word: "PAPA", hint: "Der wichtigste Mann in deinem Leben" },
{ word: "OMA", hint: "Die Mutter deiner Mutter oder deines Vaters" },
{ word: "OPA", hint: "Der Vater deiner Mutter oder deines Vaters" },
{ word: "BRUDER", hint: "Ein männliches Geschwisterkind" },
{ word: "SCHWESTER", hint: "Ein weibliches Geschwisterkind" },
{ word: "TANTE", hint: "Die Schwester deiner Mutter oder deines Vaters" },
{ word: "ONKEL", hint: "Der Bruder deiner Mutter oder deines Vaters" },
{ word: "COUSIN", hint: "Der Sohn deiner Tante oder deines Onkels" },
{ word: "COUSINE", hint: "Die Tochter deiner Tante oder deines Onkels" }
]
},
natur: {
name: "Natur",
words: [
{ word: "BAUM", hint: "Große Pflanze mit Stamm und Blättern" },
{ word: "BLUME", hint: "Schöne Pflanze mit bunten Blüten" },
{ word: "GRAS", hint: "Grüne Pflanze, die auf Wiesen wächst" },
{ word: "SONNE", hint: "Großer, heißer Stern am Himmel" },
{ word: "WOLKE", hint: "Weiß und flauschig am Himmel" },
{ word: "REGEN", hint: "Wasser, das vom Himmel fällt" },
{ word: "SCHNEE", hint: "Weiß, kalt und fällt im Winter" },
{ word: "BERG", hint: "Sehr hoch und man kann ihn besteigen" },
{ word: "FLUSS", hint: "Langes, fließendes Gewässer" },
{ word: "MEER", hint: "Sehr großes Salzwasser" }
]
}
};
// Game state
let currentWord = "";
let guessedLetters = [];
let mistakes = 0;
let maxMistakes = 8;
let hintUsed = false;
let currentCategory = "";
let gameActive = true;
let gameHistory = JSON.parse(localStorage.getItem('norasGameHistory')) || [];
let wins = 0;
let losses = 0;
let usedWords = {};
let wordsLeft = 0;
// Initialize usedWords object
function initUsedWords() {
usedWords = {};
for (const category in wordCategories) {
usedWords[category] = [];
}
}
// DOM elements
const wordDisplay = document.getElementById('word-display');
const keyboard = document.getElementById('keyboard');
const mistakesDisplay = document.getElementById('mistakes');
const progressBar = document.getElementById('progress-bar');
const message = document.getElementById('message');
const newGameButton = document.getElementById('new-game-button');
const hintButton = document.getElementById('hint-button');
const hintArea = document.getElementById('hint-area');
const hintText = document.getElementById('hint-text');
const categoryDisplay = document.getElementById('category');
const gameHistorySection = document.getElementById('game-history');
const historyTable = document.getElementById('history-table');
const winsDisplay = document.getElementById('wins');
const lossesDisplay = document.getElementById('losses');
const toggleHistoryButton = document.getElementById('toggle-history');
const hangmanParts = [
'head', 'body', 'left-arm', 'right-arm',
'left-leg', 'right-leg'
];
const face = document.getElementById('face');
const correctSound = document.getElementById('correct-sound');
const wrongSound = document.getElementById('wrong-sound');
const winSound = document.getElementById('win-sound');
const loseSound = document.getElementById('lose-sound');
const wordsLeftDisplay = document.getElementById('words-left');
const finalStatsSection = document.getElementById('final-stats');
const finalWinsDisplay = document.getElementById('final-wins');
const finalLossesDisplay = document.getElementById('final-losses');
const wordsGuessedDisplay = document.getElementById('words-guessed');
const avgMistakesDisplay = document.getElementById('avg-mistakes');
const successRateDisplay = document.getElementById('success-rate');
const restartGameButton = document.getElementById('restart-game-button');
const winRing = document.getElementById('win-ring');
const gameContainer = document.getElementById('game-container');
// Initialize the game
function initGame() {
// Reset game state
guessedLetters = [];
mistakes = 0;
hintUsed = false;
gameActive = true;
// Clear displays
wordDisplay.innerHTML = '';
keyboard.innerHTML = '';
message.classList.add('hidden');
newGameButton.classList.add('hidden');
hintArea.classList.add('hidden');
hintButton.classList.remove('hidden');
// Reset hangman
hangmanParts.forEach(part => {
document.getElementById(part).classList.remove('visible');
});
// Reset face
face.classList.remove('sad-face');
face.classList.add('happy-face');
// Select a random category that still has words left
const availableCategories = Object.keys(wordCategories).filter(category => {
return wordCategories[category].words.length > usedWords[category].length;
});
// If no categories have words left, show final stats
if (availableCategories.length === 0) {
showFinalStats();
return;
}
currentCategory = availableCategories[Math.floor(Math.random() * availableCategories.length)];
categoryDisplay.textContent = wordCategories[currentCategory].name;
// Select a random word from the category that hasn't been used yet
const availableWords = wordCategories[currentCategory].words.filter(wordObj => {
return !usedWords[currentCategory].includes(wordObj.word);
});
const randomWordObj = availableWords[Math.floor(Math.random() * availableWords.length)];
currentWord = randomWordObj.word;
hintText.textContent = randomWordObj.hint;
// Update words left display
wordsLeft = wordCategories[currentCategory].words.length - usedWords[currentCategory].length - 1;
wordsLeftDisplay.textContent = wordsLeft;
// Update UI
updateMistakesDisplay();
createWordDisplay();
createKeyboard();
// Update game history display
updateGameHistory();
}
// Create the word display with blanks
function createWordDisplay() {
wordDisplay.innerHTML = '';
for (let i = 0; i < currentWord.length; i++) {
const letter = currentWord[i];
const letterElement = document.createElement('div');
letterElement.className = 'w-10 h-10 flex items-center justify-center text-2xl font-bold border-b-2 border-gray-800';
if (guessedLetters.includes(letter)) {
letterElement.textContent = letter;
letterElement.classList.add('text-blue-600');
} else {
letterElement.textContent = ' ';
}
wordDisplay.appendChild(letterElement);
}
}
// Create the keyboard
function createKeyboard() {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ'.split('');
letters.forEach(letter => {
const button = document.createElement('button');
button.textContent = letter;
button.className = 'letter-tile w-10 h-10 flex items-center justify-center bg-blue-100 hover:bg-blue-200 text-blue-800 font-bold rounded-lg transition-all';
if (guessedLetters.includes(letter)) {
button.classList.add('guessed');
button.classList.remove('bg-blue-100', 'hover:bg-blue-200');
button.classList.add('bg-gray-200', 'cursor-not-allowed');
}
button.addEventListener('click', () => handleLetterClick(letter, button));
keyboard.appendChild(button);
});
}
// Handle letter clicks
function handleLetterClick(letter, button) {
if (!gameActive || guessedLetters.includes(letter)) return;
guessedLetters.push(letter);
button.classList.add('guessed');
button.classList.remove('bg-blue-100', 'hover:bg-blue-200');
button.classList.add('bg-gray-200', 'cursor-not-allowed');
if (currentWord.includes(letter)) {
// Correct guess
button.classList.remove('bg-gray-200');
button.classList.add('bg-green-100', 'text-green-800');
button.classList.add('bounce');
setTimeout(() => button.classList.remove('bounce'), 500);
// Play correct sound
correctSound.currentTime = 0;
correctSound.play();
createWordDisplay();
checkWin();
} else {
// Wrong guess
button.classList.remove('bg-gray-200');
button.classList.add('bg-red-100', 'text-red-800');
button.classList.add('bounce');
setTimeout(() => button.classList.remove('bounce'), 500);
// Play wrong sound
wrongSound.currentTime = 0;
wrongSound.play();
mistakes++;
updateMistakesDisplay();
showHangmanPart();
checkLose();
}
}
// Show the next hangman part
function showHangmanPart() {
if (mistakes <= hangmanParts.length) {
const part = hangmanParts[mistakes - 1];
document.getElementById(part).classList.add('visible');
// Make face sad when mistakes reach half
if (mistakes >= maxMistakes / 2) {
face.classList.remove('happy-face');
face.classList.add('sad-face');
}
}
}
// Update mistakes display
function updateMistakesDisplay() {
mistakesDisplay.textContent = mistakes;
const progressPercentage = 100 - (mistakes / maxMistakes * 100);
progressBar.style.width = `${progressPercentage}%`;
if (mistakes >= 5) {
progressBar.classList.remove('bg-green-500');
progressBar.classList.add('bg-yellow-500');
}
if (mistakes >= 7) {
progressBar.classList.remove('bg-yellow-500');
progressBar.classList.add('bg-red-500');
}
}
// Check if player won
function checkWin() {
const wordGuessed = currentWord.split('').every(letter => guessedLetters.includes(letter));
if (wordGuessed) {
gameActive = false;
message.textContent = 'Herzlichen Glückwunsch! Du hast gewonnen! 🎉';
message.classList.remove('hidden');
message.classList.add('bg-green-100', 'text-green-800');
newGameButton.classList.remove('hidden');
// Play win sound
winSound.currentTime = 0;
winSound.play();
// Mark word as used
usedWords[currentCategory].push(currentWord);
// Add to game history
addToGameHistory(true);
createConfetti();
}
}
// Check if player lost
function checkLose() {
if (mistakes >= maxMistakes) {
gameActive = false;
message.textContent = `Schade! Das Wort war: ${currentWord}`;
message.classList.remove('hidden');
message.classList.add('bg-red-100', 'text-red-800');
newGameButton.classList.remove('hidden');
// Play lose sound
loseSound.currentTime = 0;
loseSound.play();
// Mark word as used (only if not already marked)
if (!usedWords[currentCategory].includes(currentWord)) {
usedWords[currentCategory].push(currentWord);
}
// Add to game history
addToGameHistory(false);
// Reveal the word
revealWord();
}
}
// Reveal the word when game is lost
function revealWord() {
wordDisplay.innerHTML = '';
for (let i = 0; i < currentWord.length; i++) {
const letter = currentWord[i];
const letterElement = document.createElement('div');
letterElement.className = 'w-10 h-10 flex items-center justify-center text-2xl font-bold border-b-2 border-gray-800';
letterElement.textContent = letter;
if (guessedLetters.includes(letter)) {
letterElement.classList.add('text-blue-600');
} else {
letterElement.classList.add('text-red-600');
}
wordDisplay.appendChild(letterElement);
}
}
// Add game result to history
function addToGameHistory(won) {
const gameResult = {
date: new Date().toLocaleString(),
word: currentWord,
result: won ? 'Gewonnen' : 'Verloren',
mistakes: mistakes,
won: won
};
gameHistory.unshift(gameResult);
// Keep only last 20 games
if (gameHistory.length > 20) {
gameHistory = gameHistory.slice(0, 20);
}
// Save to localStorage
localStorage.setItem('norasGameHistory', JSON.stringify(gameHistory));
localStorage.setItem('norasUsedWords', JSON.stringify(usedWords));
// Update stats
updateStats();
}
// Update game history display
function updateGameHistory() {
if (gameHistory.length > 0) {
gameHistorySection.classList.remove('hidden');
historyTable.innerHTML = '';
gameHistory.forEach(game => {
const row = document.createElement('tr');
row.className = 'border-b border-gray-200 hover:bg-gray-50';
row.innerHTML = `
<td class="px-4 py-2">${game.date}</td>
<td class="px-4 py-2">${game.word}</td>
<td class="px-4 py-2 ${game.won ? 'text-green-600' : 'text-red-600'}">${game.result}</td>
<td class="px-4 py-2">${game.mistakes}</td>
`;
historyTable.appendChild(row);
});
} else {
gameHistorySection.classList.add('hidden');
}
}
// Update win/loss stats
function updateStats() {
wins = gameHistory.filter(game => game.won).length;
losses = gameHistory.filter(game => !game.won).length;
winsDisplay.textContent = wins;
lossesDisplay.textContent = losses;
}
// Show final statistics
function showFinalStats() {
gameContainer.classList.add('hidden');
finalStatsSection.classList.remove('hidden');
const totalGames = wins + losses;
const successRate = totalGames > 0 ? Math.round((wins / totalGames) * 100) : 0;
const totalMistakes = gameHistory.reduce((sum, game) => sum + game.mistakes, 0);
const avgMistakes = totalGames > 0 ? (totalMistakes / totalGames).toFixed(1) : 0;
// Calculate total words guessed
let totalWords = 0;
let guessedWords = 0;
for (const category in wordCategories) {
totalWords += wordCategories[category].words.length;
guessedWords += usedWords[category].length;
}
finalWinsDisplay.textContent = wins;
finalLossesDisplay.textContent = losses;
wordsGuessedDisplay.textContent = `${guessedWords}/${totalWords}`;
avgMistakesDisplay.textContent = avgMistakes;
successRateDisplay.textContent = successRate;
// Animate the progress ring
const circumference = 2 * Math.PI * 90;
const offset = circumference - (successRate / 100) * circumference;
winRing.style.strokeDashoffset = offset;
// Reset used words for a new game
initUsedWords();
}
// Show hint
hintButton.addEventListener('click', function() {
if (!hintUsed && gameActive) {
hintArea.classList.remove('hidden');
hintButton.classList.add('hidden');
hintUsed = true;
}
});
// New game button
newGameButton.addEventListener('click', initGame);
// Restart game button (from final stats)
restartGameButton.addEventListener('click', function() {
finalStatsSection.classList.add('hidden');
gameContainer.classList.remove('hidden');
initGame();
});
// Toggle game history visibility
toggleHistoryButton.addEventListener('click', function() {
if (gameHistorySection.classList.contains('hidden')) {
gameHistorySection.classList.remove('hidden');
toggleHistoryButton.innerHTML = '<i class="fas fa-eye-slash mr-2"></i>Verstecken';
} else {
gameHistorySection.classList.add('hidden');
toggleHistoryButton.innerHTML = '<i class="fas fa-eye mr-2"></i>Anzeigen';
}
});
// Create confetti effect
function createConfetti() {
const colors = ['#f00', '#0f0', '#00f', '#ff0', '#f0f', '#0ff'];
const container = document.querySelector('main');
for (let i = 0; i < 100; i++) {
const confetti = document.createElement('div');
confetti.className = 'confetti';
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
confetti.style.left = `${Math.random() * 100}%`;
confetti.style.top = '-10px';
confetti.style.transform = `rotate(${Math.random() * 360}deg)`;
container.appendChild(confetti);
setTimeout(() => {
confetti.style.opacity = '1';
confetti.style.top = `${Math.random() * 100}%`;
confetti.style.left = `${Math.random() * 100}%`;
confetti.style.transition = `all ${3 + Math.random() * 5}s ease-out`;
setTimeout(() => {
confetti.style.opacity = '0';
setTimeout(() => confetti.remove(), 1000);
}, 3000);
}, 0);
}
}
// Load used words from localStorage
const savedUsedWords = localStorage.getItem('norasUsedWords');
if (savedUsedWords) {
usedWords = JSON.parse(savedUsedWords);
} else {
initUsedWords();
}
// Start the game
initGame();
updateStats();
});
</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=ThetaPhiPsi/noras-hangman" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>