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

Сейчас я предлагаю интерфейс взаимодействия КП и Delphi, вполне пригодный
для практического применения программистами, желающими писать логику
верхнего уровня своих программ по-русски.


Предлагаемый интерфейс взаимодействия КП и Delphi

Программа на Delphi вызывает обработчики событий из DLL на КП, которая,
в свою очередь, вызывает функции нижнего уровня из DLL на Delphi.

Схема интерфейса между КП и Delphi выглядит следующим образом:
Вложение:
kpdelphi.jpg [ 58.17 КБ | Просмотров: 11116 ]

Для реализации приводимого примера я использовал школьную сборку
BlackBox 1.5 (с целью русификации исходного кода на КП) и Delphi 4.
Написанные на Delphi 4 проекты columns и pasdll будут собираться и под
Delphi 2010 и выше, но чтобы предлагаемый интерфейс между КП и Delphi
работал, нужно чтобы columns и pasdll были собраны в одной версии Delphi.

Файл \i21edu\Rsrc\ru.odc  мне пришлось редактировать, хотя бы потому, что
там нет русских аналогов для цикла repeat until и для встроенных процедур
inc и dec.
Возможно, что использованные мной определения ЦИКЛ_ДО  КОНЕЦ_ЦИКЛА_ДО,
УВЕЛИЧИТЬ, УМЕНЬШИТЬ и не являются идеальными, но лучше что-то, чем вообще
ничего.

Исходя из общего определения ПЕРЕМЕННАЯ:ТИП типы переменных (за исключением
массивов) я решил писать в единственном числе мужского рода.

Привожу фрагмент модуля columnsKpdll (размер columnsKpdll примерно 15 кБ,
593 строки) с основной логикой игры columns, реализацию которой на Delphi
я написал для себя лет 10 тому назад, а сейчас как пример реализации
интерфейса между КП и Delphi решил перетащить ее на КП в слегка упрощенном
виде (без таблицы результатов), а заодно и русифицировать.
## Код:
```
ПРОЦЕДУРА Стакан;
  ПЕРЕМЕННЫЕ и:ЦЕЛЫЙ;
НАЧАЛО

    ширина_канвы := запрос(ШИРИНА_КАНВЫ);
    высота_канвы := запрос(ВЫСОТА_КАНВЫ);

    цвет_фона(0);
    прямоугольник(0, 0, ширина_канвы - 1, высота_канвы - 1);

    ЦИКЛ_ДЛЯ  и:=нач_у ДО кон_у +1 ДЕЛАТЬ
      Кубик(5, и, цТемнозеленый);
      Кубик(19, и, цТемнозеленый);
    КОНЕЦ_ЦИКЛА;

    ЦИКЛ_ДЛЯ  и:=7 ДО 19  ДЕЛАТЬ
      ЕСЛИ ((и ОСТАТОК 2) # 0) ТО   
            Кубик(и, кон_у+1, цТемнозеленый);
      КОНЕЦ;
    КОНЕЦ_ЦИКЛА;

    содержимое_стакана;
КОНЕЦ  Стакан;


ПРОЦЕДУРА одна_линия(нх,ну:ЦЕЛЫЙ; их,иу:ЛОГИЧЕСКИЙ);
   ПЕРЕМЕННЫЕ 
   х, у, х1, у1, н_х, н_у, кол_повт, 
   тек_цв, цв, сх, су:ЦЕЛЫЙ;
НАЧАЛО
  ЕСЛИ  МассивИгры[ну][нх] >= цФлагЦвета ТО
     тек_цв := МассивИгры[ну][нх] - цФлагЦвета;
  ИНАЧЕ
     тек_цв := МассивИгры[ну][нх];
  КОНЕЦ;
  кол_повт:=1;  н_х:=нх;  н_у:=ну;
  сх:=1;   ЕСЛИ  ~ их  ТО  сх:=0;  КОНЕЦ;
  су:=1;   ЕСЛИ  ~ иу  ТО  су:=0;  КОНЕЦ;
  х:=нх+сх;  у:=ну+су;
  ЦИКЛ_ДО
    ЕСЛИ  МассивИгры[у][х] >= цФлагЦвета ТО
       цв := МассивИгры[у][х] - цФлагЦвета;
    ИНАЧЕ
       цв := МассивИгры[у][х];
    КОНЕЦ;
    ЕСЛИ цв = тек_цв ТО УВЕЛИЧИТЬ(кол_повт);  КОНЕЦ;
    ЕСЛИ ( (цв # тек_цв) ИЛИ
         ((цв = тек_цв) & ((х=5) ИЛИ (у=разм))) ) &
          (кол_повт >= 3) & (тек_цв # 0) ТО

      х1:=н_х; у1:=н_у;
      ЦИКЛ_ПОКА (х1 # х) ИЛИ (у1 # у)  ДЕЛАТЬ
         ЕСЛИ МассивИгры[у1][х1] < цФлагЦвета  ТО
            МассивИгры[у1][х1]:=МассивИгры[у1][х1]+цФлагЦвета;
         КОНЕЦ;
        ЕСЛИ  их  ТО  УВЕЛИЧИТЬ(х1);  КОНЕЦ;
        ЕСЛИ  иу  ТО  УВЕЛИЧИТЬ(у1);  КОНЕЦ;
      КОНЕЦ_ЦИКЛА;
      ЕСЛИ цв = тек_цв  ТО
         ЕСЛИ МассивИгры[у][х] < цФлагЦвета ТО 
            МассивИгры[у][х]:= МассивИгры[у][х] + цФлагЦвета;
         КОНЕЦ;
      КОНЕЦ;

      кол_очков:=кол_очков + кол_повт - 2;
    КОНЕЦ;
    ЕСЛИ  цв # тек_цв  ТО
      кол_повт :=1;  тек_цв :=цв;  н_х :=х;  н_у :=у;  
    КОНЕЦ;

    ЕСЛИ  их  ТО  УВЕЛИЧИТЬ(х);  КОНЕЦ;
    ЕСЛИ  иу  ТО  УВЕЛИЧИТЬ(у);  КОНЕЦ;
  КОНЕЦ_ЦИКЛА_ДО  (х > 5) ИЛИ (у > разм);
КОНЕЦ  одна_линия;


ПРОЦЕДУРА  подсчет_очков;
   ПЕРЕМЕННЫЕ и, кол_очков_1, кол_очков_2:ЦЕЛЫЙ;

    ПРОЦЕДУРА  отражение_стакана;
    ПЕРЕМЕННЫЕ и,д:ЦЕЛЫЙ;
    НАЧАЛО
      ЦИКЛ_ДЛЯ и:=1 ДО разм ДЕЛАТЬ
        д:=МассивИгры[и][0];
        МассивИгры[и][0]:=МассивИгры[и][5];
        МассивИгры[и][5]:=д;

        д:=МассивИгры[и][1];
        МассивИгры[и][1]:=МассивИгры[и][4];
        МассивИгры[и][4]:=д;

        д:=МассивИгры[и][2];
        МассивИгры[и][2]:=МассивИгры[и][3];
        МассивИгры[и][3]:=д;
      КОНЕЦ_ЦИКЛА;
    КОНЕЦ отражение_стакана;

    ПРОЦЕДУРА  диагонали;
       ПЕРЕМЕННЫЕ х,у:ЦЕЛЫЙ;
    НАЧАЛО  (* диагонали *)    
      (* возрастающие диагонали *)
      ЦИКЛ_ДЛЯ х:=0 ДО 3 ДЕЛАТЬ       
         одна_линия(х,1,ДА,ДА);   КОНЕЦ_ЦИКЛА;
      ЦИКЛ_ДЛЯ у:=2 ДО разм-2 ДЕЛАТЬ  
         одна_линия(0,у,ДА,ДА);   КОНЕЦ_ЦИКЛА;
      (* убывающие диагонали *)
      отражение_стакана;
      ЦИКЛ_ДЛЯ х:=0 ДО 3 ДЕЛАТЬ       
         одна_линия(х,1,ДА,ДА);   КОНЕЦ_ЦИКЛА;
      ЦИКЛ_ДЛЯ у:=2 ДО разм-2 ДЕЛАТЬ  
         одна_линия(0,у,ДА,ДА);   КОНЕЦ_ЦИКЛА;
      отражение_стакана;
    КОНЕЦ диагонали;

    ПРОЦЕДУРА  вертикали;
    ПЕРЕМЕННЫЕ х:ЦЕЛЫЙ;
    НАЧАЛО
      ЦИКЛ_ДЛЯ х:=0 ДО 5 ДЕЛАТЬ  
         одна_линия(х,1,НЕТ,ДА);  КОНЕЦ_ЦИКЛА;
    КОНЕЦ вертикали;

    ПРОЦЕДУРА  горизонтали;
    ПЕРЕМЕННЫЕ у:ЦЕЛЫЙ;
    НАЧАЛО
      ЦИКЛ_ДЛЯ у:=1 ДО разм ДЕЛАТЬ  
         одна_линия(0,у,ДА,НЕТ);  КОНЕЦ_ЦИКЛА;
    КОНЕЦ горизонтали;

    ПРОЦЕДУРА  удаление_блоков;
    ПЕРЕМЕННЫЕ и,ж,макс,см,у:ЦЕЛЫЙ;
    НАЧАЛО
      ЦИКЛ_ДЛЯ и:=0 ДО 5 ДЕЛАТЬ
        у:=1; см:=0;  макс:=мх_мигр[и];
        ЦИКЛ_ПОКА (у < макс) ДЕЛАТЬ
          ЕСЛИ  МассивИгры[у][и] >= цФлагЦвета  ТО
            ЦИКЛ_ДЛЯ  ж:=у+1 ДО макс-1 ДЕЛАТЬ
               МассивИгры[ж-1][и]:=МассивИгры[ж][и];
            КОНЕЦ_ЦИКЛА;
            МассивИгры[макс-1][и]:=0;  УМЕНЬШИТЬ(макс);

          ИНАЧЕ  УВЕЛИЧИТЬ(у);
          КОНЕЦ;
        КОНЕЦ_ЦИКЛА;

        см:=мх_мигр[и]-макс;
        ЕСЛИ  см # 0  ТО
          у:=мх_мигр[и]-1;
          ЦИКЛ_ДЛЯ  ж:=мхту[и]+1 ДО кон_у ДЕЛАТЬ 
            (* вывод столбца *)
            Кубик(хст[и], ж, МассивИгры[у][и]);
            УМЕНЬШИТЬ(у);
          КОНЕЦ_ЦИКЛА;
          УМЕНЬШИТЬ(мх_мигр[и],см);  УВЕЛИЧИТЬ(мхту[и],см);
        КОНЕЦ;
      КОНЕЦ_ЦИКЛА;
    КОНЕЦ удаление_блоков;

НАЧАЛО

  кол_очков_2:=кол_очков;
  ЦИКЛ_ДО
    кол_очков_1:=кол_очков;
    разм:=мх_мигр[0];
    ЦИКЛ_ДЛЯ  и:=1 ДО 5 ДЕЛАТЬ  
       ЕСЛИ  мх_мигр[и] > разм  ТО  разм:=мх_мигр[и]; КОНЕЦ;
    КОНЕЦ_ЦИКЛА;
    УМЕНЬШИТЬ(разм);
    ЕСЛИ разм > 2 ТО  диагонали;  вертикали;  КОНЕЦ;
    горизонтали;
    ЕСЛИ кол_очков_1 # кол_очков ТО удаление_блоков; КОНЕЦ;
  КОНЕЦ_ЦИКЛА_ДО  кол_очков_1 = кол_очков;

  ЕСЛИ  кол_очков_2 # кол_очков  ТО
     
    данные(ЧИСЛО_ОЧКОВ, кол_очков);

  КОНЕЦ;

КОНЕЦ подсчет_очков;
```

Полные исходники проектов columns, kpdll и pasdll, а также
отредактированный файл ru.odc находятся в архиве columns.rar.


Предлагаемый способ программирования для среды BlackBox имеет, на
мой взгляд, следующие достоинства:

1) облегчается достижимость полной русификации исходного кода на КП по
сравнению с автономным BlackBox, т.к. сейчас типы и процедуры стандартных
модулей BlackBox имеют английские названия;

2) Вывод графики в Delphi проще, чем в BlackBox;

3) при отладке программы в Delphi появляется возможность использования
отладчика, пусть и не в пошаговом режиме.

Хотя в педагогическом смысле отладчик использовать вредно, но при
практическом программировании использование отладчика иногда помогает
отладить программу значительно быстрее, чем без него (особенно в состоянии
"смотришь в книгу - видишь фигу", иногда и такое бывает).
Для отладки программы в модуле u_columns задаем процедуру отладки
следующим образом:
## Код:
```
type  t_otladka   = procedure(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10:integer);
type
  Tf_column = class(TForm)

  ...
  public:
     otladka: t_otladka;
  end;


var qq:integer;

procedure _otladka(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10:integer);
begin
   if (a1 <> -1) then begin

      ...

   end else begin
      qq := 1;  (* вызываем из FormCreate, чтобы можно было ставить
                   в этой процедуре точки останова *)
   end;
end;


В FormCreate:

   otladka := _otladka;

   otladka(-1,0,0,0,0,0,0,0,0,0);


В модуле u_pasdll задаем экспортируемую процедуру:

procedure  _otladka(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10:integer);
begin
   f1.otladka(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10);
end;


В файле kpdll.txt:

ТИП
ПРОЦЕДУРА_10ПАР_ЦЕЛЫЙ = ПРОЦЕДУРА(п1,п2,п3,п4,п5,
                                  п6,п7,п8,п9,п10:ЦЕЛЫЙ);

ПЕРЕМЕННЫЕ
отладка:           ПРОЦЕДУРА_10ПАР_ЦЕЛЫЙ;

При вызове pasdll из kpdll:

      отладка := S.VAL(  ПРОЦЕДУРА_10ПАР_ЦЕЛЫЙ,
             WinApi.GetProcAddress( dll, "_otladka" ) );   

После чего можно отлаживаться, например:

      ЦИКЛ_ПОКА (х1 # х) ИЛИ (у1 # у)  ДЕЛАТЬ
         ЕСЛИ МассивИгры[у1][х1] < цФлагЦвета  ТО
            МассивИгры[у1][х1]:=МассивИгры[у1][х1]+цФлагЦвета;
         КОНЕЦ;

         отладка(100, МассивИгры[у1][х1], у1, х1, 0,0,0,0,0,0);

         ЕСЛИ  их  ТО  УВЕЛИЧИТЬ(х1);  КОНЕЦ;
         ЕСЛИ  иу  ТО  УВЕЛИЧИТЬ(у1);  КОНЕЦ;
      КОНЕЦ_ЦИКЛА;
      ЕСЛИ цв = тек_цв  ТО
         ЕСЛИ МассивИгры[у][х] < цФлагЦвета ТО 
            МассивИгры[у][х]:= МассивИгры[у][х] + цФлагЦвета;
         КОНЕЦ;

         отладка(101, МассивИгры[у][х], у, х, 0,0,0,0,0,0);
      КОНЕЦ;
```


Архив с файлами columns.rar :
Вложение:columns.rar[65.14 КБ]
Скачиваний: 675
