نحن في السنة الأولى من دراساتنا الجامعية في الرياضيات التطبيقية والمعلوماتية في HSE في سانت بطرسبرغ. أثناء العمل في مشروع فريق الفصل الدراسي في C ++ ، قررنا كتابة نسخة كمبيوتر من Intellector مع روبوت - لعبة شطرنج على لوحة سداسية الشكل مع شخصيات خاصة.
في هذه المقالة سوف نتحدث عن كيفية تطور اللعبة ، وكيفية ترويض لوحة سداسية ، وكيفية الرسم على سطر الأوامر ، وكذلك كيف صنعنا روبوتًا لا يمكننا هزيمته تقريبًا.

هناك 3 أشخاص في فريقنا: يورا خودياكوف ، فاليرا جولوفين ، ميشا سافراسوف. بالنسبة لكل منا ، هذا هو أول مشروع جماعي ، لذلك في عملية العمل ، لم نكتب فقط عن المحترفين ، ولكن تعلمنا أيضًا العمل في فريق واستخدام أنظمة التحكم في الإصدار (في حالتنا - git). هناك عدد من الشذوذ في المشروع ، على وجه الخصوص ، الدراجات - هناك حلول جاهزة جيدة يمكن استخدامها. ومع ذلك ، كان الهدف من مشروعنا هو اكتساب الخبرة ، لذلك اخترعنا ونفذنا الكثير بأنفسنا.
ما هو العقل؟
بادئ ذي بدء ، يجدر شرح نوع اللعبة التي كتبناها.
ظهرت لعبة "Intellector" مؤخرًا ولا تزال تحظى بشعبية كبيرة. أقيمت أول بطولة مفتوحة في سان بطرسبرج هذا العام .

يختلف العقل عن الشطرنج في بنية المجال والقواعد ومجموعة القطع. ومع ذلك ، هناك العديد من العناصر المتشابهة: على سبيل المثال ، هناك شخصية بروجرسور في اللعبة ، والتي تلعب دور بيدق. تمشي فقط للأمام ويمكن أن تتحول إلى شخصية أخرى عندما تصل إلى الصف الأقصى.
الملك هنا هو شخصية تسمى العقل. الهدف من اللعبة هو قطع هذه القطعة ، وليس كش مات (على الرغم من أن هذا هو نفس الشيء تقريبًا ).
تنشأ الاختلافات في آليات اللعبة من تفاصيل المجال. مجال العقل متماثل ، مما يميزه بشكل كبير عن الشطرنج بجانبه الملك وجانبه الملك.
لفهم هذه المقالة ، لا يلزم معرفة القواعد والقدرة على اللعب.
العمارة العامة
ماذا نريد في تطبيقنا؟
لكي تعمل اللعبة ، تحتاج إلى تنفيذ مكونها الرئيسي: منطق اللعبة. يتضمن نموذج لوحة وقواعد النقل. بالإضافة إلى ذلك ، للراحة ، من المفيد الاحتفاظ بتاريخ الحركات وتنفيذ التراجع / الإعادة.
يجب عرض اللوحة والسماح للمستخدم باللعب. يتم ذلك عن طريق المكون الرسومي للعبة - الواجهة. يجب أن تحتوي الواجهة سهلة الاستخدام على قوائم وإعدادات.
بعد كل شيء ، أنت بحاجة إلى خصم للعب. قررنا إنشاء روبوت لهذه الأغراض حتى يتمكن اللاعب من التنافس مع الكمبيوتر. في هذه الحالة ، يجب أن يكون تعقيد الروبوت قابلاً للتعديل.
خطة التطبيق:
- منطق اللعبة
- نموذج لوحة سداسية
مخزنة كمصفوفة ثنائية الأبعاد من الخلايا السداسية - قواعد تحريك القطع
التحقق من قبول نقلة ، الحصول على جميع الحركات المتاحة للقطعة ، للاعب - نقل المحفوظات
تراجع عن الحركة وأعدها
- نموذج لوحة سداسية
- الواجهة
المخطط لها 2 واجهات: ncurses و Qt. يتم تنفيذ ncurses فقط في المشروع ، راجع قسم الواجهة للحصول على التفاصيل.- عرض عرض حقل
وتحديث حقل في وحدة التحكم - حرك المؤشر باستخدام مفاتيح لوحة المفاتيح ، العب بدون ماوس
- عرض تاريخ النص من الحركات
- عرض القائمة الرئيسية
- عرض عرض حقل
- الروبوت
- بوت عشوائي
- بوت جشع
- تم
تحسين برنامج Alpha-beta bot للتكرار عبر جميع الحركات
كيف يتم ذلك؟
يتم وصف بنية التطبيق المبسطة للغاية في هذا المخطط:

