<title>اسم فامیل سلطنتی | بازی سنتی مدرن</title>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.128.0/build/three.module.js"
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Vazirmatn', sans-serif;
}
body {
min-height: 100vh;
background: linear-gradient(145deg, #1a2a3a 0%, #0f1a24 100%);
overflow-x: auto;
padding: 20px;
position: relative;
}
/* 3D canvas background */
#canvas-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
opacity: 0.4;
pointer-events: none;
}
/* Main UI */
.game-container {
position: relative;
z-index: 10;
max-width: 1400px;
margin: 0 auto;
backdrop-filter: blur(4px);
}
/* header & card style */
.card-glass {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(12px);
border-radius: 40px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 25px 45px rgba(0,0,0,0.2), 0 0 0 1px rgba(255,255,255,0.1) inset;
transition: all 0.2s ease;
}
.btn {
background: linear-gradient(135deg, #ffb347, #ff8c00);
border: none;
padding: 10px 24px;
border-radius: 60px;
font-weight: bold;
color: white;
cursor: pointer;
transition: 0.2s;
font-size: 1rem;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.btn:hover {
transform: scale(1.02);
filter: brightness(1.05);
}
.btn-secondary {
background: linear-gradient(135deg, #2c3e66, #1e2a44);
}
.btn-gold {
background: linear-gradient(135deg, #e6b422, #b47c00);
}
table {
width: 100%;
border-collapse: collapse;
background: rgba(0,0,0,0.4);
border-radius: 30px;
overflow: hidden;
}
th, td {
border: 1px solid rgba(255,215,0,0.3);
padding: 12px 6px;
text-align: center;
color: #f8f9fa;
font-weight: 500;
background: rgba(20,30,45,0.7);
}
th {
background: rgba(0,0,0,0.7);
font-size: 1.1rem;
letter-spacing: 1px;
}
input {
width: 100%;
background: rgba(255,255,240,0.9);
border: none;
padding: 8px;
border-radius: 20px;
text-align: center;
font-weight: 500;
color: #1f2a3e;
}
.score-cell {
background: #2e4a33;
font-weight: bold;
font-size: 1.2rem;
}
.letter-badge {
background: #f0a500;
border-radius: 50px;
padding: 8px 12px;
font-weight: bold;
font-size: 1.4rem;
display: inline-block;
width: 70px;
text-align: center;
box-shadow: 0 0 10px gold;
}
.player-card {
background: rgba(0,0,0,0.6);
border-radius: 30px;
padding: 10px 20px;
display: inline-flex;
align-items: center;
gap: 10px;
margin: 5px;
}
.rank-badge {
width: 32px;
height: 32px;
background: gold;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.shop-item {
background: rgba(0,0,0,0.7);
border-radius: 24px;
padding: 8px;
margin: 6px;
cursor: pointer;
transition: 0.1s;
}
@keyframes float {
0% { transform: translateY(0px);}
100% { transform: translateY(-8px);}
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
backdrop-filter: blur(8px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
visibility: hidden;
}
</style>
مهمان زرین
500 سکه
رتبه: آماتور
پروفایل
فروشگاه
آنلاین دوستانه
آنلاین رندوم
آفلاین با ربات
<!-- Game Panel: Players & current round -->
<div class="card-glass" style="padding: 20px; margin-bottom: 20px;">
<div style="display: flex; justify-content: space-between; flex-wrap: wrap; gap: 15px;">
<div><span style="font-weight: bold;">📢 نوبت:</span> <span id="turn-indicator" class="letter-badge" style="font-size:1rem;">--</span></div>
<div><span>🔤 حرف انتخابی:</span> <span id="selected-letter" class="letter-badge">?</span></div>
<div><button id="choose-letter-btn" class="btn btn-gold">انتخاب حرف (نوبتی)</button></div>
<div><button id="submit-round-btn" class="btn">ثبت دور</button></div>
<div><button id="reset-game-btn" class="btn-secondary btn">بازی جدید</button></div>
</div>
</div>
<!-- 7 columns + letter column | 10 rows -->
<div class="card-glass" style="padding: 10px; overflow-x: auto;">
<table id="game-table">
<thead>
<tr><th>حرف</th><th>اسم</th><th>فامیل</th><th>شهر/کشور</th><th>میوه</th><th>غذا</th><th>اشیاء</th><th>حیوان</th><th>امتیاز ردیف</th></tr>
</thead>
<tbody id="table-body">
<!-- 10 rows will be generated by JS -->
</tbody>
</table>
</div>
<!-- ScoreBoard Players -->
<div class="card-glass" style="margin-top: 20px; padding: 15px;">
<h3><i class="fas fa-chart-line"></i> رتبهبندی بازیکنان</h3>
<div id="players-scoreboard" style="display: flex; gap: 20px; flex-wrap: wrap; margin-top: 10px;"></div>
<div id="final-winner" style="margin-top: 15px; font-weight: bold; font-size: 1.2rem;"></div>
</div>
پروفایل
کاربر: زرین کاویانی
مجموع امتیازات: 0
دستاوردها: 🏆 برنده ۳ دوره | ✨ استاد اسم فامیل
بستن
فروشگاه سکه 🔮
پس زمینه بهار - ۱۰۰ سکه
پس زمینه پاییز - ۱۰۰ سکه
قلم نستعلیق - ۱۵۰ سکه
قلم مدرن - ۱۵۰ سکه
+۱۰۰ سکه (تبلیغات) رایگان با مشاهده آگهی
بستن
توجه: خریدها صرفاً جلوه بصری و شبیهسازی شده
<script type="module">
import * as THREE from 'three';
// 3D background setup
const container = document.getElementById('canvas-container');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 0);
container.appendChild(renderer.domElement);
const geometry = new THREE.TorusKnotGeometry(1.2, 0.3, 128, 16);
const material = new THREE.MeshStandardMaterial({ color: 0xffaa44, emissive: 0x442200, metalness: 0.7, roughness: 0.2 });
const knot = new THREE.Mesh(geometry, material);
scene.add(knot);
const light = new THREE.PointLight(0xffaa66, 1);
light.position.set(2, 3, 4);
scene.add(light);
const ambient = new THREE.AmbientLight(0x404060);
scene.add(ambient);
camera.position.z = 5;
function animate() {
requestAnimationFrame(animate);
knot.rotation.x += 0.005;
knot.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// Game Logic Core
const categories = ["اسم", "فامیل", "شهر یا کشور", "میوه", "غذا", "اشیاء", "حیوان"];
const rowsCount = 10;
let gameState = {
players: [
{ id: 0, name: "بازیکن طلایی", score: 0, isBot: false, answers: [] },
{ id: 1, name: "ماهرخ", score: 0, isBot: false, answers: [] },
{ id: 2, name: "سبزقبا (ربات)", score: 0, isBot: true, answers: [] }
],
currentRound: 0, // 0..9 (10 rounds)
currentLetter: "س",
turnPlayerIndex: 0,
roundData: [], // each round: { letter, rows: [ {اسم, فامیل,...} for each player? complex but we store per cell? }
submittedRounds: 0,
gameActive: true,
mode: "offline_bot" // offline_bot, online_rand, online_friend
};
// Predefined valid word lists (simulating persian lexicon)
const validWords = {
"س": { اسم: ["سارا","سپهر","سینا"], فامیل: ["سعیدی","سلیمانی"], شهر: ["ساری","سنندج"], میوه: ["سیب","سنجد"], غذا: ["ساندویچ","سوپ"], اشیاء: ["صندلی","ساعت"], حیوان: ["سگ","سمور"] },
"م": { اسم: ["مهدی","مریم"], فامیل: ["محمدی","موسوی"], شهر: ["مشهد","مازندران"], میوه: ["موز","ملون"], غذا: ["ماهی","ملغمه"], اشیاء: ["میز","مداد"], حیوان: ["مورچه","ماهی"] },
"ب": { اسم: ["بهمن","بیتا"], فامیل: ["باقری","بابایی"], شهر: ["بوشهر","بابلسر"], میوه: ["به","بلوبری"], غذا: ["برگر","باقالی"], اشیاء: ["بشقاب","بخاری"], حیوان: ["بز","ببر"] },
"ر": { اسم: ["رضا","رها"], فامیل: ["رحیمی","رضایی"], شهر: ["رشت","رامسر"], میوه: ["انار","ریواس"], غذا: ["رشته","رولت"], اشیاء: ["رایانه","ربات"], حیوان: ["روباه","خرگوش"] },
"ش": { اسم: ["شیرین","شهاب"], فامیل: ["شریفی","شیرازی"], شهر: ["شیراز","شاهرود"], میوه: ["شلیل","شاهتوت"], غذا: ["شلهزرد","شامی"], اشیاء: ["شیشه","شمشیر"], حیوان: ["شتر","شیر"] }
};
function getRandomValidWord(letter, categoryIdx) {
const catKey = categories[categoryIdx].replace(/ یا /g,'_').split(' ')[0];
let mapKey = "";
if(categoryIdx===0) mapKey="اسم";
else if(categoryIdx===1) mapKey="فامیل";
else if(categoryIdx===2) mapKey="شهر";
else if(categoryIdx===3) mapKey="میوه";
else if(categoryIdx===4) mapKey="غذا";
else if(categoryIdx===5) mapKey="اشیاء";
else mapKey="حیوان";
const dict = validWords[letter]?.[mapKey] || null;
if(dict && dict.length) return dict[Math.floor(Math.random()*dict.length)];
return `${categories[categoryIdx]}${letter}`;
}
// Generate Table UI for current round ( each row = one player's answers? No, classic: Each row is a different letter; but we implement standard: 10 rows for 10 rounds)
// We'll create a table where each row corresponds to a round (letter) and each cell content is editable for current player? Actually multi-player simultaneous: we need per-player, but for simplicity in this advanced demo, we show "current player answers".
// Better: We implement collaborative: all players see same table, but their answers stored separately. For UI simplicity, each player has separate view? no. We redesign: The game table holds answers for current player (logged in). But multiplayer must show each player's own answers. To handle complexity, I'll implement a system where current 'activePlayer' index can edit and the table shows answers of logged-in user (main user). but bot players automatically fill on submit. This keeps functional and shows core scoring.
// Let's implement standard mode: Main human player fills the table for each round (letter), other bots fill automatically. At end of each round, scoring across all players.
let currentMainPlayer = gameState.players[0];
let tableData = []; // 10 rows, each row { letter, answers: array of 7 strings }
for(let i=0;i${tableData[row].letter || "?"}`;
tr.appendChild(tdLetter);
// 7 category cells
for(let cat=0; cat<7; cat++) {
const td = document.createElement("td");
const input = document.createElement("input");
input.type = "text";
input.value = tableData[row].cells[cat] || "";
input.placeholder = categories[cat];
input.addEventListener("change", (function(r,c) { return (e) => { tableData[r].cells[c] = e.target.value; }; })(row, cat));
td.appendChild(input);
tr.appendChild(td);
}
const tdScore = document.createElement("td");
tdScore.className = "score-cell";
tdScore.id = `row-score-${row}`;
tdScore.innerText = "0";
tr.appendChild(tdScore);
tbody.appendChild(tr);
}
}
function updateScoresUI() {
const boardDiv = document.getElementById("players-scoreboard");
boardDiv.innerHTML = "";
gameState.players.sort((a,b)=>b.score-a.score);
gameState.players.forEach((p,idx)=>{
boardDiv.innerHTML += `
${idx+1} ${p.name} : ${p.score} امتیاز
`;
});
const totalMain = gameState.players[0].score;
document.getElementById("profile-total").innerText = totalMain;
let rankText = totalMain>200?"استاد": totalMain>80?"حرفهای":"آماتور";
document.getElementById("rank-display").innerText = rankText;
}
function calculateRoundScores(letter, roundIndex) {
// gather answers from all players for this round
let perPlayerAnswers = [];
for(let p of gameState.players) {
let playerRowData = p.answers[roundIndex] || { cells: Array(7).fill("") };
perPlayerAnswers.push(playerRowData.cells);
}
let rowTotalScore = 0;
for(let cat=0; cat<7; cat++) {
let answersList = perPlayerAnswers.map(arr=>arr[cat]?.trim()?.toLowerCase() || "");
let uniqueAnswers = [...new Set(answersList.filter(a=>a!==""))];
let countMap = {};
answersList.forEach(a=>{ if(a) countMap[a] = (countMap[a]||0)+1; });
for(let pi=0; pi= rowsCount) { alert("بازی کامل شده! بازی جدید بسازید"); return; }
let letter = tableData[currentRoundIdx].letter;
if(!letter || letter.trim()===""){ alert("ابتدا یک حرف انتخاب کنید"); return; }
let mainAnswers = tableData[currentRoundIdx].cells.slice();
// store main player answers
if(!gameState.players[0].answers[currentRoundIdx]) gameState.players[0].answers[currentRoundIdx]={ cells:[] };
gameState.players[0].answers[currentRoundIdx].cells = mainAnswers;
// fill bot answers automatically
for(let p of gameState.players) {
if(p.isBot) {
let botRow = [];
for(let cat=0;cat<7;cat++){
let fake = getRandomValidWord(letter, cat);
botRow.push(fake);
}
if(!p.answers[currentRoundIdx]) p.answers[currentRoundIdx]={ cells:[] };
p.answers[currentRoundIdx].cells = botRow;
}
}
// scoring
calculateRoundScores(letter, currentRoundIdx);
gameState.currentRound++;
if(gameState.currentRound < rowsCount) {
// prepare next row letter empty
alert(`دور ${gameState.currentRound+1} شروع شد. حرف جدید انتخاب کنید`);
document.getElementById("selected-letter").innerText = "?";
} else {
alert("بازی تمام شد! برندگان:");
let sorted = [...gameState.players].sort((a,b)=>b.score-a.score);
let winnerMsg = `🥇 ${sorted[0].name} ${sorted[0].score} امتیاز `;
if(sorted[1]) winnerMsg += `🥈 ${sorted[1].name} ${sorted[1].score}`;
document.getElementById("final-winner").innerText = winnerMsg;
}
renderTable(); // re-render with cleared next row? but keep data previous rows readonly? For UX, we keep previous answers but disable? simpler: render again showing saved data.
for(let i=0;i<=gameState.currentRound-1;i++){
if(tableData[i]) for(let c=0;c<7;c++) tableData[i].cells[c] = gameState.players[0].answers[i]?.cells[c] || "";
}
renderTable();
}
function chooseLetterByTurn() {
let letters = ['س','م','ب','ر','ش','ف','ک','د','ل','ن'];
let randomLetter = letters[Math.floor(Math.random()*letters.length)];
let roundIdx = gameState.currentRound;
if(roundIdx>=rowsCount) { alert("بازی کامل شده"); return; }
tableData[roundIdx].letter = randomLetter;
document.getElementById("selected-letter").innerText = randomLetter;
renderTable();
}
// mode handlers
document.getElementById("choose-letter-btn").addEventListener("click", chooseLetterByTurn);
document.getElementById("submit-round-btn").addEventListener("click", submitCurrentRound);
document.getElementById("reset-game-btn").addEventListener("click", ()=>{
gameState = { players: gameState.players.map(p=>({...p, score:0, answers:[]})), currentRound:0, currentLetter:"", turnPlayerIndex:0, roundData:[], submittedRounds:0, gameActive:true };
for(let i=0;i{ alert("حالت آفلاین با ربات فعال است"); gameState.mode="offline_bot"; });
document.getElementById("random-online-btn").addEventListener("click",()=>{ alert("حالت آنلاین رندوم (شبیهسازی) با اتصال فرضی"); });
document.getElementById("multiplayer-btn").addEventListener("click",()=>{ alert("کد روم: ۱۲۳۴۵۶ | QR: (شبیهسازی) برای دوستان ارسال کنید"); });
// shop & profile
const shopModal = document.getElementById("shop-modal");
const profileModal = document.getElementById("profile-modal");
document.getElementById("shop-btn").onclick = ()=> shopModal.style.visibility="visible";
document.getElementById("profile-btn").onclick = ()=> profileModal.style.visibility="visible";
document.querySelectorAll(".close-modal").forEach(btn=> btn.addEventListener("click",()=>{
shopModal.style.visibility="hidden";
profileModal.style.visibility="hidden";
}));
document.querySelectorAll(".shop-item").forEach(item=>{
item.addEventListener("click",()=>{
let coins = parseInt(document.getElementById("coin-amount").innerText);
if(item.innerText.includes("۱۰۰ سکه") && coins>=100){
coins-=100;
document.getElementById("coin-amount").innerText = coins;
alert("خرید انجام شد! (تغییر پسزمینه)");
} else if(item.innerText.includes("تبلیغات")){
coins+=100;
document.getElementById("coin-amount").innerText = coins;
alert("+۱۰۰ سکه از مشاهده آگهی");
} else alert("سکه کافی نیست!");
});
});
// initial coin & setup
renderTable();
updateScoresUI();
// chat emoji simulation
const chatDiv = document.createElement("div");
chatDiv.className = "card-glass";
chatDiv.style.position = "fixed";
chatDiv.style.bottom = "20px";
chatDiv.style.left = "20px";
chatDiv.style.padding = "10px";
chatDiv.style.width = "260px";
chatDiv.style.fontSize = "14px";
chatDiv.innerHTML = `
چت سریع:
سلام! 😍 😊`;
document.body.appendChild(chatDiv);
let emojis = ["😂","❤️","👍","🎉","🔥"];
let idx=0;
document.getElementById("emojiBtn")?.addEventListener("click",()=>{
document.getElementById("chat-msg").innerHTML = `بازیکن: ${emojis[idx%emojis.length]}`;
idx++;
});
// for show, add some bot auto fill for demo on first round
setTimeout(()=>{
if(tableData[0].letter==="") chooseLetterByTurn();
},500);
</script>
پروفایل
کاربر: زرین کاویانی
مجموع امتیازات: 0
دستاوردها: 🏆 برنده ۳ دوره | ✨ استاد اسم فامیل
بستنفروشگاه سکه 🔮
توجه: خریدها صرفاً جلوه بصری و شبیهسازی شده