English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
مقدمة التجربة
1.1 محتوى التجربة
في هذا القسم من التجربة سنقوم بإنشاء تصميم دوال روسيا نايتس الرئيسية، وتحقيق الوظائف الأساسية وتشغيلها.
1.2 معلومات التجربة
رسم النافذة
تصميم مكعب
خوارزمية الدوران
دوال الحركة والإزالة
1.3 بيئة التجربة
مستعرض xface
مستعرض g++
مكتبة ncurses
1.4 تجميع البرنامج
يجب إضافة خيار -l لإدخال مكتبة ncurses:
g++ main.c -l ncurses
1.5 تشغيل البرنامج
./a.out
1.6 نتائج التشغيل
الخطوات التجريبية
2.1 ملفات الرأس
أولاً، قم ب 包اء ملفات الرأس وتعريف دالة تبادل ودالة العدد العشوائي، حيث سيتم استخدامها لاحقاً (دالة التبادل تستخدم لتحويل مكعبات، ودالة العدد العشوائي تستخدم لتحديد شكل المكعبات)
#include <iostream> #include <sys/time.h> #include <sys/types.h> #include <stdlib.h> #include <ncurses.h> #include <unistd.h> /* تبادل a و b */ void swap(int &a, int &b){ int t=a; a = b; b = t; } /* الحصول على عدد عشوائي بين (min, max) int getrand(int min, int max) { return(min+rand()%(max-min+1)); }
2.2 تعريف الفئة
بما أن محتوى البرنامج بسيط نسبياً، يتم تعريف فئة Piece واحدة هنا
class Piece { public: int score; //نقاط int shape; //تمثل شكل المكعب الحالي int next_shape; //تمثل شكل المكعب التالي int head_x; //موقع أول مكعب في المكعب الحالي، علامة الموقع int head_y; int size_h; //حجم المكعب الحالي int size_w; int next_size_h; //حجم المكعب التالي int next_size_w; int box_shape[4][4]; //مصفوفة شكل المكعب الحالي 4x4 int next_box_shape[4][4]; //مصفوفة شكل المكعب التالي 4x4 int box_map[30][45]; //استخدامها للتعليمات على كل مكعب في دبوس اللعبة bool game_over; //علامة نهاية اللعبة public: void initial(); //وظيفة التأهيل void set_shape(int &cshape, int box_shape[][4],int &size_w, int & size_h); //إعداد شكل المكعب void score_next(); //عرض شكل المكعب التالي وعدد النقاط void judge(); //تحقق من whether الطلب مليء void move(); //وظيفة الحركة، يتم التحكم فيها باستخدام ← → ↓ void rotate(); //وظيفة التدوير bool isaggin(); //تحقق من whether الإجراء التالي سيهدر أو يتداخل bool exsqr(int row); //تحقق من whether الصف الحالي فارغ };
2.3 إعداد شكل المكعب
هنا يتم تعريف 7 أنماط مكعبات عبر جملة case، ويجب استدعاؤها قبل سقوط مكعب جديد لضبط شكلها وموقعها البدائي
void Piece::set_shape(int &cshape, int shape[][4],int &size_w,int &size_h) { /*أولاً، قم بتأهيل مصفوفة 4x4 المستخدمة للتمثيل إلى 0*/ int i, j; لـ(ي=0;ي<4;ي++) لـ(ج=0;ج<4;ج++) shape[i][j]=0; /*إعداد 7 أنماط بدائية وإعداد حجمها*/ switch(cshape) { switch(cshape) case 0: size_h=1; size_w=2; size_w=3; case 3: size_w=4; shape[1][2]=1; shape[0][3]=1; case 6: size_h=2; size_w=2; shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; case 1: case 6: size_h=2; case 3: shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; case 2: case 6: size_h=2; size_w=3; case 3: shape[0][1]=1; shape[1][0]=1; shape[1][2]=1; shape[0][2]=1; case 6: size_h=2; size_w=2; size_w=3; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; case 4: case 6: case 5: size_w=2; size_w=3; shape[0][1]=1; shape[1][0]=1; shape[1][2]=1; shape[0][0]=1; case 6: size_h=2; size_w=3; shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; } break; //بعد إعداد الشكل، قم بتهيئة موقع الكتلة head_x=game_win_width/2; head_y=1; //إذا كانت الموضع عند التمهيد متطابقًا، فانتهت اللعبة~ إذا(isaggin()) /* GAME OVER ! */ }
game_over=true;
2.4 دالة الدوران
هناك استخدمت خوارزمية بسيطة للدوران على الكتلة، تشبه دوران المصفوفة، أولاً قم بتحويل مصفوفة shape إلى متماثل زاوي، ثم أعد إلى متماثل أفقي، حتى يكتمل الدوران، يجب التحقق من أن الكتلة لم تخرج عن الحدود أو تتطابق بعد الدوران، إذا كانت كذلك، فألغِ هذه المحاولة للدوران. { إنتـ temp[4][4]={0}; //مغير مؤقت إنتـ temp_piece[4][4]={0}; //مستودع مستخدم إنتـ(ي,ج,tmp_size_h,tmp_size_w); tmp_size_w=size_w; tmp_size_h=size_h; for(int i=0; i<4;i++) لـ(جـ=0;ج<4;ج++) temp_piece[ي][ج]=box_shape[ي][ج]; //تخزين الشكل الحالي، إذا فشل الدوران فاستعد إلى الشكل الحالي لـ(ي=0;ي<4;ي++) لـ(ج=0;ج<4;ج++) temp[ج][ي]=box_shape[ي][ج]; //متماثل زاوي ي=size_h; size_h=size_w; size_w=ي; لـ(ي=0;ي<size_h;ي++) لـ(ج=0;ج<size_w;ج++) box_shape[i][size_w-1-ج]=temp[i][ج]; //متماثل أفقي /*إذا كانت الشكل بعد الدوران متطابقًا، فاستعد إلى شكل الأبعاد المسبق*/ إذا(isaggin()) for(int i=0; i<4;i++) لـ(جـ=0;ج<4;ج++) box_shape[i][j]=temp_piece[i][j]; size_w=tmp_size_w; //تذكر أن size يجب أن يعود إلى الحجم الأصلي size_h=tmp_size_h; } /*إذا كانت اللف ناجحة، فأظهرها على الشاشة*/ else{ for(int i=0; i<4;i++) for(int j=0;j<4;j++){ if(temp_piece[i][j]==1){ mvwaddch(game_win,head_y+i,head_x+j,' '); //تحريك إلى مكان معين في نافذة game_win لطباعة الرمز wrefresh(game_win); } } for(int i=0; i<size_h;i++) للمحصول على (j = 0; j < size_w; j++) { إذا (this->box_shape[i][j] == 1) { mvwaddch(game_win, head_y + i, head_x + j, '#'); wrefresh(game_win); } } } }
2.5 وظيفة التحريك
إذا لم يضغط اللاعب على أي مفتاح، فإن方块 يجب أن ينزل ببطء، لذا لا يمكننا إحباطها في انتظار مدخل getch()، هنا نستخدم select() لإلغاء الحجب.
/* هنا يتم نسخ جزء من البرنامج فقط، للاطلاع على التنفيذ الفعلي، يرجى الرجوع إلى الكود المصدر */ struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec= 500000; if (select(1, &set, NULL, NULL, &timeout) == 0)
timeout هو الوقت الأقصى الذي ننتظر فيه مفتاحاً، هنا تم تعيينه على 500000us، إذا تجاوز هذا الوقت، فإننا لا ننتظر مدخل getch() بعد الآن، وننتقل مباشرة إلى الخطوة التالية.
إذا تم اكتشاف مفتاح في فترة timeout، فإن جملة if التالية صحيحة، يتم الحصول على قيمة key المدخلة، ويتم تنفيذ العمليات مثل التحريك إلى اليسار، اليمين، الأسفل، واللف، من خلال التحقق من قيمة key المختلفة.
if (FD_ISSET(0, &set))
while ((key = getch()) == -1) ;
طرق معالجة التحريك إلى اليسار، اليمين، والأسفل متشابهة بشكل أساسي، هنا سنأخذ وظيفة التحريك إلى الأسفل كحالة دراسة
/* هنا يتم نسخ جزء من البرنامج فقط، للاطلاع على التنفيذ الفعلي، يرجى الرجوع إلى الكود المصدر */ /* إذا كان المفتاح المدخل هو ↓ */ if(key==KEY_DOWN){ head_y++; //تقديم y لمستطيل if(isaggin()){ //إذا كان التداخل أو الخروج عن الحدود، قم بإلغاء هذه الحركة head_y--; /*لأننا توقفت، لذا نقوم بتعيين box الموجود على الخريطة كمساحات مستخدمة، ونستخدم 1 لتعيينها، ونستخدم 0 لتعيينها غير مستخدمة للمحصول على (i = 0; i < size_h; i++) for(int j=0;j<size_w;j++) if(box_shape[i][j]==1) box_map[head_y+i][head_x+j]=1; score_next(); // عرض النقاط وتقديم إشارة إلى القطعة التالية } /* إذا كان يمكن الهبوط، قم بإزالة عرض القطعة الحالية، قم بالهبوط إلى الأسفل وإعادة عرضها، انتبه إلى أن دورة for يجب أن تكون من الأسفل إلى الأعلى */ else{ للمحصول على (i = size_h - 1; i >= 0; i--) للمحصول على (j = 0; j < size_w; j++) { إذا (this->box_shape[i][j] == 1) { mvwaddch(game_win, head_y - 1 + i, head_x + j, ' '); mvwaddch(game_win, head_y + i, head_x + j, '#'); } } wrefresh(game_win); }
2.6 دالة تكرارية
وظيفة يجب إجراؤها بعد كل تحرك أو دوران، إذا كانت الوظيفة تعود بالصحيح فإنه لا يمكن الحركة، وإذا كانت تعود بالخطأ فإنه يمكن المضي قدمًا إلى الخطوة التالية.
bool Piece::isaggin() { للمحصول على (i = 0; i < size_h; i++) للمحصول على (j = 0; j < size_w; j++) { إذا (box_shape[i][j] == 1) { إذا (head_y + i > game_win_height - 2) // إذا كانت تخرج من الحدود السفلية إرجاع true; إذا (head_x + j > game_win_width - 2 || head_x + i - 1 < 0) // إذا كانت تخرج من الحدود اليسرى أو اليمنى إرجاع true; إذا (box_map[head_y + i][head_x + j] == 1) // إذا كانت تتداخل مع box مستخدمة إرجاع true ; } } إرجاع false; }
2.7 دالة مليئة الطبقات
أهم وظيفة هي إزالة الأعمدة الكاملة، يجب إجراء الحكم كلما توقف القطعة عن الهبوط.
void Piece::judge() { int i, j; int line = 0; // يستخدم لتحديد عدد الطبقات الكاملة bool full; للمحصول على (i = 1; i < game_win_height - 1; i++) { // استبعاد الحواف full = true; for(j=1;j<game_win_width-1;j++){ إذا (box_map[i][j] == 0) // هناك box غير مستخدم full = false; // فإن هذا يعني أن الطبقة ليست مليئة } إذا (full) { // إذا كانت الطبقة مليئة line++; // زيادة عدد الطبقات الكاملة score += 50; // زيادة النقاط ~ للمحصول على (j = 1; j < game_win_width - 1; j++) box_map[i][j] = 0; // قم بتفريغ الطبقة الحالية (تسجيلها كغير مستخدمة) } } /* بعد إتمام الحكم أعلاه، قم بمراجعة قيمة line، إذا كانت غير 0، فإن هذا يعني أن هناك طبقة مليئة ويجب إزالتها */ إذا (line != 0) { for(i=game_win_height-2;i>=2;i--){ int s=i; if(exsqr(i)==0){ while(s>1 && exsqr(--s)==0); //البحث عن الصف الذي يحتوي على الكتلة، وإعادة تحريكه إلى الأسفل for(j=1;j<game_win_width-1;j++){ box_map[i][j]=box_map[s][j]; //تحريك الطبقة العليا إلى الأسفل box_map[s][j]=0; //تنظيف الطبقة العليا } } } /*بعد إكمال تنظيف والتحرك، يجب تحديث الشاشة مرة أخرى، وإعادة طباعة game_win*/ for(int i=1;i<game_win_height-1;i++) for(int j=1;j<game_win_width-1;j++){ if(box_map[i][j]==1){ mvwaddch(game_win,i,j,'#'); wrefresh(game_win); } else{ mvwaddch(game_win,i,j,' '); wrefresh(game_win); } } } }
ثالثًا، استعراض التجربة
إلى هنا انتهت شرح هذه الدوال الرئيسية، بعد فهم وظائف هذه الدوال وتنفيذها، يمكنك التوجه إلى المصدر وتكملة الدوال الأخرى وكذلك الدالة الرئيسية main لتشغيل البرنامج! بالطبع، هناك العديد من طرق تنفيذ لعبة الربح، وقد تكون أفكارك وأساليبك مختلفة، قد تكون لعبتك الربحية أكثر بساطة وسلاسة! استمتع بها!:)
البيان: محتوى هذا المقال تم جمعه من الإنترنت، ويتمتع المالك الأصلي بحقوق الطبع والنشر، ويتم جمع المحتوى من قبل المستخدمين عبر الإنترنت الذين يقدمون المساهمات ويقومون بالتحميل، ولا يمتلك هذا الموقع حقوق الملكية، ولا يتم تعديل المحتوى بشكل يدوي، ولا يتحمل هذا الموقع أي مسؤولية قانونية. إذا كنت قد وجدت محتوى يشتبه في انتهاك حقوق النسخ، فلا تتردد في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال '#' بـ '@') لإبلاغنا، وقدم الدليل على الأدلة، وسيتم حذف المحتوى المزعوم الذي يشتبه في انتهاك حقوق النسخ على الفور.