برنامج الحياة في ++C

c

برنامج الحياة

وضع هذا النوع من البرنامج الرياضي جون كونواي من جامعة كامبريدج لتمثيل مستعمرات الكائنات المجهرية . وتمثل كل خلية حية علي الشاشة بمربع في مصفوفة ثنائية وتعيش الخلايا وتتكاثر بناء علي قواعد بسيطة .

ونستخدم في برنامجنا المحارف الرسومية وحتي نضاعف عدد الخلايا فإننا نعتبر أن كل موضع علي الشاشة يتسع لخليتين , فإذا كانت الخلية العلوية فقط هي الحية , استخدم المحرف '\xDF' والمعرف بالثابت bot , وفي حالة كون الخليتين حيتين معا , استخدم المحرف '\xDB' المعرف بالثابت both , وفي حالة كونهما ميتين معا , استخدم المحرف '\X20' المعرف الثابت none , كما يظهر في الشكل . وعلي ذلك فالشاشة التي تتضمن 25 صفا تعتبر كما لو كانت تحتوي علي 50 صفا .

 

قواعد الحياة

تكون الخلية إما حية (مربع أبيض) أو ميتة (مربع أسود , أو فارغ) . لكل خلية ثمانية جيران , يمين وشمال , اعلي وأسفل , وأربعة في الأوتار . والقواعد كالتالي :

- الخلية الميتة تولد (تصحح حية) إذا جاورها ثلاثة جيران بالضبط .

- الخلية الحية التي يجاورها خليتان حيتان أو ثلاثة خلايا حية تظل علي قيد الحياة .

- تفني الخلية (تصبح ميتة) إذا قل جيرانها الأحياء عن بعد 2 أو زادوا عن 3 .

ويقوم البرنامج بحساب حالة كل خلية , ويقرر مصيرها طبقا للقواعد السابقة . ومع استمرار البرنامج تولد خلايا وتفني أخري وتظل خلايا أخري علي قيد الحياة وكل حساب يتبع إظهار للشكل الكلي يسمي "جيل" .

ويتولد عن تطبيق القواعد السابقة علي الأشكال الأولية الذي نضعها في البرنامج نتائج مختلفة , فبعض الأشكال تظل بلا تغيير (مستقرة) , بينما تتطور أشكال أخري بصورة ما .

وإليك صياغة البرنامج

Life.cpp

// life.cpp


// recreates Conway's "Game of Life"


// UCS Laboratories


 


#include <conio.h>              // for kbhit(), getch(), etc.


const unsigned char top =  '\xDF';  // square in upper half


const unsigned char bot =  '\xDC';  // square in lower half


const unsigned char both = '\xDB';  // square in both halves


const unsigned char none = '\x20';  // square in neither half


const int maxCols = 80;             // screen width


const int maxRows = 50;             // two squares per character


const int maxRealRows = 25;         // really only 25 rows


 


enum state { dead, alive };


 


class Cell


   {


   private:


      int xCo, yCo;                    // coordinates of cell


      state oldState, newState;        // dead or alive


   public:


      void InitCell(int, int);         // initialize cell


      void SetLive(void);              // to set initial pattern


      state GetState(void);            // to check neighbors


      void Calc(void);                 // calculate new state


      void Display(void);              // display the cell


   };


 


Cell c[maxCols][maxRows];              // define array of cells


 


void Cell::InitCell(int x, int y)      // initialize cell


   {


   xCo = x;                            // set coordinates


   yCo = y;


   oldState = newState = dead;         // set to dead


   }


 


void Cell::SetLive()                   // set cell to alive


   {


   newState = alive;


   }


 


state Cell::GetState(void)             // get state of cell


   {


   return(oldState);


   }


 


void Cell::Calc()                      // calculate new state


   {                      // find number of neighbors alive


   int neighbors =  c[xCo-1][yCo-1].GetState() +   // nw


            c[xCo  ][yCo-1].GetState() +   // n


            c[xCo+1][yCo-1].GetState() +   // ne


            c[xCo-1][yCo  ].GetState() +   // w


            c[xCo+1][yCo  ].GetState() +   // e


            c[xCo-1][yCo+1].GetState() +   // sw


            c[xCo  ][yCo+1].GetState() +   // s


            c[xCo+1][yCo+1].GetState();    // se


 


   newState = dead;       // cell is dead unless


              // it's alive and has 2 or 3 neighbors


   if( oldState==alive && (neighbors==2 || neighbors==3) )


      newState = alive;


              // or it's dead and has 3 neighbors


   else if( oldState==dead && neighbors==3 )


      newState = alive;


   }


 


// display the cell


// ( yCo goes from 0 to 49, screenRow from 0 to 24 )


void Cell::Display(void)


   {


   char chbuff[1];           // one-character buffer


   oldState = newState;      // update oldState


 


   if( newState==alive )     // if cell is alive, display it


      {


      int screenRow = yCo / 2;  // find actual row on screen


      int botrow = yCo % 2;     // top row = 0, bottom row = 1


                 // get screen character (two cells)


      gettext(xCo, screenRow, xCo, screenRow, chbuff);


      int ch = chbuff[0];


                 // find character (two cells)


      if(botrow)                   // if our cell on bottom row,


     if(ch==none || ch==bot)   // and if neither or bottom,


        ch = bot;              //    set bottom only


     else                      // if both or top,


        ch = both;             //    set both


      else                   // if our cell on top row,


     if(ch==none || ch==top)   // and if neither or top half,


        ch = top;              //    set top only


     else                      // if both or bottom halves,


        ch = both;             //    set bottom


                 // set new screen character


      gotoxy(xCo, screenRow);      // move cursor


      putch(ch);                   // insert character


      }


   }


 