قسم اللعبة مسؤول عن كل منطق اللعبة ويخزن اللوحة.
عند تشغيل اللعبة مع الكمبيوتر ، تتفاعل اللعبة مع الروبوت من
وحدة تحكم الروبوت ، وتأخذ بيانات حول اللعبة من اللعبة وتنقلها إلى الواجهة لعرضها على اللاعبين. تقوم الواجهة بدورها بإرجاع نتيجة تفاعل المستخدم إلى اللعبة عبر وحدة التحكم.
يكون الارتباط الوسيط في شكل وحدة تحكم مفيدًا عندما تكون هناك عدة واجهات (خططنا في البداية لإنشاء واجهتين: وحدة تحكم ورسومية). كل منهم يتواصل مع اللعبة بطريقة موحدة من خلال وحدة التحكم ، بدلاً من قيام كل منهم بذلك بشكل مختلف.
تفاصيل تقنية
نموذج اللعبة
لوحة سداسية
ضع في اعتبارك قسم اللعبة من الرسم البياني أعلاه. يجب أن تخزن اللوحة وتتعامل مع كل منطق اللعبة.
لفهم أفضل ، يمكنك قراءة المقالة التي أخذنا منها هذه الفكرة.
سنخزن اللوحة بأكملها في مجموعة خلايا ثنائية الأبعاد ، تتم فهرسة عناصرها بواسطة أزواج من الإحداثيات
(w,h)كما هو موضح في الشكل أدناه. يبدو هذا الاختيار للإحداثيات أمرًا طبيعيًا ، ولكنه غير ملائم لوصف حركة الأشكال (لاحظ ، على سبيل المثال ، كيف تتغير الإحداثيات عند التحرك على طول القطر).

إحداثيات الخلايا على طول المحور الأفقي والمحور
wالعمودي لتسهيلh
وصف حركة الأشكال ، سنستخدم إحداثيات ثلاثية الأبعاد. دعنا نختار خلية ما كخلية مرجعية ذات إحداثيات
{0,0,0}(للراحة ، ستتزامن مع خلية (0, 0)المصفوفة).

الإحداثيات ثلاثية الأبعاد للخلايا بالنسبة للخلية المركزية ذات الإحداثيات يتم تحديد
{0,0,0}
الإزاحة على طول القطر "من اليمين إلى اليسار ، من الأسفل إلى الأعلى" بواسطة الإحداثيات
x، والإزاحة من أسفل إلى أعلى بواسطة الإحداثيات yوالإزاحة على طول القطر "من اليسار إلى اليمين ، ومن أسفل إلى أعلى" بواسطة الإحداثيات z. عند الانتقال إلى خلية مجاورة ، سيتغير الإحداثي المقابل بمقدار واحد. وهكذا ، تتلقى كل خلية ثلاثة إحداثيات ، كما في الصورة أعلاه.
في هذه الحالة ، يتم ترقيم الخلايا بشكل غامض. على سبيل المثال ، إذا
{0,0,0}تحركنا من الخلية المركزية ذات الإحداثيات إلى اليسار ثم لأعلى ، نحصل على إحداثيات الخلية {0,1,-1}. لكن نفس الخلية لها إحداثيات {1,0,0}إذا أتيت إليها مباشرة من الخلية المركزية ، كما ترون في الشكل السابق.

