Алгоритмическая игра «Жизнь» на Паскале — снимок экрана

Алгоритмическая игра «Жизнь» на Паскале

Примеры

Представляю Вашему вниманию пример реализации алгоритмической игры «Жизнь» на языке Паскаль. Игра «Жизнь», она же так называемый клеточный автомат, обязана своим появлением английскому математику Джону Конвейю.

Для начала — относительно простой, но одновременно структурированный вариант текста программы (на Free Pascal в самом простом и совместимом исполнении):

program Life;

uses
  ptcGraph, ptcCrt;

const
  HorSize = 640;
  VerSize = 480;

type
  TMatrix = array [0..VerSize+1, 0..HorSize+1] of Byte;

var
  GraphDriver :Integer;
  GraphMode :Integer;

  NewArr, OldArr :TMatrix;

  { Процедура очистки массива (заполнения нулями) }
  procedure ClearMatrix(var Matrix :TMatrix; VSize, HSize :Integer);
  var
    i, j :Integer;
  begin
    for i := 0 to VSize+1 do
      for j := 0 to HSize+1 do
        Matrix[i, j] := 0;
  end;

  { Процедура "вставки" в массив некоторого прямоугольника из единиц }
  procedure InitMatrix(var Matrix :TMatrix;
                           RVpos, RHPos, RVSize, RHSize :Integer);
  var
    i, j :Integer;
  begin
    for i := 1 to RVSize do
      for j := 1 to RHSize do
        Matrix[RVPos+i-1, RHPos+j-1] := 1;
  end;

  { Процедура "заворачивания" границ массива для эмуляции "бесконечного" поля }
  procedure InfiMatrixEdges(var Matrix :TMatrix; VSize, HSize :Integer);
  var
    i :Integer;
  begin
    Matrix[0, 0] := Matrix[VSize, HSize];
    Matrix[VSize+1, HSize+1] := Matrix[1, 1];
    Matrix[0, HSize+1] := Matrix[VSize, 1];
    Matrix[VSize+1, 0] := Matrix[1, HSize];

    for i := 1 to VSize do
      begin
        Matrix[i, 0] := Matrix[i, HSize];
        Matrix[i, HSize+1] := Matrix[i, 1];
      end;

    for i := 1 to HSize do
      begin
        Matrix[0, i] := Matrix[VSize, i];
        Matrix[VSize+1, i] := Matrix[1, i];
      end;
  end;

  { Процедура реализации игровой логики }
  procedure GameLogic(var Source, Dest :TMatrix; VSize, HSize :Integer);
  var
    i, j :Integer;
    around :Byte;
  begin
    for i := 1 to VSize do
      for j := 1 to HSize do
        begin
          around := Source[i-1, j-1] + Source[i-1, j] + Source[i-1, j+1] +
                    Source[i, j-1] + Source[i, j+1] +
                    Source[i+1, j-1] + Source[i+1, j] + Source[i+1, j+1];

          case Source[i, j] of
            0: if around = 3 then
                 Dest[i, j] := 1
               else
                 Dest[i, j] := 0;
            1: if (around < 2) or (around > 3) then
                 Dest[i, j] := 0
               else
                 Dest[i, j] := 1;
          end;
        end;
  end;

  { Процедура "визуализации" - отрисовывает игровое поле на экран }
  procedure DrawMatrix(var Matrix :TMatrix; VSize, HSize :Integer;
                                            Color, Background :Integer);
  var
    i, j :Integer;
  begin
    for i := 1 to VSize do
      for j := 1 to HSize do
        case Matrix[i, j] of
          0: PutPixel(j-1, i-1, Background);
          1: PutPixel(j-1, i-1, Color);
        end;
  end;