main()


//UCS Laboratories


   {


   int x, y;


   clrscr();                         // clear screen


   _setcursortype(_NOCURSOR);        // turn off cursor


 


   for(x=0; x<maxCols; x++)          // initialize to dead


      for(y=0; y<maxRows; y++)


     c[x][y].InitCell(x, y);


 


   c[39][24].SetLive();              // set inverted U pattern


   c[40][24].SetLive();


   c[41][24].SetLive();


   c[39][25].SetLive();


   c[41][25].SetLive();


   c[39][26].SetLive();


   c[41][26].SetLive();


 


   for(x=0; x<maxCols; x++)          // display initial cells


      for(y=0; y<maxRows; y++)


     c[x][y].Display();


 


   getch();                          // wait for keypress


 


   while( !kbhit() )                 // continue until keypress


      {


      for(x=1; x<maxCols-1; x++)     // find new states


     for(y=1; y<maxRows-1; y++)


        c[x][y].Calc();


 


      clrscr();                      // erase screen


 


      for(x=0; x<maxCols; x++)       // display cells


     for(y=0; y<maxRows; y++)


        c[x][y].Display();


      }  // end while


   _setcursortype(_NORMALCURSOR);    // restore cursor


   }  // end main()


 






تشغيل البرنامج



بمجرد تشغيل البرنامج تري علي الشاشة الشكل الأولي , وهو علي شكل حرف U مقلوب (شكل ) . وبالضرب علي أي مفتاح يبدأ الشكل في التطور متشكلا في تشكيلات متعددة لعدة مئات من الأجيال إلي أن يستقر في شكل نهائي . ويبين الشكل تشكيلا بينيا .



شرح البرنامج



يتكون البرنامج من فئة Cell , ينشأ منها مصفوفة كائنات ذات بعدين , كل كائن يمثل خلية علي الشاشة . كل كائن يحتوي علي أربعة بيانات , الإحداثيان السيني والصادي , والحالة السابقة oldState والحالة الجديدة newState وكلا البيانين من النوع enum state وله حالتان , ميت dead وحي alive .



كما يتضمن الكائن الدوال التالية :



- دالة تمهيدية InitCell تجعل الحالتين القديمة والجديدة للخلية في الوضع dead وتستغل الدالة main() هذه الدالة لتمهيد كافة الخلايا علي الشاشة .



- دالة Setlive تضع الخلية في الوضع live .



- دالة GetState للحصول علي حالة الخلية .



- دالة Cale لحساب الحالة الجديدة للخلية في الجيل التالي , وتتضمن شروط وجود الخلية علي قيد الحياة .



- دالة Display لإظهار الخلية علي الشاشة .



طريقة الإظهار



ذكرنا أن كل موضع علي الشاشة يتسع لخليتين , واحدة فوق الأخري وعلي ذلك فالخليتان اللتان رقم صفهما (المعامل yCo) 24 , 25 يظهران علي الصف 12 في الشاشة الأولي فوقه , والثانية تحته .



وتجري دالة الإظهار الاختبار التالي لكي تعرف موضع الخلية علي الشاشة :



- تقسم رقم صف الخلية علي 2 فتوجد رقم الصف علي الشاشة (المتغير screenRow)



- تقسم رقم صف الخلية علي 2 بمؤثر باقي القسمة , فإذا كان باقي القسمة (المتغير botrow) = 0 (العدد زوجي ) دل ذلك علي أن الخلية تشغل الموضع فوق الصف وإذا كان فرديا كان موقعها تحته .



ولدينا أربعة احتمالات للمحرف الذي يعبر عن حالتي الخليتين معا :






















































مكان الخلية



حالة الخلية



المحرف



ثابت



1- فوق



تحت



حية



ميتة



'\xDF'



top



2- فوق



تحت



ميتة



حية



'\xDC'



bot



3- فوق



تحت



حية



حية



'\xDB'



both



4-فوق



تحت



ميتة



ميتة



'\x20'



None





 



وتبدأ دالة الإظهار عملها بتحويل حالة الخليتين من الحالة القديمة إلي الحالة الجديدة . فإذا كانت الحالة الجديدة ''alive'' , تجري اختبار تحديد موضعها , ثم تخزن حالتها علي الشاشة في مصفوفة من عنصر واحد باستخدام الدالة gettext() . هذا البيان يدخل في متغير محرفي ch , تخزن فيه الحالة الجديدة للخلية بناء علي الشروط المبينة في البرنامج وهي كالتالي :



إذا كانت الخلية سفلية (الشرط if bottom) وكانت الحالة السابقة none (الخلية لم تملأ بعد أي في بدء البرنامج ) أو المحرف الموجود في هذا الموضع bot (محرف الخلية السفلية) . فإن الموضع يملأ بالمحرف bot غير قائم لأن الخلية أصلا سفلية ) .



يطبق نفس الاختبار مع الخلية إذا كانت علوية , فمثلا بالمحرف top أو المحرف both .



يظهر المحرف الذي خزن في المتغير ch علي الشاشة باستخدام الامر : putch(); بعد تحريك المشيرة لهذا الموضع .



وبعد ملأ الشاشة بالشكل الابتدائي , والضرب علي أي مفتاح , تطبق عليه الدالة calc() علي كلا الخلايا لتحدد أيها يفني وأيها يستمر في البقاء وأين يكون الولادة لخلايا جديدة .



تمسح الشاشة بعد ذلك لإظهار الشكل الجديد .



وتستخدم الدالة main() في بدء البرنامج دالة لإخفاء المشيرة ودالة لإعادتها في نهاية البرنامج .

التسميات: