Сделаем тип растения и модель мира.
Данная модель покажет нам жизненный цикл растения. Растение не может передвигаться. Каждые несколько итераций оно порождает рядом с собой новый потомок-клон с теми же самыми параметрами. После определенного числа итераций растение умирает от старости.
Также для демонстрации расположим несколько камней. Растение не может занимать ту же клетку, что и камень. Посмотрим, как заполнится игровой мир. Насладимся зрелищем пиршества растительной жизни!
Модуль u001_Stone.pas оставим на месте. Создадим новый модуль u002_Jungle.pas.
Наследуем новый класс TLifelessObject.
interface
type
TPlant = class (TLifelessObjectChessed)
private
FReactionPeriod: TCountIteration;
public
procedure Iterate; override;
constructor Create(AOwner: TComponent); override;
end;
Чтобы моделирование было более правдоподобным, введем следующее правило. Объект может выполнять какие-то действия не на каждой итерации, а через каждые несколько итераций. Чем больше количество пропусков, тем медленнее реакция у существа. Наоборот, чем меньше количество пропусков, тем более быстро реагирует и действует существо. Также более правдоподобным будет взаимодействие объектов мужду собой (которое мы сделаем позднее). Пока одно существо "думает" или "поворачивается", другое может успеть его съесть.
Для этого введем в класс TPlant (пока только в него, для отработки алгоритма) поле FReactionPeriod. В конструкторе мы каждому растению присваиваем ему значение периода реакции случайным образом из определенного диапазона.
У каждого объекта есть поле FAge (возраст). Он инкрементируется в начале каждой итерации.
У каждого объекта есть поле FLimitLifeExpectancy (ограничение продолжительности жизни). Значение 0 задает неограниченный срок жизни.
У мира (объекта TGamingWorldChessed) имеется метод
procedure KillObject(aObject: TMIRObject);
который уничтожает данный объект. Он выбрасывает его из игрового поля и в нем самом проставляет признак FIsCorps. После выполнения Iterate у всех объектов в Iterate мира все трупы выбрасываются из списка существ FObjectList класса TEnvironmentListObj (Ни рая, ни ада не существуют! Наши существа пропадают в небытие! Ха-ха-ха!).
В методе Iterate класса TPlant мы проверяем возраст, и если он больше задаваемой продолжительности жизни, растение умирает. Если нет, мы анализируем, прошло ли итераций столько, сколько период реакции и проводим изменения.
Изменения для данного случая такие. Ищем по соседству в произвольном направлении свободную клетку и засаживаем туда растение. Моделируем, как будто оно дало потомка. В данной модели мы не наследуем какие- либо свойства растения-родителя, не занимаемся генетикой и прочим.
implementation
uses
u001_Stone, uMIRUtils;
{ TPlant }
constructor TPlant.Create(AOwner: TComponent);
begin
inherited;
FPaintedAs := paSimpleFigure;
FPaintedAsFigure := acfSquare;
FColor := clGreen;
FReactionPeriod := 15 + Random(10);
FLimitLifeExpectancy := (1 + Random(3)) * FReactionPeriod + Random(3);
end;
procedure TPlant.Iterate;
var
gw: TGamingWorldChessed;
sg: TRandomIntSequenceGenerator;
dir: TDirection;
begin
Inherited;
gw := (GamingWorld as TGamingWorldChessed);
if
(FAge <> 0)
and(FAge > FLimitLifeExpectancy)
then begin
gw.KillObject(Self);
Exit;
end;
if (FAge mod FReactionPeriod) = 0 then begin
sg := TRandomIntSequenceGenerator.Create;
try
sg.Init(gw.COUNT_DIRECTION);
dir := TDirection(sg.GetNextItem);
while
gw.ChessOccupee(Row, Col, dir)
and (sg.Step < sg.SizeSequence)
do begin
dir := TDirection(sg.GetNextItem);
end;
if not (sg.Step = sg.SizeSequence) then begin
gw.PlaceObject(TPlant.Create(Self), Row, Col, dir);
end;
finally
sg.Free;
end;
end;
end;
Для этого воспользуемся вспомогательным классом TRandomSequenceGenerator, которому задаем количество чисел элементов (они нумеруются от нуля). При каждом вызове GetNextItem он выдает следующее случайное число из оставшихся. То есть мы задали восемь направлений. Выбираем случайное направление. Если оно занято, мы снова вызываем GetNextItem и нам выдается случайное направление из того, что осталось.
Наследуем новый мир и задаем метод GenerateObject
{ TJungleWorld }
procedure TJungleWorld.GenerateObjects;
var
count: Integer;
obj: TLifelessObjectChessed;
res: Boolean;
attemps: integer;
row, col: TLineDim;
begin
TStone.GamingWorld := Self;
TPlant.GamingWorld := Self;
//камни
count := 0;
while count < 10 do begin
obj := TStone.Create(Self);
res := False;
attemps := 0;
while (not res) and (attemps < 20) do begin
res := PlaceObject(obj, GetRanfomRow(), GetRandomCol());
Inc(attemps);
Inc(count);
end;
end;
//Растения
count := 0;
while count < 30 do begin
obj := TPlant.Create(Self);
res := False;
attemps := 0;
while (not res) and (attemps < 20) do begin
res := PlaceObject(obj, GetRanfomRow(), GetRandomCol());
Inc(attemps);
Inc(count);
end;
end;
inherited;
end;
Случайным образом разбрасываем двадцать камней и двадцать растений.
Самым главным для данной модели является алгоритм размножения: каждое растение после определенного количества итераций высажывает новое растение на соседнюю свободную клетку в случайном направлении. После определенного количеста итераций растение умирает.
Прогон модели показывает, как ареал растений распространяется от каждого растения равномерно в разные стороны. См. 002_Jungle_flowering.exe
Если продолжительность жизни слишком мала (случайно от 0 до 3 и менее периодов реакции), размножение не происходит (см. модель 002_Jungle_extincion.exe). Популяция начинает развиваться, если продолжительность более, чем случайно от 1 до 4.
Далее: Влияние периода реакции и продолжительности жизни на выживаемость
Ранее: Создание простого объекта