خيار آخر لتحديد إحداثيات الخلية
{1,0,0}.
عبور أي خلية في دورة "يسار لأسفل" - "أعلى" - "يمين لأسفل" يقودنا إلى نفس الخلية ، ولكنه يضيف متجهًا إلى إحداثياتها
{-1,1,-1}، مجموع إحداثياتها يساوي -1. عند أداء مثل هذه المسيرة ذهنيًا في نفس الاتجاه أو في الاتجاه المعاكس عدة مرات ، يمكننا تغيير إحداثيات أي خلية إلى إحداثيات مكافئة ، والتي ستختلف حسب المتجه المتناسب {-1,1,-1}. للتخلص من الغموض ، في كل فئة تكافؤ ، نختار كممثل ثلاثي الإحداثيات ، مجموعها يساوي صفرًا. هذا الاختيار للإحداثيات فريد من نوعه (أثبت ذلك!).
دعنا نصف الخوارزمية للتحويل من إحداثيات ثنائية الأبعاد إلى إحداثيات ثلاثية الأبعاد والعكس بالعكس داخل الفصل
Position.
Position(int w, int h) //
: x_{-w/2 — w % 2 - h}
, y_{w % 2 + 2 * h}
, z_{w / 2 — h} {
}
int posW() const { //
return -x_ + z_;
}
int posH() const { //
return (x_ + z_ — (x_ + z_)%2) / 2 + y_;
}
لاحظ أن المُنشئ ينتج إحداثيات
(x,y,z)تضيف ما يصل إلى الصفر. في هذه الحالة، وتحويل الإحداثيات (x,y,z)ل (w,h)يعمل بشكل صحيح لأية مجموعة من الإحداثيات (لايوجد المبلغ ليكون صفر).
كيف وجدنا كل هذه الصيغ؟ بطريقة الوخز العلمي: من خلال تحليل التغيير في الإحداثيات ثلاثية الأبعاد عندما يتم إزاحة أحد الإحداثيات ثنائية الأبعاد بواسطة
1(المُنشئ) وفي الاتجاه المعاكس (الطرق).
باستخدام الإحداثيات ثلاثية الأبعاد ، يمكننا بسهولة التحقق من أن الخلايا على خط واحد. على سبيل المثال ، للتحقق من وجود خليتين على نفس القطر
z، تحتاج إلى العثور على متجه يربط بين هذه الخلايا والتحقق من أن فئة التكافؤ بها تحتوي على متجه من النموذج{0, 0, z}... يمكن أن يكون Z أي شيء - هذا الرقم يعطي المسافة بين الخلايا. سيكون من السهل جدًا تنفيذ فحص الحركة للتأكد من صحتها وإيجاد جميع الخلايا المتاحة للحركة.
في مصفوفة ثنائية الأبعاد تمثل اللوحة ، سنخزن معلومات حول موضع الأشكال. في كل خلية ، إذا كانت هناك قطعة شطرنج ، فسنخزن نوعها ولونها.
في تطبيقنا في فئة اللوحة ، نقوم فقط بتخزين أنواع القطع في الخلايا. نحتاج إلى فصل يمكنه العثور على جميع الحركات الممكنة للقطع على هذه اللوحة والتحقق من صحة الحركات.
يتحرك للقطع
لقد أنشأنا فصلًا
FigureMoveValidatorبه 6 أحفاد لكل نوع من الأشكال (كان من الممكن الاستغناء عن أحفاد إذا قمنا في كل طريقة بعمل حالة تبديل لنوع الشكل). يحتوي مُنشئ الفئة على معلمتين: مرجع الموضع واللوحة. أيضا في الفصل هناك طريقتان allMovesو checkMove.
لنفكر في الطريقة
allMoves. للعثور على جميع الحركات ، دعنا نؤلف مصفوفة من ناقلات الإزاحة المحتملة ونمر بها. بالنسبة للقطع التي تتحرك خطوة واحدة ، نحتاج إلى التحقق من أننا لم نقفز من على اللوح ولم ندخل إلى الخلية حيث توجد قطعتنا. بالنسبة للأشكال التي تحرك عدة خلايا في خط مستقيم ، أضف متجه حركة أثناء مرور الشرط السابق.
الآن
checkMove... نتذكر أننا نعرف كيف نتحقق مما إذا كانت الأرقام على نفس الخط المستقيم. إذا تحققنا من عدم وجود قطع أخرى في هذا الخط ، فسنحصل على طريقة جاهزة للمسيطر (تناظرية الرخ). إذا كانت القطع تقع على خط مستقيم واحد ، فيمكننا إيجاد المسافة بينها ، والحصول على طرق لـ Progressor (بيدق) ، و Defenser (يمشي مثل الملك) ، و Intelligence (ملك ، لا يمكن قطعه فقط) و Liberator (يمكن المشي عبر خلية واحدة إلى أي جانب). لا يزال هناك معتد (التناظرية فيل)، الذي انتقل إلى الخلايا بشكل مائل في ستة اتجاهات من النظام الحالي (من خلية {0, 0, 0}إلى {0, 1, 1}ل {0, 2, 2}، الخ: انظر الخلايا الرمادية في الصورة أدناه). بالنسبة لهذا الشكل ، يمكنك محاولة صفر أحد الإحداثيات والتحقق من تساوي الإحداثيين المتبقيين في القيمة المطلقة (بفضل الإحداثيات ثلاثية الأبعاد).

