Spaces:
Running
Running
| <html lang="fa" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>بازی اسمفامیل آنلاین</title> | |
| <!-- Importing Vazirmatn Font for modern Persian typography --> | |
| <link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" /> | |
| <!-- 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-color: #4f46e5; | |
| --primary-hover: #4338ca; | |
| --secondary-color: #10b981; | |
| --danger-color: #ef4444; | |
| --background-color: #f3f4f6; | |
| --card-bg: #ffffff; | |
| --text-color: #1f2937; | |
| --border-radius: 12px; | |
| --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| --transition: all 0.3s ease; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| font-family: 'Vazirmatn', sans-serif; | |
| } | |
| body { | |
| background-color: var(--background-color); | |
| color: var(--text-color); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* Header */ | |
| header { | |
| background-color: var(--card-bg); | |
| padding: 1rem 2rem; | |
| box-shadow: var(--shadow); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .logo { | |
| font-size: 1.5rem; | |
| font-weight: bold; | |
| color: var(--primary-color); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .anycoder-link { | |
| font-size: 0.9rem; | |
| color: #6b7280; | |
| text-decoration: none; | |
| transition: var(--transition); | |
| } | |
| .anycoder-link:hover { | |
| color: var(--primary-color); | |
| } | |
| /* Main Container */ | |
| main { | |
| flex: 1; | |
| padding: 2rem; | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| width: 100%; | |
| display: flex; | |
| justify-content: center; | |
| align-items: flex-start; | |
| } | |
| /* Sections */ | |
| section { | |
| display: none; /* Hidden by default */ | |
| width: 100%; | |
| animation: fadeIn 0.5s ease-out; | |
| } | |
| section.active { | |
| display: block; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| /* Cards */ | |
| .card { | |
| background: var(--card-bg); | |
| border-radius: var(--border-radius); | |
| padding: 2rem; | |
| box-shadow: var(--shadow); | |
| text-align: center; | |
| } | |
| h1, h2, h3 { | |
| margin-bottom: 1rem; | |
| color: var(--text-color); | |
| } | |
| p { | |
| margin-bottom: 1.5rem; | |
| color: #4b5563; | |
| line-height: 1.6; | |
| } | |
| /* Buttons */ | |
| .btn { | |
| padding: 0.75rem 1.5rem; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| text-decoration: none; | |
| } | |
| .btn-primary { | |
| background-color: var(--primary-color); | |
| color: white; | |
| } | |
| .btn-primary:hover { | |
| background-color: var(--primary-hover); | |
| transform: translateY(-2px); | |
| } | |
| .btn-secondary { | |
| background-color: var(--secondary-color); | |
| color: white; | |
| } | |
| .btn-secondary:hover { | |
| background-color: #059669; | |
| } | |
| .btn-danger { | |
| background-color: var(--danger-color); | |
| color: white; | |
| } | |
| .btn-outline { | |
| background-color: transparent; | |
| border: 2px solid var(--primary-color); | |
| color: var(--primary-color); | |
| } | |
| .btn-outline:hover { | |
| background-color: var(--primary-color); | |
| color: white; | |
| } | |
| /* Setup Section */ | |
| .difficulty-options { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 1rem; | |
| margin-bottom: 2rem; | |
| } | |
| .diff-card { | |
| border: 2px solid #e5e7eb; | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| } | |
| .diff-card:hover { | |
| border-color: var(--primary-color); | |
| background-color: #eef2ff; | |
| } | |
| .diff-card.selected { | |
| border-color: var(--primary-color); | |
| background-color: #e0e7ff; | |
| box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.1); | |
| } | |
| .diff-icon { | |
| font-size: 2rem; | |
| margin-bottom: 0.5rem; | |
| color: var(--primary-color); | |
| } | |
| /* Game Section */ | |
| .game-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 2rem; | |
| flex-wrap: wrap; | |
| gap: 1rem; | |
| } | |
| .letter-display { | |
| font-size: 4rem; | |
| font-weight: 900; | |
| color: var(--primary-color); | |
| background: #e0e7ff; | |
| width: 100px; | |
| height: 100px; | |
| border-radius: 50%; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| margin: 0 auto; | |
| box-shadow: inset 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .timer-display { | |
| font-size: 2rem; | |
| font-weight: bold; | |
| color: var(--danger-color); | |
| display: none; /* Hidden for easy mode */ | |
| } | |
| .timer-display.active { | |
| display: block; | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 1rem; | |
| justify-content: center; | |
| margin-bottom: 1rem; | |
| } | |
| .categories-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); | |
| gap: 1rem; | |
| margin-bottom: 2rem; | |
| } | |
| .category-item { | |
| background: #f9fafb; | |
| padding: 1rem; | |
| border-radius: 8px; | |
| border: 1px solid #e5e7eb; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| position: relative; | |
| } | |
| .category-item label { | |
| font-weight: bold; | |
| color: #4b5563; | |
| display: flex; | |
| justify-content: space-between; | |
| } | |
| .category-item input[type="text"] { | |
| padding: 0.75rem; | |
| border: 2px solid #e5e7eb; | |
| border-radius: 6px; | |
| font-size: 1rem; | |
| transition: var(--transition); | |
| text-align: right; | |
| } | |
| .category-item input[type="text"]:focus { | |
| outline: none; | |
| border-color: var(--primary-color); | |
| background-color: white; | |
| } | |
| .duplicate-check { | |
| font-size: 0.8rem; | |
| color: #6b7280; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.3rem; | |
| margin-top: 0.2rem; | |
| cursor: pointer; | |
| } | |
| /* Letter Selection Modal */ | |
| .letter-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); | |
| gap: 0.5rem; | |
| margin-top: 1rem; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| padding: 1rem; | |
| border: 1px solid #e5e7eb; | |
| border-radius: 8px; | |
| } | |
| .letter-btn { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 8px; | |
| border: 1px solid #d1d5db; | |
| background: white; | |
| cursor: pointer; | |
| font-weight: bold; | |
| transition: var(--transition); | |
| } | |
| .letter-btn:hover { | |
| background: var(--primary-color); | |
| color: white; | |
| border-color: var(--primary-color); | |
| } | |
| /* Results Section */ | |
| .score-summary { | |
| font-size: 3rem; | |
| font-weight: 900; | |
| color: var(--secondary-color); | |
| margin: 1rem 0; | |
| } | |
| .result-details { | |
| text-align: right; | |
| margin-top: 2rem; | |
| } | |
| .result-row { | |
| display: flex; | |
| justify-content: space-between; | |
| padding: 0.5rem; | |
| border-bottom: 1px solid #f3f4f6; | |
| } | |
| .result-row.total { | |
| border-top: 2px solid #e5e7eb; | |
| border-bottom: none; | |
| font-weight: bold; | |
| font-size: 1.2rem; | |
| margin-top: 1rem; | |
| padding-top: 1rem; | |
| } | |
| .score-badge { | |
| padding: 0.2rem 0.6rem; | |
| border-radius: 4px; | |
| font-size: 0.85rem; | |
| font-weight: bold; | |
| } | |
| .score-10 { background: #d1fae5; color: #065f46; } | |
| .score-5 { background: #fef3c7; color: #92400e; } | |
| .score-0 { background: #fee2e2; color: #991b1b; } | |
| /* Footer */ | |
| footer { | |
| text-align: center; | |
| padding: 1.5rem; | |
| color: #6b7280; | |
| font-size: 0.9rem; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 600px) { | |
| header { padding: 1rem; } | |
| main { padding: 1rem; } | |
| .letter-display { width: 80px; height: 80px; font-size: 3rem; } | |
| .categories-grid { grid-template-columns: 1fr; } | |
| .game-header { flex-direction: column; } | |
| } | |
| /* Utility */ | |
| .hidden { display: none ; } | |
| .dice-shake { animation: shake 0.5s; } | |
| @keyframes shake { | |
| 0% { transform: rotate(0deg); } | |
| 25% { transform: rotate(10deg); } | |
| 50% { transform: rotate(-10deg); } | |
| 75% { transform: rotate(5deg); } | |
| 100% { transform: rotate(0deg); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="logo"> | |
| <i class="fas fa-puzzle-piece"></i> | |
| اسمفامیل | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| Built with anycoder <i class="fas fa-external-link-alt" style="font-size: 0.7rem;"></i> | |
| </a> | |
| </header> | |
| <main> | |
| <!-- Section 1: Setup / Difficulty --> | |
| <section id="setup-section" class="active"> | |
| <div class="card"> | |
| <h1>به بازی اسمفامیل خوش آمدید!</h1> | |
| <p>سطح دشواری بازی را انتخاب کنید تا شروع کنیم.</p> | |
| <div class="difficulty-options"> | |
| <div class="diff-card" onclick="selectDifficulty('easy', this)"> | |
| <div class="diff-icon"><i class="fas fa-smile"></i></div> | |
| <h3>آسان</h3> | |
| <p>بدون محدودیت زمانی</p> | |
| </div> | |
| <div class="diff-card selected" onclick="selectDifficulty('medium', this)"> | |
| <div class="diff-icon"><i class="fas fa-stopwatch"></i></div> | |
| <h3>چالشی</h3> | |
| <p>۵ دقیقه زمان</p> | |
| </div> | |
| <div class="diff-card" onclick="selectDifficulty('hard', this)"> | |
| <div class="diff-icon"><i class="fas fa-fire"></i></div> | |
| <h3>حرفهای</h3> | |
| <p>۶۰ ثانیه زمان</p> | |
| </div> | |
| </div> | |
| <button class="btn btn-primary" onclick="startGame()"> | |
| <i class="fas fa-play"></i> شروع بازی | |
| </button> | |
| </div> | |
| </section> | |
| <!-- Section 2: Game Board --> | |
| <section id="game-section"> | |
| <div class="card"> | |
| <div class="game-header"> | |
| <div class="letter-display" id="current-letter">?</div> | |
| <div class="timer-display" id="timer">00:00</div> | |
| </div> | |
| <div class="controls" id="pre-start-controls"> | |
| <button class="btn btn-outline" onclick="rollDice()"> | |
| <i class="fas fa-dice"></i> تاس انداز (تصادفی) | |
| </button> | |
| <button class="btn btn-outline" onclick="toggleManualSelect()"> | |
| <i class="fas fa-keyboard"></i> انتخاب دستی | |
| </button> | |
| </div> | |
| <!-- Manual Letter Selection (Hidden by default) --> | |
| <div id="manual-letter-container" class="hidden" style="margin-bottom: 1rem;"> | |
| <p>حرف مورد نظر را انتخاب کنید:</p> | |
| <div class="letter-grid" id="letter-grid"> | |
| <!-- JS will populate this --> | |
| </div> | |
| </div> | |
| <!-- Start Timer Button (Hidden until letter picked) --> | |
| <div id="start-timer-container" class="hidden" style="margin-bottom: 1rem;"> | |
| <button class="btn btn-primary" onclick="startTimer()"> | |
| <i class="fas fa-hourglass-start"></i> شروع تایمر و نوشتن | |
| </button> | |
| </div> | |
| <form id="game-form" onsubmit="event.preventDefault();"> | |
| <div class="categories-grid" id="categories-container"> | |
| <!-- Inputs will be generated here --> | |
| </div> | |
| </form> | |
| <div class="controls" id="in-game-controls" style="display:none;"> | |
| <button class="btn btn-danger" onclick="finishGame()"> | |
| <i class="fas fa-stop"></i> پایان دور | |
| </button> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Section 3: Results --> | |
| <section id="result-section"> | |
| <div class="card"> | |
| <h2>نتایج دور</h2> | |
| <p>امتیاز شما برای حرف "<span id="result-letter" style="font-weight:bold;"></span>":</p> | |
| <div class="score-summary" id="final-score">0</div> | |
| <div class="result-details" id="result-breakdown"> | |
| <!-- Breakdown rows --> | |
| </div> | |
| <div class="controls" style="margin-top: 2rem;"> | |
| <button class="btn btn-secondary" onclick="resetToGame()"> | |
| <i class="fas fa-redo"></i> دور بعد | |
| </button> | |
| <button class="btn btn-outline" onclick="goHome()"> | |
| <i class="fas fa-home"></i> صفحه اصلی | |
| </button> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <footer> | |
| <p>طراحی شده برای سرگرمی و چالش گروهی. © ۲۰۲۳</p> | |
| </footer> | |
| <script> | |
| // --- Game Data & State --- | |
| const CATEGORIES = [ | |
| 'اسم', 'فامیل', 'شهر', 'کشور', 'غذا', | |
| 'حیوان', 'رنگ', 'شغل', 'ماشین', 'میوه' | |
| ]; | |
| // Excluding rare letters to keep the game flow smooth, but allowing manual override | |
| const LETTERS = [ | |
| 'ا', 'ب', 'پ', 'ت', 'ث', 'ج', 'چ', 'ح', 'خ', | |
| 'د', 'ذ', 'ر', 'ز', 'ژ', 'س', 'ش', 'ص', 'ض', | |
| 'ط', 'ظ', 'ع', 'غ', 'ف', 'ق', 'ک', 'گ', 'ل', | |
| 'م', 'ن', 'و', 'ه', 'ی' | |
| ]; | |
| const DIFFICULTY_SETTINGS = { | |
| 'easy': { time: null, label: 'آسان' }, | |
| 'medium': { time: 300, label: 'چالشی' }, // 5 mins | |
| 'hard': { time: 60, label: 'حرفهای' } // 60 sec | |
| }; | |
| let state = { | |
| difficulty: 'medium', | |
| currentLetter: '', | |
| timerInterval: null, | |
| timeLeft: 0, | |
| isPlaying: false | |
| }; | |
| // --- DOM Elements --- | |
| const sections = { | |
| setup: document.getElementById('setup-section'), | |
| game: document.getElementById('game-section'), | |
| result: document.getElementById('result-section') | |
| }; | |
| const elements = { | |
| letterDisplay: document.getElementById('current-letter'), | |
| timerDisplay: document.getElementById('timer'), | |
| categoriesContainer: document.getElementById('categories-container'), | |
| manualLetterContainer: document.getElementById('manual-letter-container'), | |
| letterGrid: document.getElementById('letter-grid'), | |
| preStartControls: document.getElementById('pre-start-controls'), | |
| startTimerContainer: document.getElementById('start-timer-container'), | |
| inGameControls: document.getElementById('in-game-controls'), | |
| finalScore: document.getElementById('final-score'), | |
| resultLetter: document.getElementById('result-letter'), | |
| resultBreakdown: document.getElementById('result-breakdown') | |
| }; | |
| // --- Initialization --- | |
| function init() { | |
| renderLetterGrid(); | |
| loadHighScore(); | |
| } | |
| // --- Navigation & UI Logic --- | |
| function switchSection(sectionName) { | |
| Object.values(sections).forEach(sec => sec.classList.remove('active')); | |
| sections[sectionName].classList.add('active'); | |
| } | |
| function selectDifficulty(diff, element) { | |
| state.difficulty = diff; | |
| document.querySelectorAll('.diff-card').forEach(el => el.classList.remove('selected')); | |
| element.classList.add('selected'); | |
| } | |
| function toggleManualSelect() { | |
| elements.manualLetterContainer.classList.toggle('hidden'); | |
| } | |
| // --- Core Game Logic --- | |
| function startGame() { | |
| switchSection('game'); | |
| resetGameState(); | |
| renderCategories(); | |
| // Wait for user to pick a letter | |
| } | |
| function resetGameState() { | |
| clearInterval(state.timerInterval); | |
| state.currentLetter = ''; | |
| state.timeLeft = DIFFICULTY_SETTINGS[state.difficulty].time; | |
| state.isPlaying = false; | |
| elements.letterDisplay.textContent = '?'; | |
| elements.timerDisplay.textContent = '00:00'; | |
| elements.timerDisplay.classList.remove('active'); | |
| elements.preStartControls.style.display = 'flex'; | |
| elements.startTimerContainer.classList.add('hidden'); | |
| elements.inGameControls.style.display = 'none'; | |
| elements.manualLetterContainer.classList.add('hidden'); | |
| // Clear inputs | |
| elements.categoriesContainer.innerHTML = ''; | |
| } | |
| function renderLetterGrid() { | |
| elements.letterGrid.innerHTML = ''; | |
| LETTERS.forEach(letter => { | |
| const btn = document.createElement('button'); | |
| btn.textContent = letter; | |
| btn.className = 'letter-btn'; | |
| btn.onclick = () => selectLetter(letter); | |
| elements.letterGrid.appendChild(btn); | |
| }); | |
| } | |
| function rollDice() { | |
| // Animation effect | |
| elements.letterDisplay.classList.add('dice-shake'); | |
| setTimeout(() => elements.letterDisplay.classList.remove('dice-shake'), 500); | |
| // Random selection | |
| const randomIndex = Math.floor(Math.random() * LETTERS.length); | |
| const letter = LETTERS[randomIndex]; | |
| // Small delay for animation | |
| setTimeout(() => { | |
| selectLetter(letter); | |
| }, 300); | |
| } | |
| function selectLetter(letter) { | |
| state.currentLetter = letter; | |
| elements.letterDisplay.textContent = letter; | |
| elements.manualLetterContainer.classList.add('hidden'); | |
| // Show start timer button | |
| elements.preStartControls.style.display = 'none'; | |
| elements.startTimerContainer.classList.remove('hidden'); | |
| // Focus on first input if possible (optional, but good UX) | |
| // const firstInput = document.querySelector('input[type="text"]'); | |
| // if(firstInput) firstInput.focus(); | |
| } | |
| function renderCategories() { | |
| elements.categoriesContainer.innerHTML = ''; | |
| CATEGORIES.forEach(cat => { | |
| const div = document.createElement('div'); | |
| div.className = 'category-item'; | |
| div.innerHTML = ` | |
| <label for="cat-${cat}">${cat}</label> | |
| <input type="text" id="cat-${cat}" data-category="${cat}" autocomplete="off" disabled> | |
| <label class="duplicate-check"> | |
| <input type="checkbox" id="dup-${cat}" disabled> | |
| تکراری بودن (۵ امتیاز) | |
| </label> | |
| `; | |
| elements.categoriesContainer.appendChild(div); | |
| }); | |
| } | |
| function startTimer() { | |
| state.isPlaying = true; | |
| elements.startTimerContainer.classList.add('hidden'); | |
| // Enable inputs | |
| const inputs = elements.categoriesContainer.querySelectorAll('input'); | |
| inputs.forEach(input => input.disabled = false); | |
| // Show controls and timer | |
| elements.inGameControls.style.display = 'flex'; | |
| const settings = DIFFICULTY_SETTINGS[state.difficulty]; | |
| if (settings.time) { | |
| elements.timerDisplay.classList.add('active'); | |
| state.timeLeft = settings.time; | |
| updateTimerDisplay(); | |
| state.timerInterval = setInterval(() => { | |
| state.timeLeft--; | |
| updateTimerDisplay(); | |
| if (state.timeLeft <= 0) { | |
| finishGame(); | |
| } | |
| }, 1000); | |
| } else { | |
| elements.timerDisplay.classList.remove('active'); | |
| // Easy mode: no timer, just play until finished | |
| } | |
| } | |
| function updateTimerDisplay() { | |
| const minutes = Math.floor(state.timeLeft / 60); | |
| const seconds = state.timeLeft % 60; | |
| elements.timerDisplay.textContent = | |
| `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; | |
| if(state.timeLeft <= 10) { | |
| elements.timerDisplay.style.color = '#dc2626'; // Red alert | |
| } else { | |
| elements.timerDisplay.style.color = '#ef4444'; | |
| } | |
| } | |
| function finishGame() { | |
| clearInterval(state.timerInterval); | |
| state.isPlaying = false; | |
| // Disable inputs | |
| const inputs = elements.categoriesContainer.querySelectorAll('input'); | |
| inputs.forEach(input => input.disabled = true); | |
| calculateScore(); | |
| } | |
| // --- Scoring System --- | |
| function calculateScore() { | |
| let totalScore = 0; | |
| let breakdownHTML = ''; | |
| CATEGORIES.forEach(cat => { | |
| const inputVal = document.getElementById(`cat-${cat}`).value.trim(); | |
| const isDuplicate = document.getElementById(`dup-${cat}`).checked; | |
| let score = 0; | |
| let statusClass = 'score-0'; | |
| let statusText = 'بدون پاسخ'; | |
| // Logic: | |
| // 1. If empty -> 0 | |
| // 2. If starts with correct letter -> 10 | |
| // 3. If Duplicate -> 5 | |
| // 4. If starts with wrong letter -> 0 (User honesty system) | |
| if (inputVal.length > 0) { | |
| const firstChar = inputVal.charAt(0); | |
| // Normalize check (e.g., 'آ' vs 'ا') | |
| if (checkLetterMatch(firstChar, state.currentLetter)) { | |
| if (isDuplicate) { | |
| score = 5; | |
| statusClass = 'score-5'; | |
| statusText = 'تکراری (۵ امتیاز)'; | |
| } else { | |
| score = 10; | |
| statusClass = 'score-10'; | |
| statusText = 'صحیح (۱۰ امتیاز)'; | |
| } | |
| } else { | |
| score = 0; | |
| statusClass = 'score-0'; | |
| statusText = 'نامعتبر (حرف اشتباه)'; | |
| } | |
| } | |
| totalScore += score; | |
| breakdownHTML += ` | |
| <div class="result-row"> | |
| <span>${cat}: ${inputVal ? `<strong>${inputVal}</strong>` : '-'}</span> | |
| <span class="score-badge ${statusClass}">${statusText}</span> | |
| </div> | |
| `; | |
| }); | |
| breakdownHTML += ` | |
| <div class="result-row total"> | |
| <span>جمع کل</span> | |
| <span>${totalScore} امتیاز</span> | |
| </div> | |
| `; | |
| // Update UI | |
| elements.finalScore.textContent = totalScore; | |
| elements.resultLetter.textContent = state.currentLetter; | |
| elements.resultBreakdown.innerHTML = breakdownHTML; | |
| updateHighScore(totalScore); | |
| switchSection('result'); | |
| } | |
| // Helper to match Persian letters (handling Alef variations roughly) | |
| function checkLetterMatch(char, target) { | |
| const map = { | |
| 'آ': 'ا', | |
| 'أ': 'ا', | |
| 'إ': 'ا', | |
| 'ئ': 'ی', // sometimes treated as ya | |
| 'ة': 'ت', | |
| 'ی': 'ی', | |
| 'ي': 'ی', | |
| 'ک': 'ک', | |
| 'ك': 'ک' | |
| }; | |
| const normalizedChar = map[char] || char; | |
| const normalizedTarget = map[target] || target; | |
| return normalizedChar === normalizedTarget; | |
| } | |
| // --- Storage Logic --- | |
| function updateHighScore(score) { | |
| let highScores = JSON.parse(localStorage.getItem('asmFamilHighScores')) || {}; | |
| const currentLabel = DIFFICULTY_SETTINGS[state.difficulty].label; | |
| if (!highScores[currentLabel] || score > highScores[currentLabel]) { | |
| highScores[currentLabel] = score; | |
| localStorage.setItem('asmFamilHighScores', JSON.stringify(highScores)); | |
| // Show a simple toast or message (using a small DOM element) | |
| showToast(`رکورد جدید برای سطح ${currentLabel}!`); | |
| } | |
| } | |
| function loadHighScore() { | |
| // Could display high scores on the main screen if desired | |
| } | |
| function showToast(message) { | |
| // Create a temporary toast element | |
| const toast = document.createElement('div'); | |
| toast.textContent = message; | |
| toast.style.cssText = ` | |
| position: fixed; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: #10b981; | |
| color: white; | |
| padding: 10px 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| z-index: 1000; | |
| animation: fadeIn 0.3s; | |
| `; | |
| document.body.appendChild(toast); | |
| setTimeout(() => toast.remove(), 3000); | |
| } | |
| // --- Navigation Handlers --- | |
| function resetToGame() { | |
| switchSection('game'); | |
| resetGameState(); | |
| } | |
| function goHome() { | |
| switchSection('setup'); | |
| } | |
| // Run Init | |
| init(); | |
| </script> | |
| </body> | |
| </html> |