anycoder-869b9071 / index.html
samirerty's picture
Upload folder using huggingface_hub
a87630c verified
<!DOCTYPE html>
<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 !important; }
.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>طراحی شده برای سرگرمی و چالش گروهی. &copy; ۲۰۲۳</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>