إذا كنت تبحث عن فكرة تطبيق سهلة نسبيًا ويمكن تنفيذها دون الحاجة إلى بناء تطبيق أندرويد من الصفر، فإن تطبيقات المسابقات والأسئلة والأجوبة من الأفكار المناسبة جدًا للبدء.
في هذا الدليل سنشرح بالتفصيل، ومن البداية تمامًا، طريقة إنشاء تطبيق مسابقات احترافي باستخدام منصة AppCreator24، ثم إضافة قسم HTML ووضع كود تطبيق المسابقات داخله بعد تخصيصه، مع شرح أهم أجزاء الكود التي يمكنك تعديلها، وأفكار تحقيق الدخل من التطبيق.
سنستخدم في المثال تطبيقًا باسم:
تحدّاك
ويمكنك استبدال الاسم والهوية والألوان والمحتوى باسم تطبيقك الخاص.
أولًا: ما الذي سننشئه؟
التطبيق الذي سنعمل عليه ليس مجرد صفحة تحتوي على مجموعة أسئلة، وإنما واجهة مسابقات تفاعلية تحتوي على مجموعة من الوظائف، منها:
- معلومات عامة.
- إسلاميات.
- رياضة.
- علوم.
- تاريخ.
- تقنية.
- أسئلة عن مصر.
- ألغاز.
- جغرافيا.
- أدب وشعر.
- أسئلة مخصصة.
- تحدي اليوم.
- وضع البقاء.
- تحدي السرعة.
- عجلة الحظ.
- متجر للأفاتار.
- نظام العملات.
- نظام النقاط.
- نظام الخبرة XP.
- المستويات.
- الإنجازات.
- المتصدرون.
- الملف الشخصي.
- الإحصائيات.
- الوضع الداكن.
- المؤثرات الصوتية.
- الاهتزاز.
- حفظ البيانات على الجهاز.
- مشاركة نتيجة الاختبار.
- إضافة أسئلة خاصة بالمستخدم.
وهذا يجعل التطبيق أقرب إلى لعبة مسابقات صغيرة وليس مجرد صفحة أسئلة تقليدية.
القسم الأول: الدخول إلى منصة AppCreator24
ابدأ بالدخول إلى منصة AppCreator24 من المتصفح، ثم قم بتسجيل الدخول إلى حسابك.
بعد الدخول ستظهر لك الصفحة الرئيسية الخاصة بحسابك، ومنها تستطيع إنشاء تطبيق جديد وإدارة التطبيقات الموجودة لديك.
إذا كانت هذه أول مرة تستخدم فيها المنصة، لا تقلق؛ معظم خطواتنا ستكون من خلال واجهة المنصة نفسها، بينما سيكون الكود مسؤولًا عن واجهة المسابقات ووظائفها.
القسم الثاني: إنشاء تطبيق جديد
من الصفحة الرئيسية في AppCreator24 ابحث عن خيار:
إنشاء تطبيق جديد
ثم ابدأ في إدخال البيانات الأساسية للتطبيق.
1. اسم التطبيق
في خانة اسم التطبيق اكتب مثلًا:
تحدّاك
ويمكنك اختيار أي اسم آخر، ولكن يفضل أن يكون:
- قصيرًا.
- سهل التذكر.
- مناسبًا لفكرة التطبيق.
- سهل البحث عنه.
- غير مستخدم كعلامة تجارية محمية.
مثال:
تحدّاك – مسابقات وأسئلة
أو:
تحدّاك | اختبر معلوماتك
القسم الثالث: اختيار شعار التطبيق
بعد ذلك ستحتاج إلى إضافة لوجو التطبيق.
يفضل أن يكون الشعار بسيطًا وواضحًا، لأن المستخدم سيراه بحجم صغير على شاشة الهاتف.
بالنسبة لتطبيق مسابقات، يمكن استخدام عناصر مثل:
- علامة استفهام.
- كأس.
- برق.
- تاج.
- دماغ.
- عجلة حظ.
- حرف من اسم التطبيق.
مثال مناسب لتطبيق "تحدّاك":
علامة استفهام أو كأس مع كلمة تحدّاك.
ويفضل استخدام ألوان قوية ومتناسقة مع تصميم التطبيق.
في الكود الذي سنستخدمه لاحقًا اللون الأساسي هو البنفسجي، لذلك يمكن أن يكون اللوجو متناسقًا معه.
القسم الرابع: كتابة الوصف القصير
من المهم كتابة وصف مختصر يوضح فكرة التطبيق.
يمكنك استخدام وصف مثل:
اختبر معلوماتك، نافس أصدقاءك، اجمع العملات، وارفع مستواك من خلال مسابقات وتحديات متنوعة.
ويفضل ألا يكون الوصف مجرد كلمات عامة، بل يوضح للمستخدم ماذا سيجد داخل التطبيق.
القسم الخامس: إنشاء التطبيق
بعد الانتهاء من البيانات الأساسية اضغط على زر إنشاء التطبيق.
ستنتقل بعد ذلك إلى لوحة التحكم الخاصة بالتطبيق.
وهنا تبدأ المرحلة المهمة.
القسم السادس: إنشاء قسم HTML
من لوحة التحكم الخاصة بالتطبيق ابحث عن خيار إضافة قسم جديد.
اختر:
إضافة قسم جديد
ثم ابحث عن نوع القسم:
HTML
أو القسم المخصص لإضافة كود HTML حسب الواجهة الحالية للمنصة.
بعد اختيار قسم HTML، ستظهر لك الصفحة التي تسمح بإضافة كود HTML الخاص بك.
وهنا سنضع الكود الكامل لتطبيق المسابقات.
لكن لا أنصح بوضع الكود كما هو مباشرة قبل فهم طريقة تعديله، لأن الهدف من هذا الدليل هو أن تستطيع تخصيص التطبيق بنفسك لاحقًا.
القسم السابع: قبل وضع الكود — افهم تركيب التطبيق
الكود الذي لديك يتكون بشكل أساسي من ثلاثة أجزاء:
<html>
<head>
...
</head>
<body>
...
</body>
<script>
...
</script>
</html>
كل جزء له وظيفة مختلفة.
HTML
مسؤول عن:
- الصفحات.
- الأزرار.
- العناوين.
- البطاقات.
- النوافذ.
- الأقسام.
CSS
مسؤول عن:
- الألوان.
- الخطوط.
- الأحجام.
- المسافات.
- شكل الأزرار.
- البطاقات.
- الوضع الداكن.
- الحركات والمؤثرات.
JavaScript
مسؤول عن:
- الأسئلة.
- الإجابات.
- حساب النقاط.
- العملات.
- المستويات.
- التحديات.
- عجلة الحظ.
- المتجر.
- حفظ البيانات.
- الإحصائيات.
- التفاعل مع الأزرار.
وبالتالي لا تحتاج إلى إعادة بناء التطبيق بالكامل عند كل تعديل.
القسم الثامن: تغيير اسم التطبيق داخل الكود
في بداية الكود ستجد:
<title>تحدّاك — النسخة النهائية والمطورة</title>
يمكنك تغييرها إلى اسم تطبيقك.
مثال:
<title>مسابقاتي — تطبيق المسابقات</title>
ثم ابحث عن:
<div class="brand">
وستجد اسم:
تحدّاك
استبدله باسم تطبيقك.
القسم التاسع: تغيير اسم المستخدم الافتراضي
داخل الكود ستجد:
name: "بدر"
إذا أردت أن يظهر اسم مختلف بشكل افتراضي، قم بتغييره.
مثلًا:
name: "زائر"
ولكن هناك نقطة مهمة:
اسم المستخدم هنا يتم حفظه داخل localStorage، لذلك إذا سبق تشغيل التطبيق على الجهاز، قد يستمر الاسم القديم في الظهور بسبب البيانات المحفوظة.
في هذه الحالة يمكن استخدام زر تصفير البيانات من الإعدادات أو حذف بيانات الموقع/التطبيق أثناء الاختبار.
القسم العاشر: تعديل الألوان
من أهم أجزاء التصميم هذا الجزء:
:root{
--bg:#f5f6fb;
--card:#ffffff;
--soft:#eef0f7;
--text:#171827;
--muted:#777b90;
--primary:#6c5ce7;
--primary2:#8b7cf6;
--success:#16a56b;
--danger:#e5484d;
--gold:#f5b72f;
--blue:#3d8bfd;
}
هذه المتغيرات تتحكم في معظم ألوان التطبيق.
أهم لونين هما:
--primary:#6c5ce7;
--primary2:#8b7cf6;
إذا أردت جعل التطبيق أزرق مثلًا يمكنك تغييرهما إلى درجات الأزرق.
وإذا أردت اللون الأحمر أو الأخضر أو البرتقالي، يمكنك تغيير القيم بنفس الطريقة.
ميزة استخدام المتغيرات أنك لا تحتاج إلى البحث عن كل زر وتغيير لونه يدويًا؛ تغيير اللون الأساسي يمكن أن ينعكس على أجزاء كثيرة من التطبيق.
القسم الحادي عشر: تغيير الخط
الكود يستخدم خط:
Cairo
من خلال:
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800;900;1000&display=swap">
وهذا مناسب جدًا للواجهات العربية.
كما يتم استخدامه داخل CSS:
font-family:"Cairo",Tahoma,Arial,sans-serif;
يمكنك استبداله بخط عربي آخر إذا أردت، بشرط أن يكون متاحًا بالطريقة التي تستخدمها.
القسم الثاني عشر: مكتبة الأيقونات
التطبيق يستخدم:
Font Awesome 6
من خلال:
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
ولهذا نستطيع استخدام أيقونات مثل:
<i class="fa-solid fa-house"></i>
للمنزل.
ومثل:
<i class="fa-solid fa-trophy"></i>
للكأس.
ومثل:
<i class="fa-solid fa-brain"></i>
للذكاء.
وهذا يجعل التصميم أكثر احترافية من استخدام الرموز النصية فقط.
القسم الثالث عشر: إضافة أو تعديل أقسام المسابقات
داخل JavaScript توجد قاعدة بيانات الأسئلة:
const questions = {
وبداخلها ستجد الأقسام:
general
islamic
sport
science
history
tech
egypt
riddles
geography
literature
كل قسم يحتوي على مجموعة أسئلة.
مثلًا:
general:[
["ما هي أكبر قارة من حيث المساحة؟",
["آسيا","أفريقيا","أوروبا","أمريكا الجنوبية"],0]
]
تركيب السؤال هو:
السؤال
الإجابات
رقم الإجابة الصحيحة
والترقيم يبدأ من الصفر.
أي:
0 = الإجابة الأولى
1 = الإجابة الثانية
2 = الإجابة الثالثة
3 = الإجابة الرابعة
القسم الرابع عشر: طريقة إضافة سؤال جديد
إذا أردت إضافة سؤال جديد إلى قسم المعلومات العامة، تستطيع كتابة:
["من هو مخترع المصباح الكهربائي؟",
["توماس إديسون","نيوتن","أينشتاين","جاليليو"],0],
الرقم:
0
يعني أن الإجابة الأولى هي الصحيحة.
ولو كانت الإجابة الصحيحة هي الثانية:
["السؤال هنا",
["إجابة 1","إجابة 2","إجابة 3","إجابة 4"],1],
القسم الخامس عشر: إضافة مجال مسابقات جديد
إذا أردت إضافة مجال جديد، مثل:
سينما
يمكنك أولًا إنشاء مجموعة أسئلة:
cinema:[
["من أخرج فيلمًا معينًا؟",
["إجابة","إجابة","إجابة","إجابة"],0]
]
ثم تضيف اسم المجال إلى:
const names = {
ثم تضيفه إلى قائمة:
const categories = [
وتختار له أيقونة مناسبة.
بهذه الطريقة يمكن توسيع التطبيق وإضافة عشرات الأقسام.
القسم السادس عشر: فهم نظام النقاط
التطبيق لا يعطي المستخدم عددًا ثابتًا من النقاط فقط.
عند الإجابة الصحيحة يتم حساب النقاط بناءً على:
- الإجابة الصحيحة.
- الوقت المتبقي.
- السلسلة المتتالية.
في الكود ستجد:
gained = 10 + quiz.time * 2 + (quiz.streak >= 3 ? 10 : 0);
وهذا يعني أن المستخدم يحصل على نقاط إضافية كلما أجاب بسرعة، مع مكافأة إضافية عند تحقيق سلسلة من الإجابات الصحيحة.
يمكنك تعديل المعادلة إذا أردت جعل اللعبة أصعب أو أسهل.
القسم السابع عشر: نظام العملات
التطبيق يحتوي على عملات افتراضية.
في البداية:
coins: 1250
أي أن المستخدم يبدأ بـ:
1250 عملة.
ويحصل على عملات عند الإجابة الصحيحة.
كما يستطيع استخدام العملات في:
- حذف إجابتين.
- تخطي السؤال.
- تمديد الوقت.
- شراء الأفاتار.
القسم الثامن عشر: تعديل أسعار المساعدات
داخل الاختبار توجد ثلاثة أدوات:
حذف إجابتين
30 عملة
تخطي السؤال
25 عملة
تمديد الوقت
15 عملة
يمكنك تغيير هذه القيم حسب نظام اقتصاد التطبيق.
لكن لا تجعل الحصول على العملات سهلًا جدًا مقارنة بأسعار المتجر؛ لأن ذلك يلغي قيمة العملة داخل اللعبة.
القسم التاسع عشر: تعديل وقت الأسئلة
الاختبار العادي يستخدم:
time: 15
أي:
15 ثانية.
أما تحدي السرعة فيستخدم:
time: 6
أي:
6 ثوانٍ.
يمكنك مثلًا جعل الاختبار العادي 20 ثانية:
time: 20
وتحدي السرعة 5 ثوانٍ:
time: 5
القسم العشرون: تحدي اليوم
التطبيق يحتوي على تحدي يومي.
يتم إنشاء مجموعة أسئلة مختلفة بناءً على تاريخ اليوم باستخدام:
dailySeed()
وبالتالي يستطيع التطبيق اختيار أسئلة اليوم بطريقة مختلفة عن الاختبار العادي.
وعند إنهاء تحدي اليوم يحصل المستخدم على مكافأة:
state.coins += 100;
أي:
100 عملة.
وهذه فكرة جيدة لزيادة العودة اليومية للتطبيق.
القسم الحادي والعشرون: وضع البقاء
وضع البقاء من أكثر الأوضاع التي تجعل التطبيق شبيهًا بالألعاب.
فكرة الوضع:
تستمر في الإجابة عن الأسئلة، وأول إجابة خاطئة تنهي المحاولة.
ويتم تشغيله من:
startQuiz('survival')
ويمكن تطويره مستقبلًا ليحتوي على:
- مراحل.
- مستويات صعوبة.
- أرقام قياسية.
- مكافآت خاصة.
- ترتيب عالمي.
- منافسة بين اللاعبين.
القسم الثاني والعشرون: تحدي السرعة
تحدي السرعة يعتمد على وجود وقت قصير لكل سؤال.
في التطبيق الحالي:
6 ثوانٍ لكل سؤال.
وهذا النوع من التحديات مفيد جدًا لأنه يعطي المستخدم إحساسًا بالمنافسة والضغط، ويمكن لاحقًا إضافة مستويات مثل:
- سرعة عادية.
- سرعة متقدمة.
- سرعة جنونية.
القسم الثالث والعشرون: عجلة الحظ
التطبيق يحتوي على عجلة حظ يومية.
وتوجد القيم:
const wheelValues = [50, 100, 150, 200, 300, 500];
أي يمكن أن يحصل المستخدم على:
- 50 عملة.
- 100 عملة.
- 150 عملة.
- 200 عملة.
- 300 عملة.
- 500 عملة.
ويتم السماح باستخدام العجلة مرة واحدة يوميًا.
وهذه الميزة يمكن استخدامها مستقبلًا مع الإعلانات لتحقيق دخل، مثل:
شاهد إعلانًا للحصول على محاولة إضافية.
القسم الرابع والعشرون: صانع الأسئلة
من المميزات المهمة وجود:
صانع الأسئلة
حيث يستطيع المستخدم إضافة أسئلته بنفسه.
يكتب:
- السؤال.
- الإجابة الصحيحة.
- ثلاث إجابات خاطئة.
ثم يتم حفظ السؤال داخل:
state.customQs
ويستطيع المستخدم تشغيل أسئلته من قسم:
أسئلتي المخصصة
القسم الخامس والعشرون: متجر الأفاتار
يوجد داخل التطبيق متجر بسيط للأفاتار.
مثل:
- 🦁 الأسد.
- 👑 التاج.
- 🥷 النينجا.
- 🤖 الروبوت.
ويمكن تغيير الأسعار من:
const shopItems = [
مثلًا:
{
id:'av_lion',
name:'أفاتار الأسد',
icon:'🦁',
cost:300,
type:'avatar'
}
إذا أردت أن يصبح سعر الأسد 500 عملة، قم بتغيير:
cost:300
إلى:
cost:500
القسم السادس والعشرون: الإنجازات
يوجد نظام إنجازات يجعل المستخدم لديه أهداف مستمرة.
مثل:
أول فوز
سلسلة 5
سلسلة 10
50 سؤال
100 سؤال
50 إجابة صحيحة
5000 نقطة
10000 نقطة
المستوى 5
3 تحديات يومية
اختبار كامل
ملك السرعة
وهذا النظام مهم جدًا في تطبيقات الألعاب لأنه يعطي المستخدم سببًا للاستمرار في اللعب.
القسم السابع والعشرون: المستوى وXP
كل إجابة صحيحة تضيف XP.
وعند الوصول إلى:
1000 XP
يرتفع مستوى اللاعب.
ويحصل على مكافأة عملات.
في الكود:
state.level++;
state.coins += 100;
أي أن اللاعب يحصل على:
100 عملة إضافية عند رفع المستوى.
القسم الثامن والعشرون: حفظ بيانات المستخدم
من أهم أجزاء الكود:
localStorage
ويتم استخدامه لحفظ بيانات المستخدم على الجهاز.
مثل:
- الاسم.
- العملات.
- النقاط.
- المستوى.
- الإنجازات.
- الأفاتار.
- الأسئلة المخصصة.
- الإعدادات.
- سجل الاختبارات.
وهذا يسمح للمستخدم بإغلاق التطبيق والعودة إليه دون فقدان تقدمه على نفس الجهاز/بيئة التخزين.
لكن يجب الانتباه إلى أن localStorage ليس قاعدة بيانات سحابية حقيقية؛ فهو مناسب لحفظ بيانات التطبيق محليًا، لكنه لا يوفر تلقائيًا حسابًا متزامنًا بين عدة أجهزة أو نظام منافسة عالمي حقيقي.
القسم التاسع والعشرون: الوضع الداكن
التطبيق يحتوي على Dark Mode.
ويتم التحكم به من:
state.settings.dark
وعند تفعيله يتم إضافة:
.dark
إلى الصفحة.
ويمكنك تعديل ألوان الوضع الداكن من الجزء:
.dark{
...
}
القسم الثلاثون: الصوت والاهتزاز
التطبيق يحتوي أيضًا على:
المؤثرات الصوتية
باستخدام Web Audio API.
ويحتوي على أصوات مختلفة مثل:
- الإجابة الصحيحة.
- الإجابة الخاطئة.
- الضغط على الأزرار.
كما يوجد الاهتزاز عند الإجابة إذا كان الجهاز يدعم ذلك.
ويمكن للمستخدم إيقاف:
- الصوت.
- الاهتزاز.
من الإعدادات.
القسم الحادي والثلاثون: صفحة حسابي
صفحة الحساب تعرض:
- اسم اللاعب.
- الأفاتار.
- المستوى.
- العملات.
- السلسلة الحالية.
- عدد الأسئلة.
- نسبة الدقة.
- إجمالي النقاط.
- أفضل سلسلة.
- رسمًا بيانيًا للأداء.
ويتم استخدام مكتبة:
Chart.js
لعرض الرسم البياني.
القسم الثاني والثلاثون: المتصدرون
التطبيق يحتوي على صفحة للمتصدرين.
وهي تعرض مجموعة من اللاعبين التجريبيين، ثم تحسب مركز المستخدم بناءً على نقاطه.
لكن يجب الانتباه إلى نقطة مهمة جدًا:
المتصدرون في هذا الكود محليون/تجريبيون وليسوا نظام ترتيب عالمي حقيقي بين جميع مستخدمي التطبيق.
لإنشاء متصدرين حقيقيين يحتاج التطبيق إلى قاعدة بيانات وخادم أو خدمة Backend تقوم بتخزين نتائج المستخدمين ومزامنتها عبر الإنترنت.
القسم الثالث والثلاثون: أهم نقطة قبل نشر التطبيق
بعد الانتهاء من تعديل الكود، انسخ الكود كاملًا والصقه داخل قسم HTML الذي أنشأته في AppCreator24.
لا تحذف بالخطأ:
<style>
أو:
<script>
أو:
</body>
لأن حذف جزء من الكود قد يؤدي إلى توقف وظائف التطبيق.
القسم الرابع والثلاثون: اختبار التطبيق
بعد وضع الكود، لا تنتقل مباشرة إلى النشر.
اختبر كل شيء.
جرّب:
- فتح الصفحة الرئيسية.
- فتح الاختبارات.
- تشغيل كل قسم.
- الإجابة الصحيحة.
- الإجابة الخاطئة.
- انتهاء الوقت.
- حذف إجابتين.
- تخطي السؤال.
- تمديد الوقت.
- إنهاء الاختبار.
- مشاركة النتيجة.
- عجلة الحظ.
- إضافة سؤال.
- المتجر.
- شراء أفاتار.
- تغيير الأفاتار.
- الإنجازات.
- الملف الشخصي.
- الإعدادات.
- Dark Mode.
- الصوت.
- الاهتزاز.
- تغيير الاسم.
- إعادة التقدم.
ولا تعتمد على أن الكود يعمل من أول مرة؛ الاختبار على الهاتف خطوة أساسية.
القسم الخامس والثلاثون: كيف تجعل التطبيق قابلًا للربح؟
هنا نصل إلى الجزء الأهم.
وجود تطبيق مسابقات جيد لا يعني تلقائيًا أنك ستحصل على دخل.
الربح يحتاج إلى:
مستخدمين + استخدام متكرر + إعلانات أو مصادر دخل أخرى.
ومن أفضل النماذج التي يمكن استخدامها:
1. الإعلانات
يمكن تحقيق الدخل من الإعلانات التي تظهر داخل التطبيق، بشرط الالتزام بسياسات شبكة الإعلانات والمنصة التي تستخدمها.
يمكن وضع الإعلان في أماكن لا تزعج المستخدم، مثل:
- بين أقسام التطبيق.
- بعد انتهاء الاختبار.
- في بعض الشاشات.
- مقابل مكافأة اختيارية.
القسم السادس والثلاثون: الإعلانات مقابل المكافآت
هذه من أفضل الأفكار لتطبيق مسابقات.
مثلًا بعد انتهاء المستخدم من اختبار يمكن عرض خيار:
🎁 شاهد إعلانًا واحصل على 50 عملة
أو:
⏱️ شاهد إعلانًا لتحصل على وقت إضافي
أو:
🎡 شاهد إعلانًا للحصول على محاولة إضافية في عجلة الحظ
لكن يجب تنفيذ هذا باستخدام نظام إعلانات فعلي يدعم Rewarded Ads، وليس بمجرد كتابة زر داخل HTML.
القسم السابع والثلاثون: الاشتراك المدفوع
يمكنك مستقبلًا إنشاء نسخة Premium تحتوي على مزايا مثل:
- إزالة الإعلانات.
- أسئلة إضافية.
- أفاتارات حصرية.
- تحديات خاصة.
- إحصائيات متقدمة.
- مستويات إضافية.
- محتوى حصري.
وهذا يحول التطبيق من مجرد تطبيق مجاني إلى نموذج:
Free + Premium
القسم الثامن والثلاثون: شراء العملات
يمكن تطوير متجر التطبيق ليحتوي على حزم عملات حقيقية.
مثل:
1000 عملة
5000 عملة
10000 عملة
لكن هذا الجزء يحتاج إلى نظام شراء داخل التطبيق أو خدمة دفع مناسبة، ولا يكفي تعديل قيمة coins داخل JavaScript.
ويجب أيضًا الالتزام بسياسات متجر التطبيقات وخدمة الدفع المستخدمة.
القسم التاسع والثلاثون: كيف تزيد تحميلات التطبيق؟
أفضل تطبيق في العالم لن ينجح إذا لم يعرف الناس بوجوده.
لذلك أنشئ حسابات وصفحات باسم التطبيق على:
- Facebook.
- Instagram.
- TikTok.
- YouTube.
وانشر محتوى قصيرًا مثل:
هل تستطيع الإجابة خلال 5 ثوانٍ؟
ثم تعرض سؤالًا وتطلب من المشاهد اختيار الإجابة.
وفي نهاية الفيديو:
حمّل تطبيق تحدّاك واختبر نفسك.
هذه الطريقة مناسبة جدًا لتطبيقات المسابقات لأن محتوى التطبيق نفسه يمكن تحويله إلى محتوى تسويقي.
القسم الأربعون: فكرة المحتوى اليومي
بدلًا من نشر إعلان مباشر للتطبيق كل يوم، أنشئ سلسلة:
سؤال اليوم
مثال:
🧠 سؤال اليوم:
ما أكبر محيط في العالم؟
أ) الأطلسي
ب) الهندي
ج) الهادئ
د) المتجمد
ثم في نهاية المنشور:
الإجابة داخل تطبيق تحدّاك 🔥
بهذه الطريقة يتحول التسويق إلى لعبة أيضًا.
القسم الحادي والأربعون: تحسين التطبيق قبل تحقيق الربح
لا تبدأ بوضع الإعلانات في كل مكان.
الأفضل أن تجعل الأولوية:
التجربة → المستخدم → العودة اليومية → ثم الربح.
إذا فتحت اللعبة ووجدت إعلانًا قبل أن تبدأ، ثم إعلانًا بعد كل سؤال، ثم إعلانًا آخر عند الانتقال بين الصفحات، فمن الطبيعي أن يغادر المستخدم.
لذلك الإعلانات يجب أن تكون محسوبة.
القسم الثاني والأربعون: أفكار تطوير مستقبلية
يمكن تطوير التطبيق بشكل أكبر بكثير من النسخة الحالية.
ومن أهم التطويرات المستقبلية:
المنافسة الحقيقية بين لاعبين
لاعب ضد لاعب.
غرف مسابقات
إنشاء غرفة وإرسال كود لصديق.
ترتيب عالمي
ترتيب جميع المستخدمين حسب النقاط.
نظام حسابات
تسجيل الدخول وحفظ التقدم على السحابة.
قاعدة أسئلة أونلاين
إضافة آلاف الأسئلة من قاعدة بيانات بدل وضعها كلها داخل HTML.
مستويات صعوبة
- سهل.
- متوسط.
- صعب.
- خبير.
بطولات أسبوعية
بطولة تبدأ يوم الجمعة وتنتهي الخميس مثلًا.
جوائز موسمية
مكافآت لأفضل اللاعبين.
نظام دعوة الأصدقاء
دعوة صديق والحصول على مكافأة.
نظام مهام
مثل:
حل 20 سؤالًا اليوم.
أو:
حقق 5 إجابات صحيحة متتالية.
القسم الثالث والأربعون: أخطاء يجب تجنبها
هناك بعض الأخطاء التي قد تؤدي إلى مشاكل في التطبيق.
الخطأ الأول: تعديل جزء من JavaScript بطريقة غير صحيحة
خصوصًا الأقواس:
{}
والفواصل:
,
لذلك عند إضافة سؤال جديد تأكد من وضع الفاصلة في المكان الصحيح.
الخطأ الثاني: كتابة رقم إجابة صحيحة غير موجود
إذا كانت لديك أربع إجابات:
0
1
2
3
لا تضع:
4
لأن هذا يعني أن هناك إجابة خامسة غير موجودة.
الخطأ الثالث: حذف مكتبة خارجية
التطبيق يعتمد على مكتبات خارجية للأيقونات والخط وبعض المؤثرات.
إذا حذفت روابط المكتبات دون معرفة وظيفتها، قد تختفي الأيقونات أو تتوقف بعض الوظائف.
الخطأ الرابع: الاعتماد على localStorage في كل شيء
localStorage ممتاز للتقدم المحلي، لكنه ليس بديلًا عن قاعدة البيانات إذا أردت:
- حسابات حقيقية.
- ترتيبًا عالميًا.
- مزامنة بين الأجهزة.
- مسابقات مباشرة.
- بيانات مركزية.
القسم الرابع والأربعون: الطريقة الصحيحة للعمل على الكود
إذا كنت ستعدل التطبيق بنفسك، لا تغير كل شيء في وقت واحد.
اعمل بهذه الطريقة:
المرحلة الأولى
غيّر:
- الاسم.
- اللوجو.
- الوصف.
- الألوان.
- الهوية.
المرحلة الثانية
غيّر:
- الأقسام.
- الأسئلة.
- الإجابات.
- المكافآت.
المرحلة الثالثة
اختبر:
- النقاط.
- العملات.
- المستويات.
- الإنجازات.
المرحلة الرابعة
اختبر:
- عجلة الحظ.
- المتجر.
- الإعدادات.
- Dark Mode.
المرحلة الخامسة
اختبر التطبيق بالكامل على الهاتف.
المرحلة السادسة
ابدأ إضافة نظام تحقيق الدخل.
هذه الطريقة أفضل من تعديل عشرات الأجزاء في وقت واحد ثم البحث عن الخطأ عندما يتوقف التطبيق.
<!doctype html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="theme-color" content="#6c5ce7">
<title>تحدّاك — النسخة النهائية والمطورة</title>
<!-- مكتبة الأيقونات FontAwesome 6 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- خط Cairo من Google Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800;900;1000&display=swap">
<!-- مكتبة المؤثرات الاحتفالية Canvas Confetti -->
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
<!-- مكتبة الرسم البياني Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root{
--bg:#f5f6fb;--card:#ffffff;--soft:#eef0f7;--text:#171827;--muted:#777b90;
--primary:#6c5ce7;--primary2:#8b7cf6;--success:#16a56b;--danger:#e5484d;
--gold:#f5b72f;--blue:#3d8bfd;--line:#e6e8f0;--shadow:0 12px 35px rgba(36,31,82,.08);
--r:22px; --glow: rgba(108, 92, 231, 0.35);
}
*{box-sizing:border-box; transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;}
html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--text);font-family:"Cairo",Tahoma,Arial,sans-serif;-webkit-tap-highlight-color:transparent;}
button,input,select{font:inherit}
button{border:0;cursor:pointer;color:inherit;outline:none;}
button:active{transform: scale(0.97);}
.app{max-width:560px;min-height:100vh;margin:auto;padding-bottom:100px;position:relative;}
header{position:sticky;top:0;z-index:20;padding:14px 16px 10px;background:rgba(245,246,251,.88);backdrop-filter:blur(15px);-webkit-backdrop-filter:blur(15px);display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid rgba(0,0,0,0.03);}
.dark header{background:rgba(17,18,27,.88); border-bottom:1px solid rgba(255,255,255,0.05);}
.brand{font-size:24px;font-weight:1000;color:var(--primary);letter-spacing:-1px;display:flex;align-items:center;gap:8px;}
.headBtns{display:flex;gap:8px}
.ib{width:43px;height:43px;border-radius:15px;background:var(--card);box-shadow:var(--shadow);display:grid;place-items:center;transition:0.2s;}
.ib i{font-size:18px;color:var(--primary);}
.ib:hover{transform:translateY(-2px);box-shadow:0 15px 30px var(--glow);}
main{padding:4px 15px}.page{display:none; animation: fadeIn 0.35s cubic-bezier(0.16, 1, 0.3, 1);}.page.active{display:block}
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.hero{padding:21px;border-radius:28px;background:linear-gradient(135deg,#5d4fe0,#8d77fa);color:white;box-shadow:0 18px 38px #6c5ce733;margin:8px 0 14px;position:relative;overflow:hidden;}
.hero::before{content:""; position:absolute; top:-50%; right:-50%; width:200%; height:200%; background:radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 60%); pointer-events:none;}
.heroTop{display:flex;justify-content:space-between;align-items:center;position:relative;z-index:2;}
.avatar{width:51px;height:51px;border-radius:17px;background:#ffffff25;border:1px solid #ffffff35;display:grid;place-items:center;font-size:23px;font-weight:900;box-shadow:0 8px 16px rgba(0,0,0,0.15);}
.hero h1{margin:0;font-size:23px}.hero p{margin:5px 0 0;font-size:13px;opacity:.86}
.heroStats{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:18px;position:relative;z-index:2;}
.hs{background:#ffffff1d;border:1px solid #ffffff20;border-radius:16px;text-align:center;padding:9px;backdrop-filter:blur(5px);}
.hs b{display:block;font-size:17px;display:flex;align-items:center;justify-content:center;gap:4px;}
.hs small{font-size:10px;opacity:.82}
.card{background:var(--card);border-radius:var(--r);box-shadow:var(--shadow);padding:16px;margin:11px 0;position:relative;overflow:hidden;}
.row{display:flex;align-items:center;gap:10px}.between{justify-content:space-between}.muted{color:var(--muted)}.small{font-size:12px}
.section{display:flex;align-items:center;justify-content:space-between;margin:22px 2px 11px}.section h2{font-size:17px;margin:0;display:flex;align-items:center;gap:8px;}.link{background:none;color:var(--primary);font-weight:800;font-size:12px;display:flex;align-items:center;gap:4px;}
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
.tile{min-height:112px;text-align:right;background:var(--card);border-radius:20px;padding:15px;box-shadow:var(--shadow);position:relative;overflow:hidden;transition:0.25s ease;border:1px solid transparent;}
.tile:hover{transform:translateY(-3px);border-color:var(--primary2);}
.tile:after{content:"";position:absolute;width:70px;height:70px;border-radius:50%;background:#6c5ce710;left:-25px;bottom:-25px}
.tile .ico{width:42px;height:42px;border-radius:13px;background:#f0efff;color:var(--primary);display:grid;place-items:center;font-size:18px;}
.tile b{display:block;margin-top:10px;font-size:14px}.tile small{display:block;color:var(--muted);font-size:11px;margin-top:3px}
.challenge{display:flex;align-items:center;gap:12px;padding:14px;border-radius:19px;background:var(--card);box-shadow:var(--shadow);margin:9px 0;transition:0.2s;}
.challenge:hover{transform:scale(1.01);}
.challengeIcon{width:46px;height:46px;border-radius:15px;background:#f1efff;color:var(--primary);display:grid;place-items:center;font-size:20px;}
.challengeText{flex:1}.challengeText b{font-size:14px;display:block;}.challengeText small{display:block;color:var(--muted);font-size:11px;margin-top:4px}
.go,.primary{background:linear-gradient(135deg,var(--primary),var(--primary2));color:white;border-radius:13px;padding:10px 16px;font-weight:900;font-size:12px;box-shadow:0 6px 16px var(--glow);display:inline-flex;align-items:center;justify-content:center;gap:6px;}
.secondary{background:var(--soft);color:var(--text);border-radius:13px;padding:10px 16px;font-weight:800;font-size:12px;display:inline-flex;align-items:center;justify-content:center;gap:6px;}
.progress{height:9px;background:var(--soft);border-radius:99px;overflow:hidden;position:relative;}
.progress i{display:block;height:100%;border-radius:99px;background:linear-gradient(90deg,var(--primary),var(--primary2));transition:width 0.4s ease;}
.nav{position:fixed;bottom:0;left:50%;transform:translateX(-50%);width:min(560px,100%);height:78px;background:var(--card);border-top:1px solid var(--line);z-index:30;display:grid;grid-template-columns:repeat(6,1fr);padding:7px 2px env(safe-area-inset-bottom);box-shadow:0 -10px 25px rgba(0,0,0,0.03);}
.nav button{background:none;color:var(--muted);font-size:9px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;transition:0.2s;}
.nav button i{font-size:16px;}
.nav button.active{color:var(--primary);font-weight:900;transform:translateY(-2px);}
.badge{padding:5px 9px;border-radius:99px;background:#f0efff;color:var(--primary);font-size:10px;font-weight:900;display:inline-flex;align-items:center;gap:4px;}
.gold{color:#bd7e00;background:#fff5d9}.green{color:#0c8758;background:#e9faf3}
.list{background:var(--card);border-radius:20px;box-shadow:var(--shadow);padding:6px 15px}
.li{display:flex;align-items:center;gap:11px;padding:13px 0;border-bottom:1px solid var(--line)}.li:last-child{border-bottom:0}
.rank{width:31px;text-align:center;font-weight:900;font-size:14px;}.miniAv{width:40px;height:40px;border-radius:13px;background:linear-gradient(135deg,#6c5ce7,#9b8cff);color:#fff;display:grid;place-items:center;font-weight:900;}
.liMain{flex:1}.liMain b{font-size:13px}.liMain small{display:block;color:var(--muted);font-size:10px;margin-top:2px}
.statGrid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
.statCard{background:var(--card);box-shadow:var(--shadow);border-radius:19px;padding:15px;display:flex;flex-direction:column;gap:4px;}
.statCard i{font-size:20px;color:var(--primary);margin-bottom:4px;}
.statCard b{font-size:22px;display:block}.statCard small{color:var(--muted);font-size:11px}
.ach{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
.achievement{background:var(--card);border-radius:19px;padding:15px;box-shadow:var(--shadow);text-align:center;position:relative;}
.achievement .medal{width:55px;height:55px;border-radius:18px;background:#fff4d5;color:#c58b00;display:grid;place-items:center;margin:auto;font-size:24px;}
.achievement.lock .medal{filter:grayscale(1);opacity:.35;}
.achievement b{display:block;font-size:13px;margin-top:9px}.achievement small{color:var(--muted);font-size:10px}
/* التطوير الفاخر لبطاقة صفحة حسابي */
.profileHero {
text-align: center;
background: linear-gradient(135deg, #6c5ce7 0%, #8b7cf6 50%, #a29bfe 100%);
color: #fff;
border-radius: 28px;
padding: 28px 20px 22px;
box-shadow: 0 16px 36px var(--glow);
position: relative;
overflow: hidden;
}
.profileHero::before {
content: ""; position: absolute; top: -60px; right: -60px; width: 180px; height: 180px;
background: rgba(255, 255, 255, 0.12); border-radius: 50%; pointer-events: none;
}
.profileHero::after {
content: ""; position: absolute; bottom: -40px; left: -40px; width: 140px; height: 140px;
background: rgba(255, 255, 255, 0.08); border-radius: 50%; pointer-events: none;
}
.bigAvatarWrapper { position: relative; width: 84px; height: 84px; margin: 0 auto 12px; }
.bigAvatar {
width: 100%; height: 100%; border-radius: 26px;
background: rgba(255, 255, 255, 0.25);
display: grid; place-items: center; font-size: 38px; font-weight: 1000;
border: 2px solid rgba(255, 255, 255, 0.45);
backdrop-filter: blur(10px);
box-shadow: 0 12px 25px rgba(0,0,0,0.2);
}
.profileBadgeTag {
position: absolute; bottom: -6px; left: 50%; transform: translateX(-50%);
background: #f5b72f; color: #171827; font-size: 10px; font-weight: 900;
padding: 2px 10px; border-radius: 99px; box-shadow: 0 4px 10px rgba(0,0,0,0.15);
white-space: nowrap; border: 1.5px solid #fff;
}
.profileHero h2 { margin: 8px 0 2px; font-size: 20px; font-weight: 1000; text-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.profileHero small { opacity: .9; font-size: 12px; font-weight: 600; display: block; margin-bottom: 16px; }
.profileQuickStats {
display: flex; justify-content: center; gap: 10px;
background: rgba(255, 255, 255, 0.15); backdrop-filter: blur(8px);
padding: 10px; border-radius: 18px; border: 1px solid rgba(255, 255, 255, 0.2);
}
.pStatItem { flex: 1; text-align: center; }
.pStatItem span { display: block; font-size: 10px; opacity: 0.85; font-weight: 700; }
.pStatItem b { font-size: 14px; font-weight: 900; margin-top: 2px; display: block; }
/* التطوير الفاخر لبطاقة صفحة الإعدادات */
.settingsHeroCard {
background: linear-gradient(135deg, #171827 0%, #292a3e 100%);
color: #ffffff; border-radius: 26px; padding: 20px;
box-shadow: 0 14px 32px rgba(23, 24, 39, 0.18); margin-bottom: 18px;
display: flex; align-items: center; justify-content: space-between;
position: relative; overflow: hidden; border: 1px solid rgba(255, 255, 255, 0.08);
}
.settingsHeroCard::before {
content: ""; position: absolute; right: -30px; bottom: -30px; width: 120px; height: 120px;
background: rgba(108, 92, 231, 0.25); border-radius: 50%; blur: 20px; pointer-events: none;
}
.settingsHeroInfo { display: flex; align-items: center; gap: 14px; position: relative; z-index: 2; }
.settingsAvatar {
width: 64px; height: 64px; border-radius: 22px;
background: linear-gradient(135deg, #6c5ce7, #8b7cf6); border: 2px solid rgba(255, 255, 255, 0.2);
display: grid; place-items: center; font-size: 30px; font-weight: 1000;
box-shadow: 0 8px 20px rgba(108, 92, 231, 0.35);
}
.settingsUserDetails h3 { margin: 0; font-size: 18px; font-weight: 900; display: flex; align-items: center; gap: 6px; }
.settingsUserDetails p { margin: 4px 0 0; font-size: 11px; color: #a6a8b8; font-weight: 700; display: flex; align-items: center; gap: 5px; }
.settingsHeroBadge {
background: rgba(255, 255, 255, 0.08); backdrop-filter: blur(10px);
padding: 8px 14px; border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.12);
font-size: 13px; font-weight: 900; display: flex; align-items: center; gap: 6px; color: #f5b72f;
box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.1);
}
.setting{display:flex;align-items:center;justify-content:space-between;padding:14px 0;border-bottom:1px solid var(--line);font-size:13px}.setting:last-child{border-bottom:0}
.switch{width:48px;height:28px;background:#cdd0d9;border-radius:99px;padding:3px;position:relative;}
.switch i{display:block;width:22px;height:22px;border-radius:50%;background:#fff;transition:.2s;}
.switch.on{background:var(--primary)}.switch.on i{transform:translateX(-20px)}
input,select{width:100%;background:var(--soft);border:1px solid var(--line);color:var(--text);padding:12px;border-radius:13px;outline:0}
.modal{position:fixed;inset:0;background:rgba(16,16,26,0.75);backdrop-filter:blur(6px);z-index:60;display:none;align-items:flex-end;animation:fadeIn 0.2s ease;}
.modal.show{display:flex}.sheet{width:min(560px,100%);max-height:92vh;overflow:auto;background:var(--card);border-radius:28px 28px 0 0;padding:20px;box-shadow:0 -15px 40px rgba(0,0,0,0.2);animation:slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);}
@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
.quizTop{display:flex;justify-content:space-between;align-items:center}
.timer{background:#fff0f0;color:var(--danger);padding:8px 14px;border-radius:12px;font-weight:1000;display:flex;align-items:center;gap:6px;}
.qNumber{font-size:12px}.question{font-size:19px;line-height:1.65;font-weight:1000;margin:18px 0}
.answers{display:grid;gap:10px}
.answer{width:100%;text-align:right;padding:14px 16px;border:2px solid transparent;border-radius:16px;background:var(--soft);font-size:13.5px;display:flex;align-items:center;justify-content:space-between;transition:0.2s;}
.answer.correct{background:#e8faf2;border-color:var(--success);color:#0c8758;}
.answer.wrong{background:#fff0f0;border-color:var(--danger);color:#e5484d;}
.answer.disabled{pointer-events:none}.answer .letter{display:inline-grid;place-items:center;width:28px;height:28px;border-radius:99px;background:var(--card);margin-left:8px;font-weight:900;color:var(--text);}
/* التطوير الجديد والمميز لأزرار المساعدة والخروج أثناء الاختبار */
.quizActionsGrid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 22px;
}
.actionBtn {
background: var(--card); border: 2px solid var(--line); border-radius: 20px;
padding: 12px 8px; display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 6px; text-align: center; transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
box-shadow: 0 4px 14px rgba(0,0,0,0.03); position: relative; overflow: hidden;
}
.actionBtn:hover { transform: translateY(-4px); border-color: var(--primary2); box-shadow: 0 8px 20px var(--glow); }
.actionBtnIcon {
width: 44px; height: 44px; border-radius: 14px; display: grid; place-items: center;
font-size: 18px; transition: transform 0.2s ease;
}
.actionBtn:hover .actionBtnIcon { transform: scale(1.1); }
.actionBtnTitle { font-size: 12px; font-weight: 900; color: var(--text); line-height: 1.2; }
.actionBtnCost {
font-size: 10px; font-weight: 800; padding: 2px 8px; border-radius: 99px;
background: var(--soft); color: var(--muted); margin-top: 2px; display: inline-flex; align-items: center; gap: 3px;
}
.actionBtnExit {
grid-column: span 3; flex-direction: row; justify-content: center; gap: 10px; padding: 12px;
background: rgba(229, 72, 77, 0.05); border-color: rgba(229, 72, 77, 0.2); border-radius: 16px;
}
.actionBtnExit .actionBtnIcon { width: 32px; height: 32px; font-size: 15px; background: rgba(229, 72, 77, 0.12); color: var(--danger); }
.actionBtnExit .actionBtnTitle { color: var(--danger); font-size: 13px; }
.actionBtnExit:hover { border-color: var(--danger); box-shadow: 0 8px 20px rgba(229, 72, 77, 0.2); }
.result{text-align:center;padding:12px 5px 22px}.resultIcon{font-size:60px;margin-bottom:10px;}.score{font-size:53px;font-weight:1000;color:var(--primary);line-height:1}
.resultStats{display:grid;grid-template-columns:repeat(2,1fr);gap:9px;margin:18px 0}.rs{background:var(--soft);padding:13px;border-radius:16px}.rs b{display:block;font-size:20px}.rs small{font-size:10px;color:var(--muted)}.modalTitle{font-size:18px;font-weight:1000;margin:3px 0 15px}
.toast{position:fixed;bottom:92px;left:50%;transform:translate(-50%,15px);background:#191a27;color:#fff;padding:12px 18px;border-radius:14px;opacity:0;pointer-events:none;transition:.25s;z-index:100;font-size:12px;white-space:nowrap;box-shadow:0 10px 25px rgba(0,0,0,0.3);display:flex;align-items:center;gap:8px;}
.toast.show{opacity:1;transform:translate(-50%,0)}
/* نمط عجلة الحظ */
.wheelContainer { position: relative; width: 220px; height: 220px; margin: 0 auto; }
.wheelCanvas { width: 100%; height: 100%; border-radius: 50%; box-shadow: 0 0 20px var(--glow); transition: transform 4s cubic-bezier(0.15, 0.9, 0.2, 1); }
.wheelPointer { position: absolute; top: -10px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 12px solid transparent; border-right: 12px solid transparent; border-top: 20px solid var(--danger); z-index: 10; }
/* المتجر */
.shopGrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 10px; }
.shopCard { background: var(--card); border-radius: 18px; padding: 14px; text-align: center; box-shadow: var(--shadow); border: 1px solid var(--line); }
.shopCard .icon { font-size: 32px; margin-bottom: 8px; }
.shopCard b { display: block; font-size: 13px; }
.shopCard small { color: var(--muted); font-size: 10px; display: block; margin-bottom: 10px; }
.dark{--bg:#11121b;--card:#1b1c28;--text:#f5f5fa;--muted:#a6a8b8;--soft:#242532;--line:#2b2c38;--glow:rgba(108, 92, 231, 0.2);}
.dark .tile .ico{background:#28293d;color:#8b7cf6;}
.dark .challengeIcon{background:#28293d;color:#8b7cf6;}
.dark .badge{background:#28293d;color:#8b7cf6;}
.dark .timer{background:#32202b;}
.dark .actionBtn{background:var(--card);border-color:var(--line);}
.dark .settingsHeroCard{background:linear-gradient(135deg, #181926 0%, #222436 100%);}
</style>
</head>
<body>
<div class="app">
<header>
<div class="brand"><i class="fa-solid fa-bolt"></i> تحدّاك</div>
<div class="headBtns">
<button class="ib" onclick="openWheelModal()" title="عجلة الحظ"><i class="fa-solid fa-dharmachakra"></i></button>
<button class="ib" onclick="openDaily()" title="تحدي اليوم"><i class="fa-solid fa-calendar-check"></i></button>
<button class="ib" onclick="showPage('settings')" title="الإعدادات"><i class="fa-solid fa-gear"></i></button>
</div></header>
<main>
<!-- الصفحة الرئيسية -->
<section id="home" class="page active">
<div class="hero">
<div class="heroTop">
<div><h1>أهلاً، <span id="nameHome">بدر</span> 👋</h1><p>جاهز تثبت إنك تعرف أكثر؟</p></div>
<div class="avatar" id="avatarHome">ب</div>
</div>
<div class="heroStats">
<div class="hs"><b id="coinsHome"><i class="fa-solid fa-coins" style="color:#f5b72f"></i> 1,250</b><small>عملة</small></div>
<div class="hs"><b id="levelHome"><i class="fa-solid fa-star" style="color:#f5b72f"></i> 1</b><small>المستوى</small></div>
<div class="hs"><b id="streakHome"><i class="fa-solid fa-fire" style="color:#ff6b6b"></i> 0</b><small>متتالي</small></div>
</div>
</div>
<div class="card">
<div class="row between">
<div><b>المستوى <span id="lvlText">1</span></b><div class="muted small" id="xpHint">1000 XP للمستوى التالي</div></div>
<span class="badge" id="xpText"><i class="fa-solid fa-trophy"></i> 0 / 1000</span>
</div>
<div class="progress" style="margin-top:12px"><i id="xpBar" style="width:0%"></i></div>
</div>
<div class="section"><h2><i class="fa-solid fa-gamepad" style="color:var(--primary)"></i> العب الآن</h2><button class="link" onclick="showPage('quizzes')">كل الاختبارات <i class="fa-solid fa-chevron-left"></i></button></div>
<div class="grid">
<button class="tile" onclick="startQuiz('general')"><span class="ico"><i class="fa-solid fa-globe"></i></span><b>معلومات عامة</b><small>أسئلة متنوعة</small></button>
<button class="tile" onclick="startQuiz('islamic')"><span class="ico"><i class="fa-solid fa-kaaba"></i></span><b>إسلاميات</b><small>ثقافة ومعرفة</small></button>
<button class="tile" onclick="startQuiz('sport')"><span class="ico"><i class="fa-solid fa-football"></i></span><b>رياضة</b><small>تحدي عشاق الرياضة</small></button>
<button class="tile" onclick="startQuiz('science')"><span class="ico"><i class="fa-solid fa-flask"></i></span><b>علوم</b><small>اختبر ذكاءك</small></button>
</div>
<div class="section"><h2><i class="fa-solid fa-fire" style="color:#ff6b6b"></i> تحديات اليوم والأدوات</h2></div>
<div class="challenge">
<div class="challengeIcon" style="background:#ffe8e8;color:#e5484d"><i class="fa-solid fa-skull"></i></div>
<div class="challengeText"><b>وضع البقاء (Survival)</b><small>أسئلة بلا حد • الخطأ ينهي اللعبة!</small></div>
<button class="go" style="background:linear-gradient(135deg,#e5484d,#ff6b6b)" onclick="startQuiz('survival')"><i class="fa-solid fa-play"></i> ابدأ</button>
</div>
<div class="challenge">
<div class="challengeIcon"><i class="fa-solid fa-bolt-lightning"></i></div>
<div class="challengeText"><b>تحدي السرعة</b><small>10 أسئلة • 6 ثوانٍ لكل سؤال</small></div>
<button class="go" onclick="startQuiz('speed')"><i class="fa-solid fa-play"></i> ابدأ</button>
</div>
<div class="challenge">
<div class="challengeIcon"><i class="fa-solid fa-calendar-day"></i></div>
<div class="challengeText"><b>تحدي اليوم</b><small id="dailyStatus">10 أسئلة • مكافأة 100 عملة</small></div>
<button class="go" onclick="openDaily()"><i class="fa-solid fa-play"></i> ابدأ</button>
</div>
<div class="challenge">
<div class="challengeIcon" style="background:#fff4d5;color:#c58b00"><i class="fa-solid fa-dharmachakra"></i></div>
<div class="challengeText"><b>عجلة الحظ اليومية</b><small id="wheelStatus">اكسب عملات إضافية مجاناً!</small></div>
<button class="go" style="background:linear-gradient(135deg,#f5b72f,#e09b12)" onclick="openWheelModal()"><i class="fa-solid fa-spinner"></i> أدر الآن</button>
</div>
<div class="challenge">
<div class="challengeIcon" style="background:#e8faf2;color:#16a56b"><i class="fa-solid fa-square-plus"></i></div>
<div class="challengeText"><b>صانع الأسئلة</b><small>أضف أسئلتك الخاصة بالتطبيق</small></div>
<button class="go" style="background:linear-gradient(135deg,#16a56b,#107c50)" onclick="openCreateQuestionModal()"><i class="fa-solid fa-plus"></i> إضافة</button>
</div>
</section>
<!-- صفحة الاختبارات -->
<section id="quizzes" class="page">
<div class="section"><h2><i class="fa-solid fa-brain" style="color:var(--primary)"></i> جميع الاختبارات والمجالات</h2></div>
<div class="grid" id="quizGrid"></div>
</section>
<!-- صفحة المتجر -->
<section id="shop" class="page">
<div class="section"><h2><i class="fa-solid fa-store" style="color:var(--primary)"></i> متجر الأفاتار والمظاهر</h2></div>
<div class="shopGrid" id="shopGrid"></div>
</section>
<!-- صفحة المتصدرين -->
<section id="leaderboard" class="page">
<div class="section"><h2><i class="fa-solid fa-trophy" style="color:var(--gold)"></i> المتصدرون</h2><span class="badge gold"><i class="fa-solid fa-crown"></i> هذا الشهر</span></div>
<div class="list" id="leaderList"></div>
<div class="card" style="margin-top:12px">
<div class="row between"><b>مركزك الحالي</b><b id="myRank">#1</b></div>
<div class="progress" style="margin-top:10px"><i id="rankBar" style="width:35%"></i></div>
<div class="muted small" style="margin-top:7px">استمر في اللعب وزيادة نقاطك لتحسين ترتيبك.</div>
</div>
</section>
<!-- صفحة الإنجازات -->
<section id="achievements" class="page">
<div class="section"><h2><i class="fa-solid fa-medal" style="color:var(--gold)"></i> قائمة الإنجازات</h2><span class="badge" id="achCount">0 / 12</span></div>
<div class="ach" id="achGrid"></div>
</section>
<!-- صفحة الملف الشخصي المطورة -->
<section id="profile" class="page">
<div class="profileHero">
<div class="bigAvatarWrapper">
<div class="bigAvatar" id="avatarBig">ب</div>
<span class="profileBadgeTag"><i class="fa-solid fa-crown"></i> محترف</span>
</div>
<h2 id="profileName">بدر</h2>
<small id="profileTitle">لاعب تحدّاك المحترف 🌟</small>
<div class="profileQuickStats">
<div class="pStatItem"><span>المستوى</span><b id="profileLvl">1</b></div>
<div class="pStatItem" style="border-right: 1px solid rgba(255,255,255,0.2); border-left: 1px solid rgba(255,255,255,0.2);"><span>الرصيد</span><b id="profileCoins">0 🪙</b></div>
<div class="pStatItem"><span>المتتالي</span><b id="profileStreak">0 🔥</b></div>
</div>
</div>
<div class="section"><h2><i class="fa-solid fa-chart-pie" style="color:var(--primary)"></i> أرقامي وإحصائياتي</h2></div>
<div class="statGrid">
<div class="statCard"><i class="fa-solid fa-circle-question"></i><b id="stTotal">0</b><small>سؤال تم حله</small></div>
<div class="statCard"><i class="fa-solid fa-bullseye"></i><b id="stCorrect">0%</b><small>نسبة الدقة</small></div>
<div class="statCard"><i class="fa-solid fa-gem"></i><b id="stPoints">0</b><small>إجمالي النقاط</small></div>
<div class="statCard"><i class="fa-solid fa-fire-flame-curved"></i><b id="stBest">0</b><small>أفضل سلسلة</small></div>
</div>
<div class="card" style="margin-top:14px">
<div class="muted small" style="margin-bottom:8px;font-weight:700">مستوى التقدم والأداء</div>
<canvas id="statsChart" style="width:100%;max-height:180px;"></canvas>
</div>
</section>
<!-- صفحة الإعدادات المطورة -->
<section id="settings" class="page">
<div class="section"><h2><i class="fa-solid fa-sliders" style="color:var(--primary)"></i> الإعدادات المتقدمة</h2></div>
<!-- بطاقة الملف الشخصي المطورة داخل الإعدادات -->
<div class="settingsHeroCard">
<div class="settingsHeroInfo">
<div class="settingsAvatar" id="settingsAvatar">ب</div>
<div class="settingsUserDetails">
<h3 id="settingsName">بدر <i class="fa-solid fa-circle-check" style="color:#00f2fe;font-size:14px"></i></h3>
<p><i class="fa-solid fa-shield-halved" style="color:var(--primary2)"></i> مستوى <span id="settingsLvlVal">1</span> • حساب موثق</p>
</div>
</div>
<div class="settingsHeroBadge">
<i class="fa-solid fa-coins"></i>
<span id="settingsCoins">0</span>
</div>
</div>
<div class="card">
<div class="setting"><span><i class="fa-solid fa-bell"></i> الإشعارات والتحذيرات</span><button class="switch on" onclick="toggleSwitch(this)"><i></i></button></div>
<div class="setting"><span><i class="fa-solid fa-moon"></i> الوضع الداكن (Dark Mode)</span><button id="darkSwitch" class="switch" onclick="toggleDark()"><i></i></button></div>
<div class="setting"><span><i class="fa-solid fa-volume-high"></i> التأثيرات الصوتية (SFX)</span><button id="soundSwitch" class="switch on" onclick="toggleSound(this)"><i></i></button></div>
<div class="setting"><span><i class="fa-solid fa-mobile-screen"></i> الاهتزاز عند الإجابة (Haptic)</span><button id="vibSwitch" class="switch on" onclick="toggleVib(this)"><i></i></button></div>
</div>
<div class="card">
<div class="setting"><span><i class="fa-solid fa-user-pen"></i> تغيير الاسم الشخصي</span><button class="link" onclick="changeName()">تعديل</button></div>
<div class="setting"><span><i class="fa-solid fa-trash-can" style="color:var(--danger)"></i> إعادة التقدم بالكامل</span><button class="link danger" onclick="resetAll()">تصفير البيانات</button></div>
</div>
</section>
</main>
<!-- شريط التنقل السفلي -->
<nav class="nav">
<button class="active" data-page="home" onclick="showPage('home')"><i class="fa-solid fa-house"></i>الرئيسية</button>
<button data-page="quizzes" onclick="showPage('quizzes')"><i class="fa-solid fa-layer-group"></i>اختبارات</button>
<button data-page="shop" onclick="showPage('shop')"><i class="fa-solid fa-store"></i>المتجر</button>
<button data-page="leaderboard" onclick="showPage('leaderboard')"><i class="fa-solid fa-ranking-star"></i>المتصدرون</button>
<button data-page="achievements" onclick="showPage('achievements')"><i class="fa-solid fa-award"></i>إنجازات</button>
<button data-page="profile" onclick="showPage('profile')"><i class="fa-solid fa-user"></i>حسابي</button>
</nav>
</div>
<!-- المودالات والنوافذ المنبثقة -->
<div class="modal" id="quizModal"><div class="sheet" id="quizSheet"></div></div>
<div class="modal" id="customModal"><div class="sheet" id="customSheet"></div></div>
<div class="toast" id="toast"><i class="fa-solid fa-circle-info"></i> <span id="toastMsg"></span></div>
<script>
// إعداد الصوتيات الحقيقية المعتمدة على Web Audio API
const audioSounds = {
correct: [523.25, 659.25, 783.99, 1046.50],
wrong: [300, 260, 220, 150],
click: [400]
};
function playSound(type) {
if (!state.settings.sound) return;
try {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const freqs = audioSounds[type] || [400];
freqs.forEach((freq, idx) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.type = type === 'wrong' ? 'sawtooth' : 'sine';
osc.frequency.value = freq;
const startTime = ctx.currentTime + (idx * 0.08);
gain.gain.setValueAtTime(0.15, startTime);
gain.gain.exponentialRampToValueAtTime(0.001, startTime + 0.12);
osc.start(startTime);
osc.stop(startTime + 0.12);
});
} catch(e) {}
}
const icons = {
general:'<i class="fa-solid fa-globe"></i>',
islamic:'<i class="fa-solid fa-kaaba"></i>',
sport:'<i class="fa-solid fa-football"></i>',
science:'<i class="fa-solid fa-flask"></i>',
history:'<i class="fa-solid fa-monument"></i>',
tech:'<i class="fa-solid fa-laptop-code"></i>',
egypt:'<i class="fa-solid fa-pyramid"></i>',
riddles:'<i class="fa-solid fa-puzzle-piece"></i>',
geography:'<i class="fa-solid fa-map-location-dot"></i>',
literature:'<i class="fa-solid fa-book-open"></i>',
custom:'<i class="fa-solid fa-pen-to-square"></i>'
};
const questions = {
general:[
["ما هي أكبر قارة من حيث المساحة؟",["آسيا","أفريقيا","أوروبا","أمريكا الجنوبية"],0],
["ما الكوكب المعروف بالكوكب الأحمر؟",["الزهرة","المريخ","المشتري","عطارد"],1],
["كم عدد ألوان قوس قزح التقليدية؟",["5","6","7","8"],2],
["ما عاصمة اليابان؟",["بكين","سيول","طوكيو","بانكوك"],2],
["ما أكبر محيط في العالم؟",["الأطلسي","الهندي","الهادئ","المتجمد"],2],
["ما الحيوان المعروف بسفينة الصحراء؟",["الحصان","الجمل","الفيل","الغزال"],1],
["كم يومًا في السنة العادية؟",["360","364","365","366"],2],
["ما أصغر دولة في العالم مساحة؟",["موناكو","الفاتيكان","مالطا","سنغافورة"],1],
["ما العنصر الذي رمزه O؟",["ذهب","أكسجين","حديد","هيدروجين"],1],
["ما اللغة الأكثر انتشارًا كلغة أم؟",["الإنجليزية","الصينية المندرينية","الإسبانية","العربية"],1]],
islamic:[
["كم عدد الصلوات المفروضة؟",["3","4","5","6"],2],
["ما أول شهر هجري؟",["رمضان","محرم","رجب","شوال"],1],
["ما اسم ليلة نزول القرآن؟",["ليلة القدر","ليلة النصف","ليلة الإسراء","ليلة العيد"],0],
["كم عدد أركان الإسلام؟",["3","4","5","6"],2],
["إلى أين يتجه المسلمون في الصلاة؟",["المسجد النبوي","الكعبة","المسجد الأقصى","عرفات"],1],
["ما السورة التي تبدأ بالحمد لله رب العالمين؟",["البقرة","الفاتحة","الإخلاص","الناس"],1],
["كم عدد أشهر السنة الهجرية؟",["10","11","12","13"],2],
["في أي شهر يصوم المسلمون؟",["شعبان","رمضان","ذو الحجة","محرم"],1],
["ما القبلة الأولى للمسلمين؟",["الكعبة","المسجد الأقصى","المسجد النبوي","قباء"],1],
["ما اسم الزكاة المفروضة على المال؟",["صدقة التطوع","زكاة المال","الفدية","الكفارة"],1]],
sport:[
["كم لاعبًا يبدأ بهم فريق كرة القدم؟",["9","10","11","12"],2],
["كم دقيقة مدة مباراة كرة القدم الأصلية؟",["60","80","90","120"],2],
["ما الرياضة التي تستخدم كرة صفراء ومضربًا؟",["التنس","السباحة","الجولف","الملاكمة"],0],
["كم حلقة في الشعار الأولمبي؟",["4","5","6","7"],1],
["أين أقيمت أول كأس عالم لكرة القدم؟",["البرازيل","إيطاليا","أوروغواي","فرنسا"],2],
["في أي رياضة تستخدم الضربة القاضية؟",["التنس","الملاكمة","السباحة","الرماية"],1],
["كم شوطًا في مباراة NBA؟",["2","3","4","5"],2],
["ما لون بطاقة الطرد؟",["الصفراء","الحمراء","الزرقاء","الخضراء"],1],
["بطولة ويمبلدون تخص أي رياضة؟",["التنس","الرجبي","الكريكيت","الهوكي"],0],
["ما الأداة الأساسية في البيسبول لضرب الكرة؟",["مضرب","حبل","قرص","شبكة"],0]],
science:[
["ما الغاز الذي تحتاجه معظم الكائنات للتنفس؟",["النيتروجين","الأكسجين","الهيدروجين","الهيليوم"],1],
["ما أقرب كوكب إلى الشمس؟",["الزهرة","الأرض","عطارد","المريخ"],2],
["ما وحدة قياس شدة التيار؟",["الفولت","الأمبير","الواط","الأوم"],1],
["ما القوة التي تجذب الأجسام نحو الأرض؟",["الاحتكاك","الجاذبية","الضغط","الطفو"],1],
["كم عظمة في جسم الإنسان البالغ تقريبًا؟",["106","206","306","406"],1],
["ما العضو الذي يضخ الدم؟",["الرئة","الكبد","القلب","المعدة"],2],
["ما حالة الماء عند 0 مئوية عادة؟",["غاز","سائل","صلب","بلازما"],2],
["ما مركز الذرة؟",["الإلكترون","النواة","الفوتون","الغلاف"],1],
["ما أكبر كوكب؟",["الأرض","زحل","المشتري","نبتون"],2],
["كيف تصنع النباتات غذاءها؟",["التنفس","البناء الضوئي","الهضم","التبخر"],1]],
history:[
["من بنى الأهرامات الشهيرة في الجيزة؟",["الرومان","المصريون القدماء","الإغريق","الفرس"],1],
["أين تقع البتراء؟",["مصر","الأردن","العراق","تونس"],1],
["من أول الخلفاء الراشدين؟",["عمر","أبو بكر","عثمان","علي"],1],
["من أشهر الحضارات بالكتابة المسمارية؟",["السومرية","الرومانية","الفرنسية","الإنكا"],0],
["أين يقع سور الصين العظيم؟",["اليابان","الصين","الهند","كوريا"],1],
["ما عاصمة الدولة العباسية الشهيرة؟",["دمشق","بغداد","القاهرة","قرطبة"],1],
["من قاد الحملة الفرنسية على مصر؟",["نابليون","قيصر","هتلر","الإسكندر"],0],
["في أي قارة نشأت الحضارة المصرية القديمة؟",["آسيا","أوروبا","أفريقيا","أمريكا"],2],
["ما اسم الكتابة المصرية القديمة المصورة؟",["الهيروغليفية","اللاتينية","المسمارية","السنسكريتية"],0],
["ما المدينة الشهيرة في الأندلس؟",["قرطبة","روما","باريس","لندن"],0]],
tech:[
["ما الشركة المطورة لنظام Android؟",["Google","Microsoft","IBM","Nokia"],0],
["ما الامتداد الشائع لصفحات الويب؟",[".mp3",".html",".jpg",".zip"],1],
["ماذا تعني CPU؟",["وحدة المعالجة المركزية","ذاكرة مؤقتة","نظام تشغيل","بطاقة رسومات"],0],
["ما اللغة المستخدمة لتنسيق صفحات الويب؟",["HTML","CSS","SQL","Python"],1],
["ما الجهاز الذي يربط الأجهزة لاسلكيًا غالبًا؟",["الراوتر","الطابعة","الشاشة","الماسح"],0],
["من تملك Windows؟",["Apple","Google","Microsoft","Amazon"],2],["ما Wi-Fi؟",["اتصال لاسلكي","نوع بطارية","لغة برمجة","نظام ملفات"],0],
["أي من هذه لغة برمجة؟",["HTML","Python","JPEG","HTTP"],1],
["ما الذاكرة التي تفقد محتواها بانقطاع الطاقة؟",["RAM","SSD","ROM","DVD"],0],
["ما متجر التطبيقات الرسمي لـAndroid؟",["App Store","Google Play","Windows Store","Steam"],1]],
egypt:[
["ما عاصمة مصر؟",["الإسكندرية","القاهرة","الأقصر","أسوان"],1],
["ما أطول نهر يمر بمصر؟",["النيل","الفرات","دجلة","الكونغو"],0],
["في أي قارة تقع مصر؟",["أوروبا","آسيا","أفريقيا","أمريكا"],2],
["على أي بحر تقع الإسكندرية؟",["الأحمر","المتوسط","العربي","الأسود"],1],
["ما المدينة المشهورة بمعابد الكرنك؟",["الأقصر","بورسعيد","السويس","دمياط"],0],
["ما العملة الرسمية في مصر؟",["الدينار","الريال","الجنيه المصري","الدرهم"],2],
["ما القناة التي تربط المتوسط بالأحمر؟",["السويس","بنما","كيل","كورنث"],0],
["ما المدينة المعروفة بالسد العالي؟",["أسوان","المنصورة","طنطا","الفيوم"],0],["ما الصحراء التي تقع غرب وادي النيل؟",["الغربية","سيناء","النقب","الشمالية"],0],
["على أي هضبة تقع أهرامات الجيزة؟",["الجيزة","سيناء","حلايب","الفيوم"],0]],
riddles:[
["شيء كلما أخذت منه كبر، ما هو؟",["البئر","العمر","الكتاب","الطريق"],0],
["له أسنان ولا يعض؟",["المشط","الكلب","المفتاح","المنشار"],0],
["ما الشيء الذي يمشي بلا أرجل؟",["الساعة","الماء","السيارة","الكتاب"],1],
["له عين ولا يرى؟",["الإبرة","القمر","الإنسان","الشجرة"],0],
["شيء في الثلاجة لا يبرد؟",["الفلفل","الفلفل الحار","الثلج","الهواء"],1],
["بيت بلا أبواب ولا نوافذ؟",["البيضة","البيت","السيارة","الكتاب"],0],
["ما الذي يكتب ولا يقرأ؟",["القلم","الكتاب","الطالب","المعلم"],0],
["له رقبة بلا رأس؟",["الزجاجة","الإنسان","القلم","الكرسي"],0],
["يسمع بلا أذن ويتكلم بلا لسان؟",["الصدى","الكتاب","الهاتف","القلم"],0],
["شيء إذا زاد نقص؟",["العمر","المال","الطول","الوزن"],0]],
geography:[
["ما هي عاصمة أستراليا؟",["سيدني","كانبرا","ملبورن","بريزبان"],1],
["ما أطول نهر في العالم؟",["النيل","الأمازون","المسيسيبي","الينسي"],0],
["في أي دولة تقع جبال الألب الرئيسية؟",["سويسرا","إسبانيا","اليونان","البرتغال"],0],
["ما هي الدولة الأكثر إكتظاظاً بالسكان في العالم؟",["الهند","الصين","أمريكا","إندونيسيا"],0],
["ما اسم أعلى قمة جبلية في العالم؟",["إفرست","كيليمنجارو","K2","مون بلان"],0]
],
literature:[
["من هو أمير الشعراء؟",["أحمد شوقي","حافظ إبراهيم","المتنبي","نزار قباني"],0],
["من مؤلف رواية الثلاثية الشهيرة؟",["نجيب محفوظ","طه حسين","عباس العقاد","توفيق الحكيم"],0],
["ما هو أصل كتاب ألف ليلة وليلة؟",["هندي وفارسي","عربي خالص","إغريقي","روماني"],0],
["من الشاعر الملقب بـ شاعر النيل؟",["حافظ إبراهيم","أحمد شوقي","عنترة بن شداد","خليل مطران"],0]
]
};
const names = {
general:"معلومات عامة", islamic:"إسلاميات", sport:"رياضة", science:"علوم",
history:"تاريخ", tech:"تقنية", egypt:"مصر", riddles:"ألغاز",
geography:"جغرافيا العالم", literature:"أدب وشعر", custom:"أسئلتي المخصصة", survival:"وضع البقاء"
};
const categories = [
["general","معلومات عامة","أسئلة متنوعة",icons.general],
["islamic","إسلاميات","ثقافة ومعرفة",icons.islamic],
["sport","رياضة","كرة وألعاب",icons.sport],
["science","علوم","علوم وطبيعة",icons.science],
["history","تاريخ","أحداث وشخصيات",icons.history],
["tech","تقنية","تكنولوجيا",icons.tech],
["egypt","مصر","تاريخ وثقافة",icons.egypt],
["riddles","ألغاز","فكر بسرعة",icons.riddles],
["geography","جغرافيا العالم","بلدان وعواصم",icons.geography],
["literature","أدب وشعر","شعراء وروايات",icons.literature],
["custom","أسئلتي المخصصة","صناعة شرفية",icons.custom]
];
const shopItems = [
{ id: 'av_lion', name: 'أفاتار الأسد', icon: '🦁', cost: 300, type: 'avatar' },
{ id: 'av_crown', name: 'أفاتار التاج', icon: '👑', cost: 500, type: 'avatar' },
{ id: 'av_ninja', name: 'أفاتار النينجا', icon: '🥷', cost: 800, type: 'avatar' },
{ id: 'av_robot', name: 'أفاتار الروبوت', icon: '🤖', cost: 1000, type: 'avatar' }
];
const defaultState = {
name: "بدر", coins: 1250, points: 0, xp: 0, level: 1, total: 0, correct: 0,
bestStreak: 0, currentStreak: 0, days: 0, lastDay: "", dailyPlayed: "",
lastWheel: "", dailyScore: 0, avatar: "ب", inventory: [], ach: {}, history: [], customQs: [],
settings: { dark: false, sound: true, vib: true }
};
let state = JSON.parse(localStorage.getItem("tahdakFinal") || "null") || structuredClone(defaultState);
if(!state.customQs) state.customQs = [];
if(!state.inventory) state.inventory = [];
if(!state.settings) state.settings = { dark: false, sound: true, vib: true };
let quiz = { qs: [], i: 0, score: 0, correct: 0, streak: 0, time: 15, category: "", timer: null, answered: false, mode: "normal" };
let chartInstance = null;
function save() {
localStorage.setItem("tahdakFinal", JSON.stringify(state));
renderAll();
}
function renderAll() {
const s = state;
["coinsHome","settingsCoins"].forEach(id => {
const el = document.getElementById(id);
if(el) el.innerHTML = `${s.coins.toLocaleString()}`;
});
document.getElementById("levelHome").textContent = s.level;
document.getElementById("lvlText").textContent = s.level;
document.getElementById("settingsLvlVal").textContent = s.level;
document.getElementById("profileLvl").textContent = s.level;
document.getElementById("profileCoins").textContent = s.coins.toLocaleString() + " 🪙";
document.getElementById("profileStreak").textContent = s.currentStreak + " 🔥";
document.getElementById("streakHome").textContent = s.currentStreak;
document.getElementById("xpText").innerHTML = `<i class="fa-solid fa-trophy"></i> ${s.xp} / 1000`;
document.getElementById("xpBar").style.width = Math.min(100, s.xp/10) + "%";
document.getElementById("nameHome").textContent = s.name;
document.getElementById("profileName").textContent = s.name;
document.getElementById("settingsName").innerHTML = `${s.name} <i class="fa-solid fa-circle-check" style="color:#00f2fe;font-size:14px"></i>`;
const av = s.avatar || (s.name || "ب").trim()[0] || "ب";
document.getElementById("avatarHome").textContent = av;
document.getElementById("avatarBig").textContent = av;
document.getElementById("settingsAvatar").textContent = av;
document.getElementById("stTotal").textContent = s.total;
document.getElementById("stCorrect").textContent = s.total ? Math.round(s.correct / s.total * 100) + "%" : "0%";
document.getElementById("stPoints").textContent = s.points.toLocaleString();
document.getElementById("stBest").textContent = s.bestStreak;
renderCategories();
renderLeaderboard();
renderAchievements();
renderShop();
renderChart();
const today = todayKey();
document.getElementById("dailyStatus").textContent = s.dailyPlayed === today ? "تم لعب تحدي اليوم • حاول تحسين نتيجتك" : "10 أسئلة • مكافأة 100 عملة";
document.getElementById("wheelStatus").textContent = s.lastWheel === today ? "تم استخدام العجلة اليوم • عد غداً" : "فرصة مجانية متاحة الآن!";
}
function renderCategories() {
document.getElementById("quizGrid").innerHTML = categories.map(c => `
<button class="tile" onclick="startQuiz('${c[0]}')">
<span class="ico">${c[3]}</span>
<b>${c[1]}</b>
<small>${c[2]}</small>
</button>
`).join("");
}
function renderShop() {
document.getElementById("shopGrid").innerHTML = shopItems.map(item => {
const bought = state.inventory.includes(item.id);
const active = state.avatar === item.icon;
return `
<div class="shopCard">
<div class="icon">${item.icon}</div>
<b>${item.name}</b>
<small>${item.cost} عملة</small>
${active ? '<button class="secondary" disabled style="width:100%">مُستخدم</button>' :
bought ? `<button class="primary" style="width:100%" onclick="useAvatar('${item.icon}')">ارتداء</button>` :
`<button class="go" style="width:100%" onclick="buyShopItem('${item.id}', ${item.cost}, '${item.icon}')">شراء</button>`
}
</div>
`;
}).join("");
}
function buyShopItem(id, cost, icon) {
if (state.coins < cost) { showToast("لا تملك عملات كافية! 🪙"); return; }
state.coins -= cost;
state.inventory.push(id);
state.avatar = icon;
save();
showToast("تم الشراء والتفعيل بنجاح! 🎉");
}
function useAvatar(icon) {
state.avatar = icon;
save();
showToast("تم تغيير الأفاتار بنجاح! 👤");
}
function renderLeaderboard() {
let players = [
["🥇","أحمد المصري",12840,24],
["🥈","محمد علي",11920,22],
["🥉","سارة",11470,21],
["4","كريم",10850,19],
["5","نور",10420,18],
["6","يوسف",9950,17]
];
let rank = players.findIndex(p => state.points > p[2]) + 1;
if(rank < 1) rank = players.length + 1;
document.getElementById("myRank").textContent = "#" + rank;
document.getElementById("rankBar").style.width = Math.max(8, Math.min(95, 100 - rank * 4)) + "%";
document.getElementById("leaderList").innerHTML = players.map(p => `
<div class="li">
<div class="rank">${p[0]}</div>
<div class="miniAv">${p[1][0]}</div>
<div class="liMain"><b>${p[1]}</b><small>المستوى ${p[3]}</small></div>
<b>${p[2].toLocaleString()}</b>
</div>
`).join("") +
(rank > 6 ? `
<div class="li" style="background:#6c5ce710;border-radius:12px;padding:10px;">
<div class="rank">${rank}</div>
<div class="miniAv">${state.avatar || (state.name||"ب")[0]}</div>
<div class="liMain"><b>${state.name} (أنت)</b><small>المستوى ${state.level}</small></div>
<b>${state.points.toLocaleString()}</b>
</div>
` : "");
}
const achDefs = [
["first","🥇","أول فوز",() => state.total >= 10],
["streak5","🔥","سلسلة 5",() => state.bestStreak >= 5],
["streak10","⚡","سلسلة 10",() => state.bestStreak >= 10],
["questions50","🧠","50 سؤال",() => state.total >= 50],
["questions100","📚","100 سؤال",() => state.total >= 100],
["correct50","🎯","50 إجابة صحيحة",() => state.correct >= 50],
["points5k","💎","5,000 نقطة",() => state.points >= 5000],
["points10k","👑","10,000 نقطة",() => state.points >= 10000],
["level5","⭐","المستوى 5",() => state.level >= 5],
["daily3","📅","3 تحديات يومية",() => state.days >= 3],
["perfect","🏆","اختبار كامل",() => state.history.some(h => h.perfect)],
["speed","🚀","ملك السرعة",() => state.history.some(h => h.mode === "speed" && h.correct >= 8)]
];
function renderAchievements() {
let unlocked = achDefs.filter(a => a[3]()).length;
document.getElementById("achCount").textContent = `${unlocked} / ${achDefs.length}`;
document.getElementById("achGrid").innerHTML = achDefs.map(a => `
<div class="achievement ${a[3]() ? "" : "lock"}">
<div class="medal">${a[1]}</div>
<b>${a[2]}</b>
<small>${a[3]() ? "تم فتح الإنجاز" : "لم يُفتح بعد"}</small>
</div>
`).join("");
}
function renderChart() {
const ctx = document.getElementById('statsChart');
if (!ctx) return;
const recent = state.history.slice(0, 7).reverse();
const labels = recent.map((_, i) => `تحدي ${i+1}`);
const data = recent.map(h => h.correct);
if (chartInstance) chartInstance.destroy();
chartInstance = new Chart(ctx, {
type: 'line',
data: {
labels: labels.length ? labels : ['لا يوجد بيانات'],
datasets: [{
label: 'الإجابات الصحيحة',
data: data.length ? data : [0],
borderColor: '#6c5ce7',
backgroundColor: 'rgba(108, 92, 231, 0.15)',
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
plugins: { legend: { display: false } },
scales: { y: { beginAtZero: true } }
}
});
}
function showPage(id) {
playSound('click');
document.querySelectorAll(".page").forEach(p => p.classList.remove("active"));
document.getElementById(id).classList.add("active");
document.querySelectorAll(".nav button").forEach(b => b.classList.toggle("active", b.dataset.page === id));
window.scrollTo(0, 0);
renderAll();
}
function todayKey() {
let d = new Date();
return `${d.getFullYear()}-${d.getMonth()+1}-${d.getDate()}`;
}
function dailySeed() {
let x = 0;
for (const c of todayKey()) x = (x * 31 + c.charCodeAt(0)) % 99991;
return x;
}
function seeded(arr, seed) {
let a = [...arr];
for (let i = a.length - 1; i > 0; i--) {
seed = (seed * 9301 + 49297) % 233280;
let j = Math.floor(seed / 233280 * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function openDaily() { startQuiz("daily"); }
function startQuiz(cat) {
playSound('click');
let pool = [];
if(cat === "custom") {
if(state.customQs.length === 0) {
showToast("لم تقم بإضافة أسئلة مخصصة بعد!");
openCreateQuestionModal();
return;
}
pool = state.customQs;
} else if (cat === "daily" || cat === "survival") {
pool = Object.values(questions).flat();
} else {
pool = questions[cat] || questions.general;
}
let qs = cat === "daily" ? seeded(pool, dailySeed()).slice(0, 10) : cat === "survival" ? [...pool].sort(() => Math.random() - .5) : [...pool].sort(() => Math.random() - .5).slice(0, 10);
quiz = { qs, i: 0, score: 0, correct: 0, streak: 0, time: cat === "speed" ? 6 : 15, category: cat, timer: null, answered: false, mode: cat };
document.getElementById("quizModal").classList.add("show");
renderQuestion();
}
function renderQuestion() {
clearInterval(quiz.timer);
quiz.answered = false;
let q = quiz.qs[quiz.i];
document.getElementById("quizSheet").innerHTML = `
<div class="quizTop">
<div>
<div class="qNumber">${quiz.mode === 'survival' ? `سؤال البقاء #${quiz.i + 1}` : `السؤال ${quiz.i + 1} من ${quiz.qs.length}`}</div>
<div class="muted small">${quiz.category === "daily" ? "🏆 تحدي اليوم" : names[quiz.category] || "تحدي"}</div>
</div>
<div class="timer" id="timer"><i class="fa-solid fa-clock"></i> 00:${String(quiz.time).padStart(2, "0")}</div>
</div>
<div class="progress" style="margin-top:12px"><i style="width:${quiz.mode === 'survival' ? 100 : (quiz.i / quiz.qs.length) * 100}%"></i></div>
<div class="question">${q[0]}</div>
<div class="answers">
${q[1].map((a, i) => `
<button class="answer" onclick="answer(${i})">
<span><span class="letter">${String.fromCharCode(1571 + i)}</span> ${a}</span>
<i class="fa-regular fa-circle"></i>
</button>
`).join("")}
</div>
<!-- أزرار المساعدة والخروج الجديدة والتطويرية -->
<div class="quizActionsGrid">
<button class="actionBtn" onclick="useFiftyFifty()">
<div class="actionBtnIcon" style="background:#ffe8e8;color:#e5484d"><i class="fa-solid fa-scissors"></i></div>
<span class="actionBtnTitle">حذف إجابتين</span>
<span class="actionBtnCost"><i class="fa-solid fa-coins" style="color:#f5b72f"></i> 30</span>
</button>
<button class="actionBtn" onclick="useSkip()">
<div class="actionBtnIcon" style="background:#e8faf2;color:#16a56b"><i class="fa-solid fa-forward-step"></i></div>
<span class="actionBtnTitle">تخطي السؤال</span>
<span class="actionBtnCost"><i class="fa-solid fa-coins" style="color:#f5b72f"></i> 25</span>
</button>
<button class="actionBtn" onclick="useFreeze()">
<div class="actionBtnIcon" style="background:#e8f3ff;color:#3d8bfd"><i class="fa-solid fa-clock-rotate-left"></i></div>
<span class="actionBtnTitle">تمديد الوقت</span>
<span class="actionBtnCost"><i class="fa-solid fa-coins" style="color:#f5b72f"></i> 15</span>
</button>
<button class="actionBtn actionBtnExit" onclick="closeQuiz()">
<div class="actionBtnIcon"><i class="fa-solid fa-right-from-bracket"></i></div>
<span class="actionBtnTitle">خروج من الاختبار</span>
</button>
</div>`;
quiz.timer = setInterval(() => {
quiz.time--;
let el = document.getElementById("timer");
if (el) el.innerHTML = `<i class="fa-solid fa-clock"></i> 00:` + String(Math.max(0, quiz.time)).padStart(2, "0");
if (quiz.time <= 0) {
clearInterval(quiz.timer);
answer(-1);
}
}, 1000);
}
function answer(choice) {
if (quiz.answered) return;
quiz.answered = true;
clearInterval(quiz.timer);
let q = quiz.qs[quiz.i];
let btn = [...document.querySelectorAll(".answer")];
btn.forEach((b, i) => {
b.classList.add("disabled");
if (i === q[2]) {
b.classList.add("correct");
b.querySelector("i").className = "fa-solid fa-circle-check";
}
if (i === choice && choice !== q[2]) {
b.classList.add("wrong");
b.querySelector("i").className = "fa-solid fa-circle-xmark";
}
});
state.total++;
let gained = 0;
if (choice === q[2]) {
playSound('correct');
quiz.correct++;
quiz.streak++;
state.correct++;
state.currentStreak++;
state.bestStreak = Math.max(state.bestStreak, state.currentStreak);
gained = 10 + quiz.time * 2 + (quiz.streak >= 3 ? 10 : 0);
quiz.score += gained;
state.points += gained;
state.xp += gained;
state.coins += 5;
vibrate(20);
showToast(`إجابة صحيحة! +${gained} نقطة 🔥`);
} else {
playSound('wrong');
quiz.streak = 0;
state.currentStreak = 0;
showToast(`إجابة خاطئة! الإجابة الصحيحة: ${q[1][q[2]]}`);
vibrate(60);
if (quiz.mode === 'survival') {
setTimeout(finishQuiz, 1200);
return;
}
}
while (state.xp >= 1000) {
state.xp -= 1000;
state.level++;
state.coins += 100;
showToast(`🎉 مبروك! وصلت للمستوى ${state.level}! +100🪙`);
if(typeof confetti === 'function') confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
}
save();
setTimeout(() => {
quiz.i++;
if (quiz.i < quiz.qs.length) {
quiz.time = quiz.mode === "speed" ? 6 : 15;
renderQuestion();
} else finishQuiz();
}, 1000);
}
function finishQuiz() {
let totalQs = quiz.mode === 'survival' ? quiz.i + 1 : quiz.qs.length;
let pct = Math.round((quiz.correct / totalQs) * 100) || 0;
let perfect = pct === 100;
if (perfect && typeof confetti === 'function') {
confetti({ particleCount: 150, spread: 80, origin: { y: 0.5 } });
}
if (quiz.category === "daily") {
let t = todayKey();
if (state.dailyPlayed !== t) {
state.dailyPlayed = t;
state.days++;
state.coins += 100;
state.dailyScore = quiz.score;
showToast("🎁 مكافأة التحدي اليومي +100 عملة!");
}
}
state.history.unshift({ date: new Date().toISOString(), category: quiz.category, mode: quiz.mode, score: quiz.score, correct: quiz.correct, total: totalQs, perfect });
state.history = state.history.slice(0, 100);
save();
document.getElementById("quizSheet").innerHTML = `
<div class="result">
<div class="resultIcon">${perfect ? "🏆" : pct >= 80 ? "🎉" : pct >= 50 ? "👏" : "💀"}</div>
<div class="modalTitle">${quiz.mode === 'survival' ? 'انتهت محاولة البقاء!' : 'انتهى التحدي بنجاح!'}</div>
<div class="score">${quiz.score}</div>
<div class="muted small">نقطة مجتمعة</div>
<div class="resultStats">
<div class="rs"><b>${quiz.correct}/${totalQs}</b><small>إجابات صحيحة</small></div>
<div class="rs"><b>${pct}%</b><small>نسبة الدقة</small></div>
<div class="rs"><b>+${quiz.correct * 5}</b><small>عملات مكتسبة</small></div>
<div class="rs"><b>${quiz.streak}</b><small>أعلى سلسلة</small></div>
</div>
<button class="primary" style="width:100%;margin-bottom:8px" onclick="shareResult()"><i class="fa-solid fa-share-nodes"></i> مشاركة النتيجة</button>
<button class="secondary" style="width:100%" onclick="startQuiz('${quiz.category}')"><i class="fa-solid fa-rotate-right"></i> إعادة التحدي</button>
<button class="secondary" style="width:100%;margin-top:8px" onclick="closeQuiz()"><i class="fa-solid fa-house"></i> العودة للرئيسية</button>
</div>`;
}
function closeQuiz() {
clearInterval(quiz.timer);
document.getElementById("quizModal").classList.remove("show");
renderAll();
}
function useFiftyFifty() {
if (quiz.answered) return;
if (state.coins < 30) { showToast("لا تملك عملات كافية 🪙"); return; }
let q = quiz.qs[quiz.i];
let buttons = [...document.querySelectorAll(".answer")];
let wrongIndices = buttons.map((_, i) => i).filter(i => i !== q[2]);
wrongIndices.sort(() => Math.random() - 0.5);
state.coins -= 30;
wrongIndices.slice(0, 2).forEach(idx => {
buttons[idx].style.opacity = ".2";
buttons[idx].classList.add("disabled");
});
save();
showToast("تم حذف إجابتين خاطئتين ✂️");
}
function useSkip() {
if (quiz.answered) return;
if (state.coins < 25) { showToast("لا تملك عملات كافية 🪙"); return; }
state.coins -= 25;
clearInterval(quiz.timer);
save();
showToast("تم تخطي السؤال بنجاح ⏭️");
quiz.i++;
if (quiz.i < quiz.qs.length) {
quiz.time = quiz.mode === "speed" ? 6 : 15;
renderQuestion();
} else finishQuiz();
}
function useFreeze() {
if (quiz.answered) return;
if (state.coins < 15) { showToast("لا تملك عملات كافية 🪙"); return; }
state.coins -= 15;
quiz.time += 10;
save();
showToast("تم إضافة +10 ثوانٍ للوقت ❄️");
}
function shareResult() {
let txt = `🎮 لعبت تحدي "${names[quiz.category] || 'تحدّاك'}" وحققت ${quiz.score} نقطة بأجوبة صحيحة ${quiz.correct}! هل تقدر تتحداني؟`;
if (navigator.share) {
navigator.share({ title: "نتيجة تحدّاك", text: txt });
} else {
navigator.clipboard?.writeText(txt);
showToast("تم نسخ النتيجة إلى الحافظة 📋");
}
}
// أداة عجلة الحظ
function openWheelModal() {
playSound('click');
const today = todayKey();
const isUsed = state.lastWheel === today;
document.getElementById("customSheet").innerHTML = `
<div class="modalTitle" style="text-align:center"><i class="fa-solid fa-dharmachakra" style="color:var(--gold)"></i> عجلة الحظ اليومية</div>
<div style="text-align:center;padding:10px 0;">
<div class="wheelContainer">
<div class="wheelPointer"></div>
<canvas id="wheelCanvas" class="wheelCanvas" width="220" height="220"></canvas>
</div>
<p id="wheelMsg" style="margin-top:15px;font-weight:700;">${isUsed ? "لقد استمتعت بفرصتك اليوم، عد غداً!" : "أدر العجلة واكسب عملات فورية!"}</p>
<button class="primary" id="spinBtn" style="width:100%;margin-top:10px" ${isUsed ? "disabled style='opacity:0.5'" : "onclick='spinWheel()'"}><i class="fa-solid fa-rotate"></i> تدوير العجلة</button>
<button class="secondary" style="width:100%;margin-top:8px" onclick="closeCustomModal()">إغلاق</button>
</div>
`;
document.getElementById("customModal").classList.add("show");
drawWheel();
}
const wheelValues = [50, 100, 150, 200, 300, 500];
const wheelColors = ['#6c5ce7', '#f5b72f', '#16a56b', '#3d8bfd', '#e5484d', '#8b7cf6'];
function drawWheel() {
const canvas = document.getElementById('wheelCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const numSegments = wheelValues.length;
const anglePerSegment = (Math.PI * 2) / numSegments;
ctx.clearRect(0, 0, 220, 220);
for (let i = 0; i < numSegments; i++) {
ctx.beginPath();
ctx.moveTo(110, 110);
ctx.arc(110, 110, 100, i * anglePerSegment, (i + 1) * anglePerSegment);
ctx.fillStyle = wheelColors[i];
ctx.fill();
ctx.save();
ctx.translate(110, 110);
ctx.rotate(i * anglePerSegment + anglePerSegment / 2);
ctx.textAlign = "right";
ctx.fillStyle = "#fff";
ctx.font = "bold 14px Cairo";
ctx.fillText(wheelValues[i], 85, 5);
ctx.restore();
}
}
function spinWheel() {
const btn = document.getElementById("spinBtn");
btn.disabled = true;
btn.style.opacity = "0.5";
const wheel = document.getElementById("wheelCanvas");
const selectedIndex = Math.floor(Math.random() * wheelValues.length);
const degrees = 1800 + (360 - (selectedIndex * (360 / wheelValues.length))) - 30;
wheel.style.transform = `rotate(${degrees}deg)`;
playSound('click');
setTimeout(() => {
const win = wheelValues[selectedIndex];
state.coins += win;
state.lastWheel = todayKey();
save();
if(typeof confetti === 'function') confetti({ particleCount: 80, spread: 60 });
document.getElementById("wheelMsg").textContent = `🎉 مبروك! ربحت ${win} عملة مجانية!`;
playSound('correct');
}, 4000);
}
// صانع الأسئلة المخصصة
function openCreateQuestionModal() {
playSound('click');
document.getElementById("customSheet").innerHTML = `
<div class="modalTitle"><i class="fa-solid fa-pen-to-square" style="color:var(--primary)"></i> إضافة سؤال مخصص</div>
<div style="display:grid;gap:10px;">
<input type="text" id="custQ" placeholder="اكتب السؤال هنا...">
<input type="text" id="custA0" placeholder="الإجابة الصحيحة (أ)">
<input type="text" id="custA1" placeholder="إجابة خاطئة (ب)">
<input type="text" id="custA2" placeholder="إجابة خاطئة (ج)">
<input type="text" id="custA3" placeholder="إجابة خاطئة (د)">
<button class="primary" onclick="saveCustomQuestion()"><i class="fa-solid fa-floppy-disk"></i> حفظ السؤال</button>
<button class="secondary" onclick="closeCustomModal()">إلغاء</button>
</div>
`;
document.getElementById("customModal").classList.add("show");
}
function saveCustomQuestion() {
const q = document.getElementById("custQ").value.trim();
const a0 = document.getElementById("custA0").value.trim();
const a1 = document.getElementById("custA1").value.trim();
const a2 = document.getElementById("custA2").value.trim();
const a3 = document.getElementById("custA3").value.trim();
if(!q || !a0 || !a1 || !a2 || !a3) {
showToast("الرجاء تعبئة كافة الحقول!");
return;
}
state.customQs.push([q, [a0, a1, a2, a3], 0]);
save();
closeCustomModal();
showToast("تم إضافة السؤال بنجاح! يمكنك لعبه من قائمة الاختبارات 📝");
}
function closeCustomModal() {
document.getElementById("customModal").classList.remove("show");
}
// الإعدادات
function toggleSwitch(el) { el.classList.toggle("on"); }
function toggleSound(el) { state.settings.sound = !state.settings.sound; el.classList.toggle("on", state.settings.sound); save(); }
function toggleVib(el) { state.settings.vib = !state.settings.vib; el.classList.toggle("on", state.settings.vib); save(); }
function toggleDark() {
state.settings.dark = !state.settings.dark;
applyTheme();
save();
}
function applyTheme() {
if (state.settings.dark) {
document.body.classList.add("dark");
} else {
document.body.classList.remove("dark");
}
const darkSw = document.getElementById("darkSwitch");
if (darkSw) darkSw.classList.toggle("on", state.settings.dark);
}
function changeName() {
let n = prompt("اكتب اسمك الجديد", state.name);
if (n && n.trim()) {
state.name = n.trim().slice(0, 18);
save();
showToast("تم تغيير الاسم بنجاح 👤");
}
}
function vibrate(ms) { if (state.settings.vib && navigator.vibrate) navigator.vibrate(ms); }
function resetAll() {
if (confirm("سيتم حذف جميع أرقامك والعملات والتقدم بالكامل. هل أنت متأكد؟")) {
let name = state.name;
state = structuredClone(defaultState);
state.name = name;
save();
applyTheme();
showToast("تم إعادة ضبط البيانات بالكامل 🧹");
}
}
function showToast(t) {
let e = document.getElementById("toast");
document.getElementById("toastMsg").textContent = t;
e.classList.add("show");
clearTimeout(window.toastT);
window.toastT = setTimeout(() => e.classList.remove("show"), 2600);
}
// التشغيل الأولي
applyTheme();
renderAll();
</script>
</body>
</html>
القسم الخامس والأربعون: هل يمكن بناء التطبيق بالكامل بهذا الكود؟
نعم، يمكن استخدام هذا الكود كأساس قوي لتطبيق مسابقات يعمل داخل قسم HTML، خصوصًا إذا كان الهدف إنشاء واجهة تفاعلية محلية بدون Backend.
لكن يجب التفريق بين شيئين:
تطبيق مسابقات يعمل محليًا
و
منصة مسابقات أونلاين كاملة.
الكود الحالي يستطيع تقديم تجربة لعبة ممتازة على مستوى الواجهة والتفاعل والحفظ المحلي، لكنه لا يحول التطبيق تلقائيًا إلى منصة متعددة المستخدمين.
إذا أردت مستقبلًا منافسة حقيقية بين المستخدمين، ستحتاج إلى تطوير جانب الخادم وقاعدة البيانات والحسابات.
الخلاصة
إنشاء تطبيق مسابقات على AppCreator24 يمكن أن يبدأ بطريقة بسيطة جدًا:
إنشاء التطبيق → اختيار الاسم واللوجو والوصف → إضافة قسم HTML → تعديل الكود → وضع الكود → اختبار التطبيق → نشره → التسويق له → إضافة نظام تحقيق الدخل.
والميزة الحقيقية في هذا النوع من التطبيقات أن المحتوى نفسه قابل للتوسع باستمرار.
يمكنك البدء بعشرة أسئلة في كل قسم، ثم إضافة مئات الأسئلة، ثم إضافة مستويات وصعوبة وتحديات يومية وإنجازات ومتجر ومنافسات.
أما الربح، فلا ينبغي أن يكون الهدف الأول قبل التأكد من أن التطبيق يقدم تجربة ممتعة تجعل المستخدم يعود إليه باستمرار.
ابدأ بتطبيق جيد، ثم ابنِ حوله جمهورًا، وبعدها اجعل طرق تحقيق الدخل جزءًا من التجربة وليس عائقًا أمامها.
تحدّاك ليس مجرد تطبيق أسئلة وأجوبة؛ الفكرة الأفضل هي تحويل الأسئلة إلى تجربة لعب كاملة: نقاط، عملات، مستويات، إنجازات، تحديات ومنافسة.
إرسال تعليق