التحركات الممكنة للمعتدي
الآن نحن بحاجة لمعرفة ما يجب فعله بهذه التحركات. لنقم بإنشاء فئة نقل تقوم بتخزين جميع المعلومات اللازمة لهذه الخطوة. قررنا تخزين موضعين و 4 قطع: الموضع الذي تتحرك منه القطعة ، والموضع الذي ستأتي منه ، والمعلومات المتعلقة بالقطع التي وقفت في كل من هذه الخلايا وأيها ستقف بعد تطبيق الحركة. هذا سيجعل من السهل تنفيذ نظام محفوظات النقل وتحريك التراجع.
واجهه المستخدم
هندسة معمارية
التطبيق مكتوب في مكتبة وحدة التحكم ncurses (هنا برنامج تعليمي له ) . تتيح لك هذه المكتبة إنشاء رسومات زائفة في وحدة التحكم. على سبيل المثال ، يعتمد Midnight Commander و Nano على ذلك .
قد يبدو الاختيار غريبًا جدًا: هناك العديد من المكتبات الأخرى ، وهي أكثر جمالا وملاءمة ومتعددة المنصات. إنه مرتبط بحقيقة أننا خططنا في البداية لإنشاء واجهتين: وحدة تحكم ورسومية. لم نتمكن من كتابة واجهتين بحلول الوقت الذي تم فيه تسليم المشروع وبدلاً من ذلك قمنا بتوفير المزيد من الميزات في إصدار وحدة التحكم. على الرغم من أنه من الناحية المعمارية ، لا يزال التطبيق مصممًا لواجهات مختلفة.
هناك 2 الكيانات الرئيسية: عرض و حدة تحكم... تُظهر المشاهدات الصورة للاعبين ، وتتوسط وحدة التحكم بين طرق العرض المختلفة ونموذج اللعبة الداخلي.
تتعامل الشاشة مع جميع تفاعلات المستخدم: موضع المؤشر وحركته ، واختيار الشكل ، وتسليط الضوء على الحقول المتاحة ، وإكمال اللعبة ، والمزيد. تشير الإجراءات التي تؤثر على اللوحة إلى وحدة التحكم وإرسال / تلقي المعلومات الضرورية من / إلى النموذج.
تقوم الشاشة بإنشاء نسختها الخاصة من اللوحة ، ولكن مع وجود معلمات إضافية تحتاج إليها ، مثل موضع المؤشر ولون الخلايا. لا يمكن إضافة هذه المعلمات إلى النموذج الرئيسي لأن التعيينات المختلفة تحتاج إلى معلمات مختلفة. على سبيل المثال ، في واجهة وحدة التحكم ، تحتاج إلى تخزين موضع المؤشر ، ولكن ليس في الواجهة الرسومية ، نظرًا لأن تحديد الشكل وحركته يتم باستخدام الماوس.
إليك ما يحدث إذا أراد اللاعب معرفة الحقول المتاحة للحركة:
- يقوم اللاعب بتحريك المؤشر إلى حقل الشكل ويضغط على مفتاح المسافة
- الحقل الذي يحتوي على الشكل محددًا
- تشير الواجهة إلى طريقة
selectCellعلى وحدة التحكم - الطريقة
selectCellتشير إلى طريقةallFigureMovesالنموذج allFigureMovesينشئFigureMoveValidatorالتي تحسب جميع الحركات المتاحةallFigureMovesتم العثور على عمليات نقل تعود إلى وحدة التحكم- تقوم وحدة التحكم بتمريرها إلى الواجهة
- تعيد الواجهة رسم الحقل ، مع إبراز الحقول المتاحة

