برنامج الحياة في ++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() في بدء البرنامج دالة لإخفاء المشيرة ودالة لإعادتها في نهاية البرنامج .
تعليقات
إرسال تعليق