begin

  ClearMatrix(NewArr, VerSize, HorSize);
  ClearMatrix(OldArr, VerSize, HorSize);

  InitMatrix(OldArr, 140, 220, 200, 200);

  { DetectGraph(GraphDriver, GraphMode);
  WriteLn('Driver: ', GraphDriver, ', Mode: ', GraphMode); }

  GraphDriver := VGA;
  GraphMode := VGAHi;
  InitGraph(GraphDriver, GraphMode, '');

  repeat

    GameLogic(OldArr, NewArr, VerSize, HorSize);
    InfiMatrixEdges(NewArr, VerSize, HorSize);

    OldArr := NewArr;

    { Delay(100); }

    DrawMatrix(NewArr, VerSize, HorSize, 15, 0);

  until KeyPressed;

  CloseGraph;

end.

Пояснения по тексту программы ниже, а сейчас — немного о сути игры.

В нашем распоряжении есть игровое поле — двумерный массив ячеек (клеток). Каждая такая ячейка может находиться в одном из двух возможных состояний: «живом» и «неживом». Скажем, пусть состоянию «жив» соответствует численное значение, равное 1, а состоянию «мертв» — численное значение, равное 0. Для образности каждую «живую» ячейку мы можем называть, скажем, бактерией.

В ходе действия игрового алгоритма над игровым полем мы выполняем некоторые манипуляции, результат которых графически отображаем на экране.

Суть игрового алгоритма такова: по очереди «пересматриваем» все клетки и подсчитываем количество «живых соседей» каждой клетки. В зависимости от количества «живых соседей» меняем состояние текущей клетки — «возрождаем» (создаем) нашу «бактерию» либо «убиваем» её. Вот варианты условия:

  • если вокруг пустой («мертвой») находятся ровно три непустые («живые») клетки, то данная клетка «оживает» (либо, другими словами, «появляется» — кому как больше нравится);
  • состояние непустой («живой») клетки при наличии двух или трех непустых же («живых») соседей не изменяется (клетка продолжает «существовать»);
  • если у непустой («живой») клетки меньше двух или больше трех «живых» соседей, то данная клетка «умирает» (перестает «существовать»).

Задав выборочно начальные условия (разместив на игровом поле то или иное количество «живых» клеток) и запустив игровой алгоритм, можно наблюдать за развитием «жизни» в пределах игрового поля — изменением конфигурации и количества «бактерий», их движением. Процесс и результат могут быть довольно любопытными.

И немного по тексту программы-примера. Для реализации игрового поля будем использовать двумерный массив элементов типа Byte, в цикле перебирая все его ячейки (по строкам и столбцам). «Живой» ячейкой будет элемент со значением «1», «мертвой» — со значением «0». Результат работы алгоритма, что естественно, будем помещать в другой идентичного типа двумерный массив, который и отображается графически. После отработки игрового алгоритма в очередном «шаге» первый массив будем обновлять, копируя в него содержимое из второго.

Чтобы сделать игровое поле «бесконечным» (и избавиться от «краевых» эффектов), мы «заворачиваем» края двумерного массива. Для этого добавляем к каждому краю двумерного массива по лишнему элементу, куда копируем значение элемента с противоположенного края. Соответственно, в данном случае каждая ячейка с обычным индексом в массиве «видит» полный набор «соседей» и алгоритм работает корректно (пусть и в «завернутом» пространстве игрового поля).

В тексте программы названия переменных и процедур/функций выбраны таким образом, чтобы можно было максимально быстро понять, для чего они нужны. Например, HorSize и VerSize — задают горизонтальный и вертикальный размеры игрового поля соответственно. Поскольку для реализации применен Free Pascal, графика выполнена с использованием модулей ptcGraph и ptcCrt (последний — для считывания клавиатуры). Но версия Паскаля здесь принципиального значения не имеет — программу легко адаптировать под любой диалект, немного изменится лишь реализация графики.

Скачать текст программы и её модификации с «улучшенной» графикой Вы можете здесь: zip-архив.

Добавить комментарий

Ваш адрес email не будет опубликован.