يوجد المؤشر في منتصف اللوحة: على مربع أزرق باهت. قبل نقله إلى هذا الموضع ، حدد المستخدم شكلاً. يتم تمييز الحركات المتاحة لها باللون الأخضر ، وتكون الخلية المحددة نفسها أرجوانية.
كيف يتم رسم الميدان؟
يتم تقديم واجهة وحدة التحكم في نافذة مستطيلة تحتوي على سطور من النص. إذا وضعت الرموز في الأماكن الصحيحة وقمت بتلوينها ، تحصل على صورة:

(Evil Pacman ، مرسوم بالأحرف "o")
وظيفة
move(int y, int x)في ncurses تغير الموضع الحالي ، addch(chtype c)وتضيف الوظيفة حرفًا وتغير الموضع الحالي 1 إلى اليمين ( x —> x+1).
يمكن تخزين الصورة المعقدة كمصفوفة ثنائية الأبعاد وعرضها سطراً بسطر: عندما ينتهي السطر ، انقل الموضع الحالي إلى بداية السطر التالي. المبدأ مشابه جدًا للآلة الكاتبة.
على كمبيوتر المستخدم ، سيتم تلوين الحقل في لعبتنا إذا كان الجهاز يدعم الألوان وسمات النص الأخرى.
يسمح Ncurses للمطور بتغيير سمات النص عندما يتم إخراجه إلى وحدة التحكم (لون ، غامق ، وميض). للقيام بذلك ، اكتب الرمز:
attron( *attributes* );
addch(c);
attroff( *attributes* );
كل رمز له لونه ولون خلفيته. لوحات المفاتيح الحديثة تدعم بحد أقصى 256 لونا، ولذلك عليك أن العمل مع محدودية مجموعة : حزينة جدا من حيث التصميم اللون.
يمكن تخزين الصور للمخرجات في كود (كما فعلنا في البداية) ، أو يمكن تخزينها في ملفات منفصلة وقراءتها منها عند بدء تشغيل البرنامج. لهذا توصلنا إلى تنسيق الملف الخاص بنا
*.btn.
يقوم بتخزين صورة نصية ستقرأها اللعبة وتعرضها. على سبيل المثال ، شكل أو النقش "فوز الأبيض" / "فوز الأسود" ، أو زر قائمة. في هذه الحالة ، قد تحتاج أحيانًا إلى الشفافية حتى لا تحل محل ما تم رسمه مسبقًا. للقيام بذلك ، يمكنك إضافة تجزئة في السطر الأول
#وبعد قائمة الرموز "الشفافة" التي سيتم تجاهلها في الإخراج.
على سبيل المثال ، لنفترض أن لدينا 3 مستطيلات مرسومة على الشاشة:

أضف مستطيلاً من الملف التالي إلى المركز:
#C
AAAAAAAAA
ACCCCCCCA
ACCCCCCCA
ACCCCCCCA
ACCCCCCCA
ACCCCCCCA
AAAAAAAAA
ونحصل على الصورة التالية:

(مظللة باللون الأصفر للتوضيح)
كان هذا التنسيق مفيدًا بشكل خاص عند تطوير القوائم.
قائمة طعام
تحتوي اللعبة أيضًا على قائمة تحتوي على الإعدادات ويتم رسمها قبل بدء اللعبة وبعد انتهائها.
تتم قراءة أزرار القائمة من الملفات
*.btn. يجب أن تحتوي هذه الأزرار على نص كبير وجميل. لا يمكننا الرسم بشكل جميل باستخدام أحرف ASCII ، ولكن أداة figlet ، وهي أداة لعرض نص ASCII في خطوط مختلفة ، يمكنها:

تعمل الأزرار على تأطير الملصقات المقروءة من الملف:

ثم يتم توسيطها وإخراجها بالتتابع:

في بعض القوائم ، يمكنك أن تفشل ، على سبيل المثال ، يمكنك ضبط درجة تعقيد ولون الروبوت:

الجزء الأكثر إثارة للاهتمام في تصميم نظام القائمة هو دمج عناصره في نظام واحد. يتم ذلك بواسطة عنصر منفصل من النظام ، والذي أطلقنا عليه اسم معدد الإرسال. الاسم مستوحى من معددات المحطة الطرفية .
يقبل معدد الإرسال المفتاح الذي يضغط عليه المستخدم ويرسله إلى جميع القوائم المعروضة حاليًا. تقرر كل قائمة بنفسها ما يجب فعله بالمفتاح: تجاهل أو معالجة بطريقة ما. يتم إرجاع نتيجة معالجة القائمة إلى معدد الإرسال ، الذي يقرر ما يجب فعله بعد ذلك: إغلاق القائمة ، وإنشاء قائمة جديدة ، وتغيير الإعدادات ، وإغلاق التطبيق ...
تبين أن هذا النهج مناسب لاحتياجاتنا ، على الرغم من أنه قد لا يكون كافيًا بشكل عام: يمكن لقائمتين مختلفتين الاستجابة لنفس المفتاح ، ويجب أن يكون المستخدم قادرًا على اختيار القائمة التي يجب أن تستجيب. سيكون الحل هو اختصار لوحة مفاتيح خاص يسمح لك بالتبديل بين القوائم المختلفة. على سبيل المثال ، كما في tmux . لكن هذا مبالغة ولم يكن مطلوبًا.
الروبوت
كما ذكرنا أعلاه ، تحتوي لعبتنا على روبوت. حاولنا أن نجعلها ممتعة لكل من اللاعب المبتدئ وذوي الخبرة.
قبل وصف برامج الروبوت ، أود التحدث عن بعض تفاصيل التنفيذ. لقد خصصنا بعض الوزن لكل شكل. كلما زاد حجمها ، زادت قيمة هذا الرقم. نحدد مدى جودة الموضع على السبورة باستخدام الصيغة (مجموع أوزان القطع البيضاء) - (مجموع أوزان القطع السوداء). من المفيد لـ White زيادة هذا التعبير ، وللأسود تقليله.
يعد الحساب الكامل لشجرة الحركات بأكملها مهمة صعبة للغاية ، لذلك قمنا بحساب الحركات القليلة الأولى فقط (بالنظر إلى الأمام ، سأقول أنه تم حساب 6 حركات للأمام). لقد اعتبرنا جميع الحالات الموجودة على السبورة عند عمق معين بمثابة أوراق شجرة العبور.
هناك ثلاثة أنواع مختلفة من الروبوتات في اللعبة:
RandomBot— . .GreedyBot— «» , , .AlphaBetaBot— , - .
عندما بدأنا تجربة التحسينات ، أدركنا أنه لا يمكننا الاستغناء عن اختبارات الوحدة للروبوت ، لذلك أنشأنا شقيقًا توأمًا لـ
AlphaBetaBot"a" ، والذي أطلقنا عليه اسمًا OptimizedAlphaBetaBot. اختبرنا جميع أفكار التحسين OptimizedAlphaBetaBot، وساعدت اختبارات الوحدة على التأكد من أن الشقيقين التوأمين يجدان نفس الخطوة المفيدة. لقد خدمنا RandomBot جيدًا من خلال إنشاء أنماط عشوائية على السبورة. للقيام بذلك ، كان يكفي أن تسأل RandomBotوتذهب عدة مرات لكلا الجانبين.
إجمالاً
OptimizedAlphaBetaBot ، تم تنفيذ 3 تحسينات رئيسية (هنا يتم تقديمها بترتيب تنازلي للفائدة):
- باستخدام التراجع. بعد هذا التحسين ، لم يعد من الضروري نسخ اللوحة عدة مرات للقيام بخطوة.
-
FigureKeeper, , .std::vector. -
std::unordered_mapZobrish hashing.
بالإضافة إلى التحسينات الرئيسية ، كانت هناك أيضًا تحسينات أصغر. على سبيل المثال ، إذا قمت ، قبل الفرز ، بحساب جميع قيم المواضع على اللوحة ، مع مراعاة حركة معينة ، فلن تحتاج بعد الآن إلى فرز الكائنات المعقدة
Move، ولكن ببساطة int.
في البداية ، تم التخطيط لتنفيذ عدة مجموعات من وظائف التقييم: على سبيل المثال ، الرقم الذي يهدده العدو يقدر بنصف التكلفة. ولكن اتضح أن الروبوت يلعب بشكل "نظيف" تمامًا ، ويفقد بعض القطع ، لذلك تبين أن كمية بسيطة كانت أكثر فاعلية.
ومع ذلك ، لا تزال بنية الروبوت تدعم إضافة وظائف تقييم جديدة. للقيام بذلك ، تحتاج إلى تحديد ثلاثة أشياء فقط:
- وظيفة إذا كنت بحاجة إلى حساب التكلفة "من الصفر" لترتيب معين للأرقام
- دالة دلتا ، والتي يجب أن تعيد حساب التكلفة لحركة معينة بسرعة.
- عدد هذه المجموعة من الوظائف لمنشئ الفئة المخصصة
FunctionSet.
يمكنك تشغيل معركة الروبوتات ومشاهدة العملية.

لعبة مكونة من 2 روبوتات من نفس الصعوبة (المستوى 4 من 6). يوجد المؤشر في وسط الميدان للعبة بأكملها
خاتمة
لقد طبقنا لعبة مشابهة للشطرنج ، لكن بقواعد مختلفة ولوحة غير عادية. تنفيذنا لديه مجال للتوسع. إن برنامج Intellector نفسه يتطور ويتغير أيضًا: مؤخرًا كان هناك تحديث للقواعد ، وهو ما لم ندعمه بعد في تطبيقنا. على سبيل المثال ، لا يمكنك الآن عبور خط الوسط لأول 2 منعطفات.
بالإضافة إلى ذلك ، هناك العديد من الميزات التي خططنا لها في الأصل ، ولكن لم يكن لدينا الوقت لتنفيذها بحلول وقت المشروع. على سبيل المثال ، في هذا التطبيق أود حقًا مشاهدة لعبة على الشبكة. أيضًا ، لن تؤذي واجهة لطيفة عبر الأنظمة الأساسية ، على سبيل المثال ، على Qt.
ربما سيظهر كل هذا أو جزء منه في المستقبل القريب. حتى ذلك الحين، شكرا للقراءة!
مستودع جيثب