بسم الله الرحمن الرحيم
دلني الأستاذ أحمد الشنقيطي منذ فترة على لعبة الوصول لرقم عن طريق القيام بعمليات حسابية على الأعداد المعطاة
كتمرين برمجي ... لمنع الركود الفكري :lol: جزاه االه خيراً
وشرح اللعبة ..
مثلاً كيف نصل للرقم 15 عن طريق القيام بعمليات حسابية على الأرقام 1 و2و3و4و5 الحل هو 1+2=3 ثم 3+3=6 ثم 6+4=10 ثم 10+5=15
الفكرة كانت في كتابة الخوارزمية ثم كتابة الكود ... كانت فكرة الحل التي فكرت بها هي أن أترك الحاسوب يجرب جميع الاحتمالات الممكنة ... حتى الوصول للرقم المطلوب
و تعميماً للفائدة أضع بين يديكم كما نصحني الأستاذ أحمد الكود ... وقد شرحته بناءً على القاعدة الرائعة والتي تقول :
:happy:اقتباسحسب رأيي المتواضع :مشروع بدون documation لا أعتبره open source :)
وأرجو أن يكون فيه الفائدة ... أترككم مع الكود والشرح والسلام عليكم :
//قبل البدء :أود تذكيرك أخي بأن البرمجة هي كتابة الخوارزمية فإذا أردت أخي الاستفادة من الشرح دون تضييع وقتك فاذهب إلى جسم الدالة التالية وستجده في آخر البرنامج
void NewProblem(int*,int,int);//أهم دالة هي التي تقوم بإدارة حل المسألة ككل وكل مسألة جزئية
int sum(int a,int b){return a+b;}//تابع يعيد مجموع الدخل
int mul(int a,int b){return a*b;}//تابع يعيد جداء الدخل
int sub(int a,int b){if(a>b)return a-b;else return b-a;}//تابع يعيد فرق عددين بالقيمة المطلقة
int div_(int a,int b){if(a>b&&a%b==0)return a/b;else return 0;}//تابع يعيد ناتج قسمة الدخل الاول على الثاني ان كان الناتج عدد صحيح
int C(int a){return a*(a-1)/2;}//C(n,2) number of couples
//هذا التابع يحسب عدد احتمالات الأزواج من أجل ن عدد
int function(int,int,char );//للقيام بعملية على أول وسيطين نمرر العملية على شكل رمز بالوسيط الثالث
void mov(int*,int*,int,int);//دالة نسخ مصوفة مع استثناء احد العناصر
void ShowOperators();//لإظهار العمليات المتبعة بعد الانتهاء
char operation[4]={'+','-','*','/'};//مصفوفة لتعداد العمليات المتوفرة
char *Operators;//مجرد مؤشر سيؤشر لا حقا على مصفوفة تحتفظ بنوع العملية المجراة على الأعداد
int *Oprands;//مؤشر سيؤشر لا حقاً على مصفوفة للاحتفاظ بالأرقام التي أجريت عليها العمليات
int operators_=0;//عدد العمليات المجراة
#include<iostream.h>//تضمين مكتبة الدخل والخرج
#include<windows.h>//تضمين مكتبة التعامل مع ويندوز
//ملاحظة :لمحبي اللينوكس يمكنهم إلغاء المكتبة السابقة وأترك لهم أن يضعوا داخل التوابع التالية ما يكافئ عملها
void pause(){system("pause");}//لإيقاف البرنامج مؤقتا
void exit(){exit(0);}//للخروج قبل نهاية الدالة الرئيسية
void cls(){system("cls");};//لمحو كل ماهو ظاهر على الكونسول
void main()//الدالة الرئيسية
{
cls();// محو الشاشة ... قد تبدو بلا فائدة ولكن ستتضح فائدتها في السطر 74
int nsize;//حجم مصفوفة المدخلات وهي عدد الأعداد
cout<<"How Many Numbers You Want To enter ?\n";//سؤال المستخدم
cin>>nsize;//ادخال عدد الاعداد
int *n=new int[nsize];//انشاء مصفوفة بعدد الارقام تماما
for(int i=0;i<nsize;i++){cout<<"Enter Number("<<i<<") :";cin>>n;}//حلقة لادخال العناصر الرقمية كلها
cout<<"Enter The Goal Number : ";//سؤال
int N;//الرقم الهدف
cin>>N;//ادخال الرقم الهدف
Operators=new char[nsize-1];//عدد العمليات الأعظمي هو عدد الأرقام ناقصا 1 لذلك ننشئ مصفوفة العمليات بهذا الحجم
Oprands=new int[(nsize-1)*2];//سنستعمل عددين في كل عملية لذلك ننشئ مصفوفة للاحتفاظ بهذه الأعداد بهذا الحجم
NewProblem(n,nsize,N);//استدعاء دالة حل المسألة ووسطاؤها هم على الترتيب مصفوفة الأعداد ثم حجمها ثم الرقم الهدف
}
int function(int a,int b,char c)
{//هذه الدالة للتعرف على رمز العملية المراد القيام بها ثم القيام بها
if(c=='+')return sum(a,b);
else if(c=='-')return sub(a,b);
else if(c=='*')return mul(a,b);
else if(c=='/'&&b!=0)return div_(a,b);
else return -1;//في حالة الخطأ في الكود... وليس لها داع
}
void mov(int*newn,int*n,int i_,int new_size)
{//عندما نختار رقما من المصفوفة فيجب حذفه من المصفوفة عند اختيار الرقم الثاني حتى لا نختار هذا الرقم مرتين وهذه هي وظيفة هذه الدالة
for(int i=0;i<new_size&&i!=i_;i++)
newn=n;
for(;i<new_size;i++)
newn=n[i+1];
}
void SaveOperator(int a,int b,char operation)
{//تقوم بحفظ العملية في مصفوفتي العمليات والمعاملات
Operators[operators_]=operation;//حفظ العملية
Oprands[operators_*2]=a;//حفظ المعامل الاول
Oprands[operators_*2+1]=b;//حفظ المعامل الثاني
}
void ShowOperators()
{
for(int i=1;i<operators_+1;i++)
{
cout<<Oprands[i*2]//عرض المعامل الاول
<<Operators//عرض العملية
<<Oprands[i*2+1]//عرض المعامل الثاني
<<'='//عرض اشارة المساواة
<<function(Oprands[i*2],Oprands[i*2+1],Operators)<<endl;////بدل الاحتفاظ في الجواب سنستدعي دالة الحساب ثانية
}//عرض العمليات المخزنة في مصفوفتي العمليات والمعاملات.....لمن أحب تطروير البرنامج يمكنه أن ينشئ بنية معطيات
//للعمليات بدل أن نستخدم مصفوفتين ولا بأس بإضافة الناتج أيضا للبنية كما أنه يجب تغيير حالة العرض عند عملية طرح
//عدد صغير من عدد كبير .. العملية التي يجريها التابع هي الفرق وهو عملية تبديلية أما عند العرض سيظن المستخدم أن
//البرنامج أوجد الناتج معكوس الإشارة لذلك يمكن اضافة شرط لعكس المعاملات في حالة الطرح عندما يكون المعامل الاول اصغر من الثاني
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////Algorithm's Function .............////////////////////////////////
////////////////////////////////////////BY Mostafa 36a2/////////////////////////////////////////////
void NewProblem(int*n,int new_size,int Goal)
{//دالة حل المسالة ككل والمسائل الجزئية ..... هنا الخوارزمية .... هنا لب البرنامج ... وكل الدوال الباقية مجرد مظاهر
for(int w=0;w<new_size;w++)//من اجل جميع أرقام الدخل
if(n[w]==Goal){cout<<n[w]<<"\tIt's In Your Numbers Man!!!\nIs It A Test For Stupidity :)?\nNo Problem Try Again .."<<endl;pause();main();}
//الشرط السابق في حال أدخل المستخدم الرقم الهدف ضمن أرقام الدخل ... وهنا لا داعي للبحث عنه فهو موجود... لاحظ استدعاء الدالة الرئيسية بدل الخروج من البرنامج ... فقد يكون المستخدم أخطأ سهواً
int new_element=0;//العنصر الجديد هو ناتج العملية التي ستجرى على المعاملين
int a,b;//المعاملان الذين ستجرى عليهما العملية
for(int i=0;i<C(new_size);i++)
{//سنكرر بعدد الأزواج الممكن تشكيلها من أرقام الدخل
a=n;//نختار أحد الأرقام
int *newn=new int[new_size-1];//ننشئ مصفوفة جديدة أصغر من السابقة بواحد
mov(newn,n,i,new_size-1);//المصفوفة الجديدة هي نفس القديمة بعد استثناء الرقم المختار
//لمن أراد تطوير البرنامج يمكنه تغيير طريقة استثناء الرقم المختار
for(int j=i;j<new_size-1;j++)//الآن لدينا مصفوفة خالية من المعامل الاول سنكرر من اجل جميع عناصرها
{
b=newn[j];//نختار احد هذه العناصر
for(int op=0;op<4;op++)//نكرر من اجل جميع العمليات
{
new_element=function(a,b,operation[op]);//العنصر الجديد سيكون ناتج العملية التي ستجرى على المعاملين
operators_++;//نزيد عدد العمليات المجراة لأننا أجرينا عملية للتو
SaveOperator(a,b,operation[op]);//نحفظ العملية التي قمنا بها
if(new_element==Goal)//هذا يعني اننا وصلنا للرقم المطلوب
{
cout<<"It Takes "<<operators_<<" operators"<<endl;//رسالة تخبر المستخدم بعدد العمليات المجراة
cout<<"I Think It was Speeder Than You Thought Mr.Ahmad ? isn't it!!\n";//هذه رسالة خاصة للأستاذ أحمد تخبره بأن البرنامج سريع جدا حتى لو كانت الخوارزمية غبية!!هههه
//ملاحظة هامة : قد تنزعج أخي أثناء قراءتك للشرح من وجود مثل السطر السابق ولكنني أصررت على عدم حذفه وذلك للتأكيد على أن هذه المظاهر غير مهمة والمهم هو الخوارزمية
ShowOperators();//طبعا سنعرض للمستخدم العمليات التي قمنا بها
if(operators_>7)cout<<"IT Sometimes Look Crazy To Do That Like This :p\n";//رسالة البرءة من الحول والقوة فالخوارزمية قد تؤدي الى سلوك طريق طويل دون الحاجة للتطويل
//وهنا يكمن جوهر التطوير بإضافة بعض الخطوات والشروط في الخوارزمية كي تسلك الطريق القصير حتى لا تلتقي بالذئب
cout<<"With All My Thanks For You MY Teacher \nI am Ready For Any Thing :)\n";//رسالة الشكر للأستاذ أحمد
//goto : ملاحظة هامة
pause();//ايقاف البرنامج مؤقتا حتى يلحق المستخدم يقرا
cout.flush();//لافراغ ذاكرة العرض فأحيانا مع استخدام دالة الايقاف السابقة يمتنع البرنامج عن افراغ ذاكرة العرض
pause();//لا أدري لماذا أضفتها ثانية ولكن لا مانع
exit();//انهاء البرنامج
//ويمكن بدل الانهاء استدعاء الدالة الرئيسة ثانية ولكن يجب تصفير المتحولات وخصوصا عدد العمليات
}
else if(new_element!=0&&new_size!=2)//ان لم نصل للرقم الهدف وكانت المسألة الحالية بها أكثر من رقمين
//سبب الشرط الأخير هو أنه إن لم يكن هناك سوى رقمين وجربنا جميع العمليات فهذا يعني احد امرين اما اننا في مسألة جزئية لا تفيد في الحل
//وإما أن الحل أصلاً غير ممكن وذلك ان كنا في المسألة الرئيسسة
{//انتباه: الآن سننشئ مسألة جديدة
//المسألة الجديدة تعالج الاحتمالات الناتجة عن استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما
newn[j]=new_element;//لاحظ كيف استبدلنا المعامل الثاني بالرقم الناتج ...ولا تنسى أن هذه المصوفة قد حُذف منها المعامل الاول سابقا
NewProblem(newn,new_size-1,Goal);//استدعاء حلال المسائل لمسألتنا الجديدة ... الهدف هو نفسه والحجم أصغر بواحد والأعداد هي نفس أعداد المسألة السابقة بعد استبدال المعاملين المستخدمين بالرقم الناتج عن العملية بينهما
newn[j]=b;//إذا وصلنا لهذه التعليمة فهذا يعني أن التفريع السابقة والتي دخلت إلى حل مسألة جديدة قد فشلت في الوصول للهدف ... والآن سنعيد للخانة التي استبدلناها قبل الاستدعاء قيمتها الأصلية جتى نتابع الحساب وكأن شيئا لم يكن
}//انتهاء الشرط
operators_--;//إذا وصلنا إلى هنا فهذا يعني أن العملية التي أجريت بين المعامل الاول والثاني كانت خاطئة لأننا جربنا كل الاحتمالات الناتجة عنها وفشلت... ولذلك سننقص عدد العمليات المجراة لأن العملية المجراةكانت خاطئة
}//انتهاء تجربة العمليات بين المعاملين الاول والثاني
}
delete newn;//إذا وصلنا إلى هنا فهذا يعني أن المعامل الاول من البداية كان خاطئاً لأننا جربنا كل الاحتمالات الناتجة عن اختياره وفشلت لذلك سنحذف المصفوفة الناتجة عن حذفه لأننا سنختار غيره
}//انتهاء دالة
}//انتهاء دالة حل المسألة
void By_Mostafa_36a2(){
/* ما كان من خير فمن الله جل جلاله
وما كنا من تقصير فمني ... أستغفر الله العظيم
أخي القارئ... إذا كان هذا الشرح قد أفادك فلا تحرمني دعوة في ظهر الغيب جزاك الله خيراً
وأرجو أن نتعاون لسد أي خلل
والسلام عليكم*/
};







