Играта Живот


Създадена е от математика Джон Конуей – специалист по алгебра, открил една от големите крайни групи.
Околната среда при неговата игра се състои от безкрайна, разделена на квадратни клетки равнина.
Всяка клетка може да бъде или жива или мъртва.
Изменението състоянието й в момент (t+1) се определя от състоянието на съседите й в момент t.
      Живот - Всяка клетка с два или три съседни живи клетки остава жива за следващото поколение.
      Смърт - Всяка клетка с четири или повече съседни умира от пренаселване. Всяка клетка без съседи или с единствен съсед загива от самота
      Зараждане - Всяка празна клетка с точно три съседни живи клетки - ни повече, ни по-малко - е родилна клетка. В нея на следващия ход (t+1) се "ражда" клетка. Важно е да се разбере, че всички раждания и умирания стават едновременно
Последния пасаж е цитиран дословно от страницата :       http://audio-spirit.com/~wania/Fractals/Pages/Game_Life.php
Препоръчвае още страницата       http://famlife.narod.ru/
Целта е да направим алгоритъм за тази игра.
Ще разберем как се работи с изображения (Image) и как се чертаят върху тях линии и правоъгълници.
Ще видим и как се "прихваща" съобщение на Операционната система на възникнало събитие.
Опитахме се да снабдим кода с подробни пояснения.
Дефинираме удобен за нас тип с обичайните член променливи за дължина и ширина.
type TWorld=record Height,Width:integer; cellsOld,cellsNew:Array [0..100,0..100] of boolean; end; Редно е типът да се дефинира преди дефиницията на формата. Първият индекс показва номера на реда а вторият на стълба. Клетките на нашия "Свят" са логически – "истина" ако клетката е жива и "лъжа" – ако е мъртва. "Светът" има две копия – старо и ново. В новото отразяваме възникналите промени и ги изобразяваме. В раздела за дефиниране на променливите на приложението (var), непосредствено под променливата за формата ( Form1: TForm1; ) декларираме нашата: ( World:TWorld; )

Изчертаване на мрежата

Поставяме контролите Бутон и Изображение във формата. Имената им са BttnGrid и Image1. При двойно натискане на бутона се появява празна процедура, която ще се изпълни при възникването на това събитие. Коментарите са с кръгли скоби, но знаем че трябва да ги заменим с фигурни ({ ,}) или с // ако са на един ред, отдясно на командите. Окончателният и вид е този: procedure TForm1.BttnGridClick(Sender: TObject); var i,j:integer; rctL:TRect; (правоъгълник) begin rctL.Top:=0; rctL.Left:=0; rctL.Right:=Image1.Width;rctL.Bottom:=image1.Height; (Сега правоъгълникът е с размерите на изображението) Image1.Canvas.Brush.Color:=clWhite; Image1.Canvas.FillRect(rctL); (Изчистваме изображението) World.Width:=round(Image1.Width/10);World.Height:=round(Image1.Height/10); (Ще чертаем хоризонтални и вертикални линии с отстъп 10 точки) for i:=1 to World.Width do begin Image1.Canvas.MoveTo(i*10,0); Image1.Canvas.LineTo(i*10,Image1.Height); (хоризонтална линия) end; for i:=1 to World.Height do begin Image1.Canvas.MoveTo(0,i*10); Image1.Canvas.LineTo(Image1.Width,i*10); (вертикална линия) end; for i:=1 to World.Height do for j:=1 to World.Width do begin World.cellsOld[i,j]:=false; World.cellsNew[i,j]:=false; (Изчистваме нашия Свят – стар и нов) end; Image1.Canvas.Brush.Color:=clBlack; (Ще рисуваме в черно.) end;
С натискането на бутона трябва да се появи разграфеното изображение.

Начално попълване на местата с живите клетки

Желаем ако натиснем левият бутон на мишката намирайки се върху клетка тя да стане черна и съответната клетка от стария свят-жива. С F11 задействаме инспектора (Object Inspector), преминаваме на Събитията (Еvents) и двойно натескаме при On Mouse Down Тази процедура прябва да изглежда така: procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); (X,Y – Координатите на мишката) var nX,nY:integer;rctL:TRect; (nX,nY – Координатите на клетката) begin nX:=round(int(x/10)); nY:=round(int(y/10)); World.cellsOld[nX,nY]:=true; (Тя е жива!) rctL.Left:=10*nx;rctL.Right:=rctL.Left+10; rctL.Top:=10*ny;rctL.Bottom:=rctL.Top+10; Image1.Canvas.Brush.Color:=clBlack; Image1.Canvas.FillRect(rctL); (Зачерняме я.) end;

Преминаване от стария свят към новия


Същността на алгоритъма е че трябва да преминем през всички клетки от стария свят и съобразно правилата да конструираме новия, да го изобразим и след това новия свят да заеме мястото на стария, готов за следващата стъпка. Всичко това ще се осъществява с бутон на име Bttn Step. procedure TForm1.BttnStepClick(Sender: TObject); var i,j,neib:integer; (neib – броят на съседите) begin (Изтриваме новия свят) for i:=0 to World.Height do for j:=0 to World.Width do World.cellsNew[i,j]:=false; for i:=0 to World.Height do begin for j:=0 to World.Width do begin neib:=0; if j-1 >= 0 then begin (левите съседи на клетката) if i+1 <= World.Height {дали сме най-отгоре?}then if World.cellsOld[i+1,j-1] then inc(neib); if World.cellsOld[i,j-1] then inc(neib); if i-1 >= 0 then if World.cellsOld[i-1,j-1] then inc(neib); end; if j+1 <= World.Width then begin (десните съседи на клетката) if i+1 <= World.Height then if World.cellsOld[i+1,j+1] then inc(neib); if World.cellsOld[i,j+1] then inc(neib); if i-1 >= 0 then if World.cellsOld[i-1,j+1] then inc(neib); end; //if naib>() if i-1 >= 0 then if World.cellsOld[i-1,j] then inc(neib); (горната и долната клетки) if i+1 <= World.Height then if World.cellsOld[i+1,j] then inc(neib); if (World.cellsOld[i,j]=true) and (neib<2) then World.cellsNew[i,j]:=false;(самотност) (запазване) if (World.cellsOld[i,j]=true) and (neib<=3) and (1 < neib) then World.cellsNew[i,j]:=true; (пренаселеност) if (World.cellsOld[i,j]=true) and (neib>3) then World.cellsNew[i,j]:=false; (раждане) if (World.cellsOld[i,j]=false) and (neib=3) then World.cellsNew[i,j]:=true; end; end; WorldShow(Image1); (Показване) end;

Изобразяването на състоянието не се нуждае от коментари

procedure WorldShow(ImageF:TImage); var i,j:integer; rctL:TRect; begin ImageF.Canvas.Brush.Color:=clWhite; for i:=0 to World.Width do for j:=0 to World.Height do begin rctL.Left:=10*i+1;rctL.Right:=rctL.Left+10-1; rctL.Top:=10*j+1;rctL.Bottom:=rctL.Top+10-1; ImageF.Canvas.FillRect(rctL); //Изтриваме клетката end; ImageF.Canvas.Brush.Color:=clBlack; for i:=0 to World.Width do begin for j:=0 to World.Height do begin if World.cellsNew[i,j]=false then continue; rctL.Left:=10*i;rctL.Right:=rctL.Left+10; rctL.Top:=10*j;rctL.Bottom:=rctL.Top+10; ImageF.Canvas.FillRect(rctL); end; end; for i:=0 to World.Width do for j:=0 to World.Height do World.cellsOld[i,j]:=World.cellsNew[i,j]; end;