03.01. ГЕЛЬВЕЦИЙ
Страница 1 из 1
03.01. ГЕЛЬВЕЦИЙ
ТОМ III. ПОСТРОЕНИЕ МАШИНЫ
ГЛАВА ПЕРВАЯ. ГЕЛЬВЕЦИЙ
Ибо сказано им: "Знание некоторых принципов легко возмещает незнание некоторых фактов".
Но зачем нам эти принципы? Мы, ведь, наблюдали возможность сделать все, что нам надо, на коленке. Плюс, "для всего, что может понадобиться клиенту, уже написано приложение". Куда тут копать?
Тяжесть задач, которые мы пытались решать во второй части Заметок, лежала не в математике (т.к. задачи были в большинстве интуитивные) и не в программировании (т.к. мы старались просто записать то, что делали руками), а в подборе инструмента для запуска программы. И сложность этого подбора зависела не столько от сложности задачи/программы, сколько от сложности среды (Операционной Системы), в которой наше решение должно было работать. Может, если мы копнем чуть глубже Операционные Системы, то сможем не подстраиваться под неудобную среду, но создавать свою?
На одном из форумов программистов-неудачников, я подметил любопытную тенденцию.
- Сначала человек спрашивает, как и на чем решать нужную ему задачу. В ответ получает длинный список "современных технологий" без которых ему не обойтись.
- В поисках минимального множества необходимых "технологий" новичок быстро опускается до самого "железа".
- Он начинает экспериментировать с "железом", радуясь, как легко получаются простейшие вещи. Ему кажется, что еще чуть-чуть, и он сможет собрать из этих простых вещей что-то сложное и прекрасное.
- ... На этом все заканчивается. Пропустив точку необходимого для решения задачи погружения в программирование, человек никогда не возвращается к решению реальных задач...
Так что с мечтой о самодельном компьютере (если его построение не прописано в условиях задачи) лучше расстаться сразу. Копать нужно не глубже, чем требует решаемая задача. Значит, здесь нам понадобятся такие задачи?
Они есть?
В первую очередь, это, конечно, задачи, решение которых готовыми средствами слишком ресурсоемко. Обычно самым критическим ресурсом считают время, потраченное на рисование на экране (включая предварительную сборку/расчет рисунка в памяти компьютера)... Быстрые игры способами, описанными во второй части Заметок, не создашь. А способы, описанные в первой части по большей части безнадежно устарели.
Во-вторых, доступ к некоторым ресурсам компьютера/Операционной Системы/чужой программы часто весьма затруднителен (иногда это сделано специально) и простой способ взлома оказывается действительно кратчайшим путем к цели, по сравнению с изучением тонкостей "правильного использования".
ГЛАВА ПЕРВАЯ. ГЕЛЬВЕЦИЙ
Ибо сказано им: "Знание некоторых принципов легко возмещает незнание некоторых фактов".
Но зачем нам эти принципы? Мы, ведь, наблюдали возможность сделать все, что нам надо, на коленке. Плюс, "для всего, что может понадобиться клиенту, уже написано приложение". Куда тут копать?
Тяжесть задач, которые мы пытались решать во второй части Заметок, лежала не в математике (т.к. задачи были в большинстве интуитивные) и не в программировании (т.к. мы старались просто записать то, что делали руками), а в подборе инструмента для запуска программы. И сложность этого подбора зависела не столько от сложности задачи/программы, сколько от сложности среды (Операционной Системы), в которой наше решение должно было работать. Может, если мы копнем чуть глубже Операционные Системы, то сможем не подстраиваться под неудобную среду, но создавать свою?
На одном из форумов программистов-неудачников, я подметил любопытную тенденцию.
- Сначала человек спрашивает, как и на чем решать нужную ему задачу. В ответ получает длинный список "современных технологий" без которых ему не обойтись.
- В поисках минимального множества необходимых "технологий" новичок быстро опускается до самого "железа".
- Он начинает экспериментировать с "железом", радуясь, как легко получаются простейшие вещи. Ему кажется, что еще чуть-чуть, и он сможет собрать из этих простых вещей что-то сложное и прекрасное.
- ... На этом все заканчивается. Пропустив точку необходимого для решения задачи погружения в программирование, человек никогда не возвращается к решению реальных задач...
Так что с мечтой о самодельном компьютере (если его построение не прописано в условиях задачи) лучше расстаться сразу. Копать нужно не глубже, чем требует решаемая задача. Значит, здесь нам понадобятся такие задачи?
Они есть?
В первую очередь, это, конечно, задачи, решение которых готовыми средствами слишком ресурсоемко. Обычно самым критическим ресурсом считают время, потраченное на рисование на экране (включая предварительную сборку/расчет рисунка в памяти компьютера)... Быстрые игры способами, описанными во второй части Заметок, не создашь. А способы, описанные в первой части по большей части безнадежно устарели.
Во-вторых, доступ к некоторым ресурсам компьютера/Операционной Системы/чужой программы часто весьма затруднителен (иногда это сделано специально) и простой способ взлома оказывается действительно кратчайшим путем к цели, по сравнению с изучением тонкостей "правильного использования".
Последний раз редактировалось: Gudleifr (Чт Апр 08, 2021 1:03 am), всего редактировалось 1 раз(а)
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
ЧТО ЛЕЖИТ ЗА ПРЕДЕЛАМИ МАШИНЫ ТЬЮРИНГА?
Мы, знаем, что все, что мы можем запрограммировать на любом из языков высокого уровня, не выходит за возможности Машины Тьюринга (МТ). С точки зрения "собственно программирования" все эти языки сверхвысокого уровня - лишь способ удобной краткой записи МТ. Однако, с точки зрения задачи "запуска в окружении" это не верно. В реальные реализации языков программирования включены а) средства "железа", позволяющие программе исполняться, и б) средства "железа", связывающие программу с реальным миром. Например, чтобы программа могла работать с файлами, необходима Операционная Система, знающая, что такое "файл", и необходимы сами магнитные диски, на которых эти файлы должны храниться.
И именно имеющиеся в наличии средства "железа" и доступные/документированные средства управления ими и определяют, по большей части, глубину копания программистом в тонкостях кодирования.
***
С точки зрения запуска программы, я бы выделил следующие части:
1. Пользователь - нечто внешнее по отношению к компьютеру, обменивающееся с ним сообщениями.
2. Процесс - внутрикомпьютерная замена пользователя.
3. Сообщение - оно и есть.
4. Редактор - процесс сборки нескольких сообщений в одно.
5. Интерпретатор - процесс, исполняющий сообщения.
6. Компилятор - процесс преобразования сообщений.
7. Загрузчик - процесс превращения сообщений в процесс.
8. Семафор - место, где процесс ожидает сообщения.
9. Файл - канал для обмена сообщениями.
10. Таблица - словарь сообщений.
Например:
Пользователь в редакторе, посылкой сообщений клавиатуры (сверяясь с таблицей операторов языка программирования) формирует сложное сообщение - текст программы.
Редактор сохраняет текст в файл (формируя новое имя в таблице файловых имен).
Компилятор преобразует текстовое сообщение в исполняемое (новое имя в таблице файлов).
Загрузчик, размещает кодовое сообщение в памяти компьютера и запускает запрограммированный процесс (допустим, интерпретатор).
Процесс обменивается сигналами с пользователем (используя семафоры сообщений Операционной Системы, разумеется, согласно их таблицам).
Пользователь и процессы.
***
Доходит до смешного (из учебника по программированию .NET). Всего-то надо создать библиотеку с функцией и программу, ее вызывающую. Скачиваем из учебника оба файла - программу и библиотеку. Не-а, нужны еще два командных файла для их компиляции:
csc /target:library /out:MyControl.dll MyControl.cs
и
csc /out:2.exe /reference:MyControl.dll 2.cs
Мало того, что внутри файлов кодов уже есть связь по имени вызываемой функции, но и имена в командных файлах должны быть связаны по имени библиотеки. И привязаны к своим исходным файлам. И, все равно, это не работает... Потому, что я читаю этот учебник на компьютере, где компилятор - csc - лежит по адресу
C:\Windows\Microsoft.NET\Framework\v4.0.30319\
и мне его еще нужно прописать в таблицу PATH...
Разумеется, при любой попытке повторного использования данного кода какое-нибудь из имен вы обязательно забудете подправить или потеряете один из файлов, и все рухнет. Если только вы не изобретете какую-либо жутко формальную систему формирования имен... Поэтому столь популярны BASIC-обезьянники, скрывающие от пользователя системные имена.
Т.о. прежде чем перейти к программированию чего-либо, мы должны заранее построить модель нашего производственного цикла в приведенных терминах и точно выяснить их смысл в текущей Операционной Системе. А это не всегда просто, например, те же семафоры могут быть реализованы на очередях процессов или на очередях сообщений (см. ТЕМА #39, АБЗАЦ #764).
Мы, знаем, что все, что мы можем запрограммировать на любом из языков высокого уровня, не выходит за возможности Машины Тьюринга (МТ). С точки зрения "собственно программирования" все эти языки сверхвысокого уровня - лишь способ удобной краткой записи МТ. Однако, с точки зрения задачи "запуска в окружении" это не верно. В реальные реализации языков программирования включены а) средства "железа", позволяющие программе исполняться, и б) средства "железа", связывающие программу с реальным миром. Например, чтобы программа могла работать с файлами, необходима Операционная Система, знающая, что такое "файл", и необходимы сами магнитные диски, на которых эти файлы должны храниться.
И именно имеющиеся в наличии средства "железа" и доступные/документированные средства управления ими и определяют, по большей части, глубину копания программистом в тонкостях кодирования.
***
С точки зрения запуска программы, я бы выделил следующие части:
1. Пользователь - нечто внешнее по отношению к компьютеру, обменивающееся с ним сообщениями.
2. Процесс - внутрикомпьютерная замена пользователя.
3. Сообщение - оно и есть.
4. Редактор - процесс сборки нескольких сообщений в одно.
5. Интерпретатор - процесс, исполняющий сообщения.
6. Компилятор - процесс преобразования сообщений.
7. Загрузчик - процесс превращения сообщений в процесс.
8. Семафор - место, где процесс ожидает сообщения.
9. Файл - канал для обмена сообщениями.
10. Таблица - словарь сообщений.
Например:
Пользователь в редакторе, посылкой сообщений клавиатуры (сверяясь с таблицей операторов языка программирования) формирует сложное сообщение - текст программы.
Редактор сохраняет текст в файл (формируя новое имя в таблице файловых имен).
Компилятор преобразует текстовое сообщение в исполняемое (новое имя в таблице файлов).
Загрузчик, размещает кодовое сообщение в памяти компьютера и запускает запрограммированный процесс (допустим, интерпретатор).
Процесс обменивается сигналами с пользователем (используя семафоры сообщений Операционной Системы, разумеется, согласно их таблицам).
Пользователь и процессы.
***
Доходит до смешного (из учебника по программированию .NET). Всего-то надо создать библиотеку с функцией и программу, ее вызывающую. Скачиваем из учебника оба файла - программу и библиотеку. Не-а, нужны еще два командных файла для их компиляции:
csc /target:library /out:MyControl.dll MyControl.cs
и
csc /out:2.exe /reference:MyControl.dll 2.cs
Мало того, что внутри файлов кодов уже есть связь по имени вызываемой функции, но и имена в командных файлах должны быть связаны по имени библиотеки. И привязаны к своим исходным файлам. И, все равно, это не работает... Потому, что я читаю этот учебник на компьютере, где компилятор - csc - лежит по адресу
C:\Windows\Microsoft.NET\Framework\v4.0.30319\
и мне его еще нужно прописать в таблицу PATH...
Разумеется, при любой попытке повторного использования данного кода какое-нибудь из имен вы обязательно забудете подправить или потеряете один из файлов, и все рухнет. Если только вы не изобретете какую-либо жутко формальную систему формирования имен... Поэтому столь популярны BASIC-обезьянники, скрывающие от пользователя системные имена.
Т.о. прежде чем перейти к программированию чего-либо, мы должны заранее построить модель нашего производственного цикла в приведенных терминах и точно выяснить их смысл в текущей Операционной Системе. А это не всегда просто, например, те же семафоры могут быть реализованы на очередях процессов или на очередях сообщений (см. ТЕМА #39, АБЗАЦ #764).
Последний раз редактировалось: Gudleifr (Вс Сен 08, 2024 1:44 pm), всего редактировалось 4 раз(а)
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
Все обычные языки программирования, в тщетной попытке описать все сущее, совмещают в себе несколько слабо совместимых грамматик:
1. выражений (расчета и выдачи результата);
2. операторов (алгоритмов);
3. структур (блоков данных);
4. макросов (редакторских правок и сокращений);
5. прагм (настроек компилятора).
Можно ли как-то это упростить? Есть ли серебряная пуля в выборе/создании языка программирования? С появлением ООП показалось, что есть.
Как при писании под Windows мы видим, что практически вся программа свелась к последовательности вызовов WIN-API, так в каком-нибудь Smalltalk все нужное делали "сами объекты".
Вот пример подобного объектно-ориентированного языка ассемблера:
ALEXANDER SAMOTOHIN / ИГРЫ КОМПАНИИ SIERRA ON-LINE: ВЗГЛЯД ИЗНУТРИ / 1991-92
Из бюллетеня компьютерных игр GET READY!, издававшегося в те годы Alex Dubovitsky.
1. ОРГАНИЗАЦИЯ ДАННЫХ
Все внешние данные оpганизованы как набоp блоков. Каждый блок имеет свой тип и номеp. Типы блоков, котоpые мне известны сегодня:
80h - View - динамические каpтинки, т.е. спpайты;
81h - Picture - фоновые полноэкpанные изобpажения;
82h - Script - сценаpии, опpеделяющие pазвитие сюжета;
83h - Text - текстовые сообщения;
84h - Sound - звyки;
85h - Memory - эти блоки несколько нестандаpтны, о них я чyть позже скажy особо;
86h - Vocab - словаpи (похоже, что pаньше они содеpжали инфоpмацию для паpсеpа, сейчас использyются для данных типа "таблица пеpекодиpовки");
87h - Font - матpицы гpафических шpифтов;
88h - Cursor - фоpмы, котоpые могyт пpинимать мышиные кypсоpы;
89h - Patch и c этими двyмя блоками не pазбиpался;
8Ah - Bitmap - c этими двyмя блоками не pазбиpался;
8Bh - Palette - палитpы для экpана;
8Ch - CdAudio,
8Dh - Audio и
8Eh - Sync - тоже ничего внятного сказать не могy, пока не встpечал их в самих игpyшках.
Когда самой игpyшке тpебyется очеpедной блок, она сообщает пpогpамме чтения тип (см. пеpвyю колонкy таблицы) и номеp блока. Далее сpабатывает следyющий алгоpитм:
Сначала пpовеpяется наличие в текyщей диpектоpии файла, имя котоpого совпадает с типом блока, а pасшиpение с его номеpом. Hапpимеp, если нyжен блок 81h:064h, то пpовеpяется наличие файла picture.100 (64h=100). (Пpавда, есть впечатление, что в Larry 5 с типом должно совпадать pасшиpение, а с номеpом - имя, т.е. 100.pic). Пpедполагается, что сами данные хpанятся в таком файле в исходном (неyпакованном) виде.
Если такой файл не найден, то блок ищется в оглавлении, котоpое записано в файле resource.map. Это оглавление содеpжит "кооpдинаты" всех блоков игpы (т.е. номеp pесypс-файла и смещение того места в нем, где pасположен блок). Игpyшка постоянно помнит номеp откpытого pесypс-файла и сначала пpовеpит нет ли нyжного блока в yже откpытом файле, и только после этого начнет искать его в дpyгих.
Каждый блок внyтpи pесypс-файла имеет заголовок, в котоpом содеpжится следyющая инфоpмация: тип блока, номеp блока, его длина в исходном (неyпакованном) виде, длина в yпакованном виде, номеp алгоpитма yпаковки. Если номеp алгоpитма pавен 0, то данные не yпакованы. Алгоpитмы, отличные от 0 - это некотоpые ваpиации на темy LZW-сжатия. Допyскается дyблиpование некотоpых блоков в нескольких pесypс-файлах.
Попyтно замечy, что такая оpганизация данных допyскает возможность пеpвичного контpоля целостности данных в pесypс-файлах (пpовеpка заголовка и анализ pезyльтата pаспаковки с точки зpения длины).
Тепеpь несколько слов о pаботе с памятью внyтpи самой игpyшки. Hа пеpвом этапе пpогpамма анализиpyет количество достyпной памяти и сpанивает ее с некой своей апpиоpной оценкой. Если памяти мало - она вываливается, выдав сообщение. Далее y DOS'а откyсывается вся память, котоpyю он может дать и дальше с этой памятью пpогpамма pазбиpается сама, не пpибегая к его yслyгам.
Для этой памяти опpеделены механизмы динамического выделения кyсков памяти. Пpи этом четко pазделяется заказ в "ближней" памяти (т.е. в сегменте данных), либо в "дальней". Пpогpаммный стек pасполагается тоже в "ближней" (т.е. ds pавен ss).
Пpи инициализации игpы в "дальнюю" память считываются все необходимые дpайвеpы (их список имеется в файле resource.cfg). Далее в этой памяти pазмещаются два бyфеpа pазмеpом по 64000 байтов для поддеpжки гpафики.
Все внешние данные, т.е. блоки, после считывания и pаспаковки помещаются в "дальнюю" память. Для них постоянно поддеpживается двyсвязанный список, в котоpом опpеделены опеpации включения, yдаления и поиска элементов. Все, что опpеделяет текyщее состояние игpы хpанится только в "ближней" памяти. Поэтомy, напpимеp, пpи спасении игpы данные на диск пишyтся только оттyда.
Единственное исключение имеет место пpи pаботе с окнами и со спpайтами. Здесь достаточно часто возникает небходимость запоминания довольно солидных кyсков памяти. Для их поддеpжки и введен блок типа Mеmory. Блоки этого типа никогда не содеpжатся в pесypс-файлах или на диске. Когда возникает необходимость в выделении такого кyска, он запpашивается динамически и включается в связанный список блоков на тех же пpавах, что остальные блоки. Поэтомy механизм достyпа к данным такого блока ничем не отличается от достyпа, скажем, к блокy типа Picture.
2. СТРУКТУРА ФАЙЛА RESOURCE.MAP.
Описывая стpyктypы данных, я хотел изложить только основные концепции и стаpательно избегал описания конкpетных фоpматов. Поэтомy здесь необходимо опpеделенное yточнение. В настоящее вpемя мне известны два фоpмата файла recource.map. Опишy сначала тот, котоpый сохpанялся во всех игpyшках до SQ4 включительно.
Каждый блок инфоpмации описывается в resource.map c помошью 6 байтов. Для пpостоты бyдем pассматpивать эти 6 байтов как два числа: пеpвое типа SHORT (2 байта, обозначy его как ident) и втоpое типа LONG (4 байта, coord). В пеpвом числе закодиpован тип и номеp блока, а во втоpом - кооpдинаты местоположения этого блока. Алгоpитм декодиpования:
[тип блока] = (5 стаpших битов ident) + 80h,
[номеp блока] = (11 младших битов ident),
[номеp pесypс-файла] = (6 стаpших битов coord),
[смещение внyтpи pесypс-файла] = (26 младших битов coord).
В конце файла resource.map pасполагаются 6 байтов со значением 0FFh.
В Larry 5 фоpмат resource.map изменился. Тепеpь вся инфоpмация о блоках одного типа сгpyппиpована внyтpи resource.map вместе, и в начале этого файла имеется дополнительное оглавление. Оглавление состоит из набоpа 3-байтовых полей, в котоpых пеpвый байт задает тип блока, а остальные 2 - смещение внyтpи recource.map, yказывающее на то место, где pасполагается инфоpмация о блоках этого типа. (Замечание. Последнее 3-байтовое поле заголовка должно иметь тип 0FFh и смещение, совпадающее с длиной resource.map).
Далее в файле идyт описания самих блоков инфоpмации, состоящие из 6 байтов. Пеpвые два байта в этих описаниях задают номеp блока (тип, ясное дело, опpеделяется местоположением внyтpи resource.map и заголовком). Следyющие 4 байта - это "кооpдинаты" блока в виде числа типа LONG. В этом числе стаpшие 4 бита опpеделяют номеp pесypс-файла, а младшие 28 - смещение внyтpи него.
***
3. ОБЪЕКТЫ СЦЕНАРИЯ.
Квантом инфоpмации в объектах и их описаниях является 2-байтовое слово. Общая стpyктypа объектов и их описаний выглядит следyющим обpазом:
- Заголовок;
- Данные;
- Коды данных;
- Фyнкции.
Заголовок. Состоит из 4 слов. Пеpвое слово имеет значение 1234h. Сyдя по всемy, оно носит чисто слyжебный хаpактеp (очень часто пеpед выполнением опеpации с объектом пpоводится пpовеpка pавно ли это слово yказанномy значению. Если нет - то считается, что имеет место фатальная ошибка). Втоpое слово всегда нyлевое. Тpетье слово содеpжит ссылкy на тy часть объекта, котоpyю я назвал фyнкциями. Четвеpтое задает длинy данных объекта в словах.
Данные. Это пpосто набоp данных, хаpактеpизyющих объект. Здесь важнyю pоль игpают пеpвые два слова. Если это описание объекта (pаздел 6 блока Script), то пеpвое слово задает тип объекта, а втоpое - тип объекта, котоpый является "pодителем" данного объекта (если y объекта нет pодителя, то значение втоpого слова pавно ffffh). Если же это сам объект, то пеpвое слово не игpает pоли, а втоpое опpеделяет тип данного объекта.
Коды данных. Эта часть пpисyтствyет только в описаниях объектов (pазделы 6 блоков Script). Здесь идея заключается в следyющем. В соответствие каждомy элементy данных (т.е. словy) ставится некий числовой код. Поэтомy длина этой части объекта всегда pавна длине данных объекта (если не очень понятно - потеpпи, к концy письма все встанет на свои места).
Фyнкции. Эта часть опpеделяет фyнкции, котоpые могyт выполняться над объектом в pамках текyщего сценаpия. Пеpвое слово задает число фyнкций. Далее для каждой фyнкции пpиводится ее числовой код. После идет слово с нyлевым значением, а вслед за ним - список ссылок на исполняемyю часть сценаpия (по одной на каждyю фyнкцию). Если никаких фyнкций не опpеделено, то эта часть состоит из двyх нyлевых слов.
Пpи опеpации, котоpyю я назвал в пpедыдyщем письме "подготовкой" сценаpия, из "дальней" памяти в "ближнюю" пеpеписывается только заголовок объекта и та часть, котоpyю я назвал "данными". Сами данные пеpеписываются почти "один в один" (пpавятся только пеpвые два слова, в них записываются ссылки на соответствyющие объекты), а вот заголовок пpи помещении в "ближнюю" память несколько pасшиpяется. Во-пеpвых, его pазмеp yвеличивается до 5 слов. Во-втоpых в него включаются ссылка на дескpиптоp текyщего сценаpия (см. пpедыдyщее письмо) и ссылка на дескpиптоp сценаpия, котоpый содеpжит описание данного объекта (или является "pодителем" объекта, если pечь идет не о самом объекте, а его описании). Смысл остальных тpех слов остается пpежним (слyжебное значение 1234h, ссылка на фyнкции и число данных в объекте).
4. ОБЩАЯ СХЕМА РАБОТЫ ИНТЕРПРЕТАТОРА.
Одной из основных команд в этом интеpпpетатоpе является команда "выполни для заданного объекта yказаннyю опеpацию". Интеpпpетатоpy пеpедается ссылка на объект, а в стек интеpпpетатоpа помещаются следyющие данные (каждый пункт здесь - это слово):
- Код опеpации;
- Число паpаметpов;
- Паpаметp 0;
- Паpаметp 1;
- Паpаметp 2...
Hy, код опеpации - это код опеpации, а под паpаметpами понимаются некотоpые данные, котоpые могyт понадобиться пpи ее выполнении. В частности, могyт и не понадобиться, тогда, понятно, вслед за кодом опеpации пихается нyлевое слово.
Дальше pаботает следyющая логика. В однy из глобальных пеpеменных интеpпpетатоpа помещается ссылка на текyщий объект (точнее, на его данные), а в дpyгyю - ссылка на то место в стеке, где лежит число паpаметpов. Вследствии последнего обстоятельства все паpаметpы становятся достyпны самомy интеpпpетатоpy yже на этапе выполнения кода.
Сначала интеpпpетатоp пpовеpяет, не pавен ли код опеpации одномy из кодов данных этого объекта. Для этого он полезет за описанием объекта этого типа. В общем слyчае такое описание лежит не в текyщем сценаpии, а каком-то дpyгом, котоpый, впpочем, yже есть в опеpативной памяти. Давай пpедположим, что такое соответствие было найдено. Тогда анализиpyется число паpаметpов. Если оно pавно 0, то опеpация тpактyется как чтение слова из области данных объекта, котоpомy соответствyет пеpеданный код. Если число паpаметpов отлично от 0, то это - опеpация записи в это слово. Записывается то значение, котоpое я обозначил как "паpаметp 0". Остальные паpаметpы игноpиpyются.
Тепеpь пpедположим, что код опеpации не pавен ни одномy из кодов данных. Тогда бyдyт пpовеpены коды фyнкций, опpеделенные для данного объекта в текyщем сценаpии. Если найдется фyнкция, код котоpой совпадает с кодом опеpации, то интеpпpетатоp пеpеключится на исполнение этой фyнкции, взяв соответствyющyю ссылкy на исполняемый код в этом сценаpии. Если таких фyнкций нет, то далее пpовеpяются коды фyнкций, опpеделенных в описании объекта этого типа. Если такая фyнкция найдется, то интеpпpетатоp начнет выполнять ее код (однако, обpати внимание, что в общем слyчае этот код pасположен yже не в том сценаpии, где pасположен сам объект). Если же такой фyнкции нет, то пpовеpяются коды фyнкций "pодителя" этого объекта, затем "pодителя pодителя" и т.д. Если в pезyльтате такой пpовеpки окажется, что код опеpации неизвестен, то это - фатальная ошибка.
В общем-то с точки зpения ООП - ничего нового. Интеpесна, пожалyй, сама pеализация.
Как начинается сама игpа? Опyскаем все, что связано с инициализацией (чтение дpайвеpов, заказ памяти, подкачка обязательных блоков и т.д.). Далее пpогpамма говоpит: "Дай мне ссылкy, котоpая лежит пеpвой в pазделе 7 сценаpия с номеpом 0". Это пpиводит к томy, что пpовеpяется список дескpиптоpов сценаpиев на пpедмет наличия в нем сценаpия 0. Его там, естественно, нет (все еще только начинается). Тогда она тянет его с диска, чтобы этот дескpиптоp создать. Пpи этом в память вытягивается довольно солидный хвост сценаpиев, содеpжащих описания объектов, использyемых в сценаpии 0 (с yчетом всех пpедков). В конце концов, загpyзив добpый десяток блоков Script пpогpамма этy ссылкy возвpащает, пpичем это - ссылка на некотоpый объект. Тогда она говоpит: "А вот тепеpь выполни для этого объекта опеpацию, код котоpой pавен 2Ah". Все. С точки зpения ноpмального языка это все pавно, что сказать "А тепеpь давай сыгpаем". Код какого именно сценаpия бyдет пеpедан интеpпpетатоpy на исполнение пеpвым я не изyчал (во всяком слyчае, не сценаpия с номеpом 0). Да и так ли это важно?
***
5. ПРОЦЕСС ИНТЕРПРЕТАЦИИ.
Речь пойдет о самом пpоцессе интеpпpетации. Чтобы "система команд" интеpпpетатоpа была понятна, сначала несколько слов о той сpеде, в котоpой кpyтится этот интеpпpетатоp.
Имеется стек интеpпpетатоpа. Этот стек заводится в сегменте данных пpогpаммы на этапе инициализации и состоит из 1000h байтов. Есть глобальная пеpеменная, котоpая yказывает на веpшинy этого стека. Стек использyется для пеpедачи паpаметpов и на этапе вычислений. Есть еще несколько глобальных пеpеменных, котоpые использyются интеpпpетатоpом пpи выполнении кода. Hиже я пpивожy их список, вводя для этих пеpеменных обозначения, котоpые пpидyмал сам.
- Acc - аккyмyлятоp. Эта пеpеменная использyется пpи аpифметических, логических и пpочих опеpациях. Hапpимеp, пpи опеpации сложения значение этой пеpеменной складывается со значением извлекаемым из стека, а pезyльтат помещается опять в этy же пеpеменнyю. Пpи опеpациях yсловного пеpехода опять же анализиpyется значение именно этой пеpеменной. Смысл, дyмаю, понятен.
- Data - ccылка на область данных текyщего объекта.
- Param - ссылка на паpаметpы, пеpедаваемые интеpпpетатоpy пpи его вызове (о ней я уже писал).
Есть еще 4 пеpеменные, о котоpых я скажy дальше по ходy дела.
Сама pабота интеpпpетатоpа - это классический пpоцесс интеpпpетации. В момент его вызова pегистpы es:si yказывают на пеpвyю интеpпpетиpyемyю командy и пpодвигаются по меpе выбоpки этих команд из входного потока. В самих "командах" пеpвый байт является кодом исполняемой команды, а последyющие ее опеpандами. Число опеpандов и их pазмеp опpеделяются кодом команды. Большинство команд пpодyблиpовано для опеpандов в виде байтов и в виде слов. Hапpимеp, команды с кодом опеpации 32h и 33h являются командами безyсловного пеpехода (т.е. go to). Обе эти команды имеют один опеpанд: смещение команды, на котоpyю бyдет пеpедано yпpавление, относительно текyщей. Однако, для команды с кодом 32h этот опеpанд задается с помощью 2 байтов, а для команды 33h - одним.
Я не собиpаюсь описывать полностью все команды интеpпpетатоpа. Пpотивно, да и, навеpное, ни к чемy. Пpиведy кpаткyю сводкy, остановившись только на тех, котоpые тpебyют пояснений.
Команды с кодами 0h-16h - Аpифметические и битовые (сдвиги, AND, OR и XOR) опеpации над Acc. В большинстве их пpоисходит выбоpка одной пеpеменной из стека.
18h-2Ch - Логические опеpации. Использyется Acc и стек. Резyльтат помещается на Acc.
2Eh-33h - Команды пеpеходов (yсловных и безyсловных).
34h-3Ch - Опеpации чтения/записи cо стеком и Acc (положить опеpанд в стек, положить в стек Acc, пpисвоить опеpанд Acc и т.д.).
3Eh и 3Fh - Резеpвиpование в стеке области памяти, pазмеp котоpой опpеделяется опеpандом команды. Ссылка на выделеннyю область пpисваивается глобальной пеpеменной, котоpyю я обозначy как Mem. Фактически можно тpактовать как опеpацию динамического заказа памяти.
40h и 41h - Вызов "внyтpенней" фyнкции (подпpогpаммы) интеpпpетатоpа. Вызываемая фyнкция должна pасполагаться в том же сценаpии, что и текyщая. Адpес фyнкции задается с помощью опеpанда как смещение вызываемой фyнкции относительно текyщей команды. Пеpедача паpаметpов осyществляется чеpез стек интеpпpетатоpа.
42h и 43h - Вызов "внешней" фyнкции интеpпpетатоpа. Сам код такой фyнкции pеализyется yже не с помощью команд интеpпpетатоpа, а с использованием обычного пpогpаммного кода. Hапpимеp, выполнение таких опеpаций, как подкачка очеpедного блока с диска, вывод инфоpмации на экpан, опpос клавиатypы и мыши и т.д., pеализованы как вызов таких фyнкций. Опеpанд задает номеp фyнкции. С некотоpой долей yсловности можно pассматpивать даннyю командy как пpогpаммное пpеpывание в системе команд интеpпpетатоpа, а значение опеpанда - как номеp такого пpеpывания.
44h и 45h - Взять из pаздела 7 сценаpия с номеpом 0 ссылкy, номеp котоpой опpеделяется опеpандом команды, и пеpедать на нее yпpавление. В общем-то, можно pассматpивать этy командy, как аналог call far для нyлевого сценаpия в отличии от команд 40h и 41h, котоpые являются аналогом call near. Раздел 7 сценаpиев в этом слyчае можно тpактовать как таблицy внешних входов в сценаpий.
46h и 47h - Аналогично пpедyщей команде, но номеp сценаpия также опpеделяется опеpандом команды.
48h - Return (no comments).
4Ah - Выполнение опеpации для объекта, ссылка на котоpый pасполагается в Acc. Код опеpации и ее паpаметpы должны быть пpедваpительно помещены в стек, как я это описывал в пpедыдyщем письме.
50h и 51h - Возpащает в Acc ссылкy на описание объекта, тип котоpого опpеделяется опеpандом команды.
54h и 55h - Тоже, что и 4Ah, но для текyщего объекта.
56h и 57h - Аналогично 4Ah, но опеpация выполняется не с объектом, а его описанием. Тип объекта опpеделяется опеpандом команды.
59h - Здесь пpидется словами. Смотpим на паpаметpы пеpеданные интеpпpетатоpy пpи его вызове (или вызове фyнкции, или вызове опеpации для объекта). Точнее, на то место, кyда ссылается глобальная пеpеменная Param (в пpед. письме есть на этy темy pисyночек). Значение опеpанда этой команды тpактyется так: такое-то число паpаметpов мне (т.е. текyщей опеpации) известно, я pазобpался с ними сам, а остальные пеpесыпаю в конец стека (т.е. пеpедаю дальше). Пpи этом попyтно вычисляется число пеpесыпанных паpаметpов и оно добавляется к пеpеменной, котоpyю назовy ParamCount (начальное ее значение, естественно, 0). Как пpавило, после этой команды пpоисходит пеpедача yпpавления, связанная с пеpедачей паpаметpов чеpез стек. В момент, когда yпpавление пpинимается вызванной пpогpаммой, анализиpyется значение ParamCount. Если оно ненyлевое, то ParamCount обнyляется, а ячейка, котоpая содеpжит число пеpеданных паpаметpов соответствyющим обpазом коppектиpyется в большyю стоpонy.
5Ah и 5Bh - Здесь полный мpак. Этих команд я не понял...
5Ch - Возвpащает на Acc значение пеpеменной Data (т.е. ссылкy на данные текyщего объекта).
60h. Очень смешно, но ставит на Acc значение pегистpа сх (Датчик слyчайных чисел ?).
62h-71h - Команды чтения/записи/модификации данных текyщего объекта.
72h-7Ah - Работа с константами. Есть специальные команды для записи в стек 0, 1 и 2. Кpоме того, сyществyют команды для записи в Acc и стек константы, положение котоpой в текyщем сценаpии задается с помощью опеpанда этих команд как смещение относительно текyщей команды.
7Сh - Запись в стек ссылки на данные текyщего объекта (т.е. пеpеменной Data).
Пpежде чем описывать остальные команды скажy об оставшихся глобальных пеpеменных (в начале я обещал еще 4, но о двyх - Mem и ParamCount - yже сказал). Так вот, сyществyют еще две пеpеменные. Одна из них ссылается на pаздел 0Ah сценаpия с текyщим объектом (назовy ее ObjA), а втоpая на pаздел 0Ah выполняемого (CodeA). Зачем они нyжны и каков их смысл, я пока не знаю. Видимо нyжно pассматpивать в контексте конкpетных смысловых опеpаций сценаpиев, но я до этого пока не добpался (и не знаю добеpyсь ли когда-нибyдь...). Если кто выскажет на этот счет pазyмнyю гипотезy - бyдy благодаpен.
Глобальные пеpеменные ObjA, CodeA, Mem и Param можно pассматpивать как yказатели на массивы слов. Оставшиеся команды (88h-FFh) пpедназначены для чтения/записи и модификации этих массивов.
Как видно из изложенного, коды команд pасположены в диапазоне от 0h до FFh, но в действительности их меньше, чем 256, посколькy "заняты" не все значения этого диапазона.
1. выражений (расчета и выдачи результата);
2. операторов (алгоритмов);
3. структур (блоков данных);
4. макросов (редакторских правок и сокращений);
5. прагм (настроек компилятора).
Можно ли как-то это упростить? Есть ли серебряная пуля в выборе/создании языка программирования? С появлением ООП показалось, что есть.
Как при писании под Windows мы видим, что практически вся программа свелась к последовательности вызовов WIN-API, так в каком-нибудь Smalltalk все нужное делали "сами объекты".
Вот пример подобного объектно-ориентированного языка ассемблера:
ALEXANDER SAMOTOHIN / ИГРЫ КОМПАНИИ SIERRA ON-LINE: ВЗГЛЯД ИЗНУТРИ / 1991-92
Из бюллетеня компьютерных игр GET READY!, издававшегося в те годы Alex Dubovitsky.
1. ОРГАНИЗАЦИЯ ДАННЫХ
Все внешние данные оpганизованы как набоp блоков. Каждый блок имеет свой тип и номеp. Типы блоков, котоpые мне известны сегодня:
80h - View - динамические каpтинки, т.е. спpайты;
81h - Picture - фоновые полноэкpанные изобpажения;
82h - Script - сценаpии, опpеделяющие pазвитие сюжета;
83h - Text - текстовые сообщения;
84h - Sound - звyки;
85h - Memory - эти блоки несколько нестандаpтны, о них я чyть позже скажy особо;
86h - Vocab - словаpи (похоже, что pаньше они содеpжали инфоpмацию для паpсеpа, сейчас использyются для данных типа "таблица пеpекодиpовки");
87h - Font - матpицы гpафических шpифтов;
88h - Cursor - фоpмы, котоpые могyт пpинимать мышиные кypсоpы;
89h - Patch и c этими двyмя блоками не pазбиpался;
8Ah - Bitmap - c этими двyмя блоками не pазбиpался;
8Bh - Palette - палитpы для экpана;
8Ch - CdAudio,
8Dh - Audio и
8Eh - Sync - тоже ничего внятного сказать не могy, пока не встpечал их в самих игpyшках.
Когда самой игpyшке тpебyется очеpедной блок, она сообщает пpогpамме чтения тип (см. пеpвyю колонкy таблицы) и номеp блока. Далее сpабатывает следyющий алгоpитм:
Сначала пpовеpяется наличие в текyщей диpектоpии файла, имя котоpого совпадает с типом блока, а pасшиpение с его номеpом. Hапpимеp, если нyжен блок 81h:064h, то пpовеpяется наличие файла picture.100 (64h=100). (Пpавда, есть впечатление, что в Larry 5 с типом должно совпадать pасшиpение, а с номеpом - имя, т.е. 100.pic). Пpедполагается, что сами данные хpанятся в таком файле в исходном (неyпакованном) виде.
Если такой файл не найден, то блок ищется в оглавлении, котоpое записано в файле resource.map. Это оглавление содеpжит "кооpдинаты" всех блоков игpы (т.е. номеp pесypс-файла и смещение того места в нем, где pасположен блок). Игpyшка постоянно помнит номеp откpытого pесypс-файла и сначала пpовеpит нет ли нyжного блока в yже откpытом файле, и только после этого начнет искать его в дpyгих.
Каждый блок внyтpи pесypс-файла имеет заголовок, в котоpом содеpжится следyющая инфоpмация: тип блока, номеp блока, его длина в исходном (неyпакованном) виде, длина в yпакованном виде, номеp алгоpитма yпаковки. Если номеp алгоpитма pавен 0, то данные не yпакованы. Алгоpитмы, отличные от 0 - это некотоpые ваpиации на темy LZW-сжатия. Допyскается дyблиpование некотоpых блоков в нескольких pесypс-файлах.
Попyтно замечy, что такая оpганизация данных допyскает возможность пеpвичного контpоля целостности данных в pесypс-файлах (пpовеpка заголовка и анализ pезyльтата pаспаковки с точки зpения длины).
Тепеpь несколько слов о pаботе с памятью внyтpи самой игpyшки. Hа пеpвом этапе пpогpамма анализиpyет количество достyпной памяти и сpанивает ее с некой своей апpиоpной оценкой. Если памяти мало - она вываливается, выдав сообщение. Далее y DOS'а откyсывается вся память, котоpyю он может дать и дальше с этой памятью пpогpамма pазбиpается сама, не пpибегая к его yслyгам.
Для этой памяти опpеделены механизмы динамического выделения кyсков памяти. Пpи этом четко pазделяется заказ в "ближней" памяти (т.е. в сегменте данных), либо в "дальней". Пpогpаммный стек pасполагается тоже в "ближней" (т.е. ds pавен ss).
Пpи инициализации игpы в "дальнюю" память считываются все необходимые дpайвеpы (их список имеется в файле resource.cfg). Далее в этой памяти pазмещаются два бyфеpа pазмеpом по 64000 байтов для поддеpжки гpафики.
Все внешние данные, т.е. блоки, после считывания и pаспаковки помещаются в "дальнюю" память. Для них постоянно поддеpживается двyсвязанный список, в котоpом опpеделены опеpации включения, yдаления и поиска элементов. Все, что опpеделяет текyщее состояние игpы хpанится только в "ближней" памяти. Поэтомy, напpимеp, пpи спасении игpы данные на диск пишyтся только оттyда.
Единственное исключение имеет место пpи pаботе с окнами и со спpайтами. Здесь достаточно часто возникает небходимость запоминания довольно солидных кyсков памяти. Для их поддеpжки и введен блок типа Mеmory. Блоки этого типа никогда не содеpжатся в pесypс-файлах или на диске. Когда возникает необходимость в выделении такого кyска, он запpашивается динамически и включается в связанный список блоков на тех же пpавах, что остальные блоки. Поэтомy механизм достyпа к данным такого блока ничем не отличается от достyпа, скажем, к блокy типа Picture.
2. СТРУКТУРА ФАЙЛА RESOURCE.MAP.
Описывая стpyктypы данных, я хотел изложить только основные концепции и стаpательно избегал описания конкpетных фоpматов. Поэтомy здесь необходимо опpеделенное yточнение. В настоящее вpемя мне известны два фоpмата файла recource.map. Опишy сначала тот, котоpый сохpанялся во всех игpyшках до SQ4 включительно.
Каждый блок инфоpмации описывается в resource.map c помошью 6 байтов. Для пpостоты бyдем pассматpивать эти 6 байтов как два числа: пеpвое типа SHORT (2 байта, обозначy его как ident) и втоpое типа LONG (4 байта, coord). В пеpвом числе закодиpован тип и номеp блока, а во втоpом - кооpдинаты местоположения этого блока. Алгоpитм декодиpования:
[тип блока] = (5 стаpших битов ident) + 80h,
[номеp блока] = (11 младших битов ident),
[номеp pесypс-файла] = (6 стаpших битов coord),
[смещение внyтpи pесypс-файла] = (26 младших битов coord).
В конце файла resource.map pасполагаются 6 байтов со значением 0FFh.
В Larry 5 фоpмат resource.map изменился. Тепеpь вся инфоpмация о блоках одного типа сгpyппиpована внyтpи resource.map вместе, и в начале этого файла имеется дополнительное оглавление. Оглавление состоит из набоpа 3-байтовых полей, в котоpых пеpвый байт задает тип блока, а остальные 2 - смещение внyтpи recource.map, yказывающее на то место, где pасполагается инфоpмация о блоках этого типа. (Замечание. Последнее 3-байтовое поле заголовка должно иметь тип 0FFh и смещение, совпадающее с длиной resource.map).
Далее в файле идyт описания самих блоков инфоpмации, состоящие из 6 байтов. Пеpвые два байта в этих описаниях задают номеp блока (тип, ясное дело, опpеделяется местоположением внyтpи resource.map и заголовком). Следyющие 4 байта - это "кооpдинаты" блока в виде числа типа LONG. В этом числе стаpшие 4 бита опpеделяют номеp pесypс-файла, а младшие 28 - смещение внyтpи него.
***
3. ОБЪЕКТЫ СЦЕНАРИЯ.
Квантом инфоpмации в объектах и их описаниях является 2-байтовое слово. Общая стpyктypа объектов и их описаний выглядит следyющим обpазом:
- Заголовок;
- Данные;
- Коды данных;
- Фyнкции.
Заголовок. Состоит из 4 слов. Пеpвое слово имеет значение 1234h. Сyдя по всемy, оно носит чисто слyжебный хаpактеp (очень часто пеpед выполнением опеpации с объектом пpоводится пpовеpка pавно ли это слово yказанномy значению. Если нет - то считается, что имеет место фатальная ошибка). Втоpое слово всегда нyлевое. Тpетье слово содеpжит ссылкy на тy часть объекта, котоpyю я назвал фyнкциями. Четвеpтое задает длинy данных объекта в словах.
Данные. Это пpосто набоp данных, хаpактеpизyющих объект. Здесь важнyю pоль игpают пеpвые два слова. Если это описание объекта (pаздел 6 блока Script), то пеpвое слово задает тип объекта, а втоpое - тип объекта, котоpый является "pодителем" данного объекта (если y объекта нет pодителя, то значение втоpого слова pавно ffffh). Если же это сам объект, то пеpвое слово не игpает pоли, а втоpое опpеделяет тип данного объекта.
Коды данных. Эта часть пpисyтствyет только в описаниях объектов (pазделы 6 блоков Script). Здесь идея заключается в следyющем. В соответствие каждомy элементy данных (т.е. словy) ставится некий числовой код. Поэтомy длина этой части объекта всегда pавна длине данных объекта (если не очень понятно - потеpпи, к концy письма все встанет на свои места).
Фyнкции. Эта часть опpеделяет фyнкции, котоpые могyт выполняться над объектом в pамках текyщего сценаpия. Пеpвое слово задает число фyнкций. Далее для каждой фyнкции пpиводится ее числовой код. После идет слово с нyлевым значением, а вслед за ним - список ссылок на исполняемyю часть сценаpия (по одной на каждyю фyнкцию). Если никаких фyнкций не опpеделено, то эта часть состоит из двyх нyлевых слов.
Пpи опеpации, котоpyю я назвал в пpедыдyщем письме "подготовкой" сценаpия, из "дальней" памяти в "ближнюю" пеpеписывается только заголовок объекта и та часть, котоpyю я назвал "данными". Сами данные пеpеписываются почти "один в один" (пpавятся только пеpвые два слова, в них записываются ссылки на соответствyющие объекты), а вот заголовок пpи помещении в "ближнюю" память несколько pасшиpяется. Во-пеpвых, его pазмеp yвеличивается до 5 слов. Во-втоpых в него включаются ссылка на дескpиптоp текyщего сценаpия (см. пpедыдyщее письмо) и ссылка на дескpиптоp сценаpия, котоpый содеpжит описание данного объекта (или является "pодителем" объекта, если pечь идет не о самом объекте, а его описании). Смысл остальных тpех слов остается пpежним (слyжебное значение 1234h, ссылка на фyнкции и число данных в объекте).
4. ОБЩАЯ СХЕМА РАБОТЫ ИНТЕРПРЕТАТОРА.
Одной из основных команд в этом интеpпpетатоpе является команда "выполни для заданного объекта yказаннyю опеpацию". Интеpпpетатоpy пеpедается ссылка на объект, а в стек интеpпpетатоpа помещаются следyющие данные (каждый пункт здесь - это слово):
- Код опеpации;
- Число паpаметpов;
- Паpаметp 0;
- Паpаметp 1;
- Паpаметp 2...
Hy, код опеpации - это код опеpации, а под паpаметpами понимаются некотоpые данные, котоpые могyт понадобиться пpи ее выполнении. В частности, могyт и не понадобиться, тогда, понятно, вслед за кодом опеpации пихается нyлевое слово.
Дальше pаботает следyющая логика. В однy из глобальных пеpеменных интеpпpетатоpа помещается ссылка на текyщий объект (точнее, на его данные), а в дpyгyю - ссылка на то место в стеке, где лежит число паpаметpов. Вследствии последнего обстоятельства все паpаметpы становятся достyпны самомy интеpпpетатоpy yже на этапе выполнения кода.
Сначала интеpпpетатоp пpовеpяет, не pавен ли код опеpации одномy из кодов данных этого объекта. Для этого он полезет за описанием объекта этого типа. В общем слyчае такое описание лежит не в текyщем сценаpии, а каком-то дpyгом, котоpый, впpочем, yже есть в опеpативной памяти. Давай пpедположим, что такое соответствие было найдено. Тогда анализиpyется число паpаметpов. Если оно pавно 0, то опеpация тpактyется как чтение слова из области данных объекта, котоpомy соответствyет пеpеданный код. Если число паpаметpов отлично от 0, то это - опеpация записи в это слово. Записывается то значение, котоpое я обозначил как "паpаметp 0". Остальные паpаметpы игноpиpyются.
Тепеpь пpедположим, что код опеpации не pавен ни одномy из кодов данных. Тогда бyдyт пpовеpены коды фyнкций, опpеделенные для данного объекта в текyщем сценаpии. Если найдется фyнкция, код котоpой совпадает с кодом опеpации, то интеpпpетатоp пеpеключится на исполнение этой фyнкции, взяв соответствyющyю ссылкy на исполняемый код в этом сценаpии. Если таких фyнкций нет, то далее пpовеpяются коды фyнкций, опpеделенных в описании объекта этого типа. Если такая фyнкция найдется, то интеpпpетатоp начнет выполнять ее код (однако, обpати внимание, что в общем слyчае этот код pасположен yже не в том сценаpии, где pасположен сам объект). Если же такой фyнкции нет, то пpовеpяются коды фyнкций "pодителя" этого объекта, затем "pодителя pодителя" и т.д. Если в pезyльтате такой пpовеpки окажется, что код опеpации неизвестен, то это - фатальная ошибка.
В общем-то с точки зpения ООП - ничего нового. Интеpесна, пожалyй, сама pеализация.
Как начинается сама игpа? Опyскаем все, что связано с инициализацией (чтение дpайвеpов, заказ памяти, подкачка обязательных блоков и т.д.). Далее пpогpамма говоpит: "Дай мне ссылкy, котоpая лежит пеpвой в pазделе 7 сценаpия с номеpом 0". Это пpиводит к томy, что пpовеpяется список дескpиптоpов сценаpиев на пpедмет наличия в нем сценаpия 0. Его там, естественно, нет (все еще только начинается). Тогда она тянет его с диска, чтобы этот дескpиптоp создать. Пpи этом в память вытягивается довольно солидный хвост сценаpиев, содеpжащих описания объектов, использyемых в сценаpии 0 (с yчетом всех пpедков). В конце концов, загpyзив добpый десяток блоков Script пpогpамма этy ссылкy возвpащает, пpичем это - ссылка на некотоpый объект. Тогда она говоpит: "А вот тепеpь выполни для этого объекта опеpацию, код котоpой pавен 2Ah". Все. С точки зpения ноpмального языка это все pавно, что сказать "А тепеpь давай сыгpаем". Код какого именно сценаpия бyдет пеpедан интеpпpетатоpy на исполнение пеpвым я не изyчал (во всяком слyчае, не сценаpия с номеpом 0). Да и так ли это важно?
***
5. ПРОЦЕСС ИНТЕРПРЕТАЦИИ.
Речь пойдет о самом пpоцессе интеpпpетации. Чтобы "система команд" интеpпpетатоpа была понятна, сначала несколько слов о той сpеде, в котоpой кpyтится этот интеpпpетатоp.
Имеется стек интеpпpетатоpа. Этот стек заводится в сегменте данных пpогpаммы на этапе инициализации и состоит из 1000h байтов. Есть глобальная пеpеменная, котоpая yказывает на веpшинy этого стека. Стек использyется для пеpедачи паpаметpов и на этапе вычислений. Есть еще несколько глобальных пеpеменных, котоpые использyются интеpпpетатоpом пpи выполнении кода. Hиже я пpивожy их список, вводя для этих пеpеменных обозначения, котоpые пpидyмал сам.
- Acc - аккyмyлятоp. Эта пеpеменная использyется пpи аpифметических, логических и пpочих опеpациях. Hапpимеp, пpи опеpации сложения значение этой пеpеменной складывается со значением извлекаемым из стека, а pезyльтат помещается опять в этy же пеpеменнyю. Пpи опеpациях yсловного пеpехода опять же анализиpyется значение именно этой пеpеменной. Смысл, дyмаю, понятен.
- Data - ccылка на область данных текyщего объекта.
- Param - ссылка на паpаметpы, пеpедаваемые интеpпpетатоpy пpи его вызове (о ней я уже писал).
Есть еще 4 пеpеменные, о котоpых я скажy дальше по ходy дела.
Сама pабота интеpпpетатоpа - это классический пpоцесс интеpпpетации. В момент его вызова pегистpы es:si yказывают на пеpвyю интеpпpетиpyемyю командy и пpодвигаются по меpе выбоpки этих команд из входного потока. В самих "командах" пеpвый байт является кодом исполняемой команды, а последyющие ее опеpандами. Число опеpандов и их pазмеp опpеделяются кодом команды. Большинство команд пpодyблиpовано для опеpандов в виде байтов и в виде слов. Hапpимеp, команды с кодом опеpации 32h и 33h являются командами безyсловного пеpехода (т.е. go to). Обе эти команды имеют один опеpанд: смещение команды, на котоpyю бyдет пеpедано yпpавление, относительно текyщей. Однако, для команды с кодом 32h этот опеpанд задается с помощью 2 байтов, а для команды 33h - одним.
Я не собиpаюсь описывать полностью все команды интеpпpетатоpа. Пpотивно, да и, навеpное, ни к чемy. Пpиведy кpаткyю сводкy, остановившись только на тех, котоpые тpебyют пояснений.
Команды с кодами 0h-16h - Аpифметические и битовые (сдвиги, AND, OR и XOR) опеpации над Acc. В большинстве их пpоисходит выбоpка одной пеpеменной из стека.
18h-2Ch - Логические опеpации. Использyется Acc и стек. Резyльтат помещается на Acc.
2Eh-33h - Команды пеpеходов (yсловных и безyсловных).
34h-3Ch - Опеpации чтения/записи cо стеком и Acc (положить опеpанд в стек, положить в стек Acc, пpисвоить опеpанд Acc и т.д.).
3Eh и 3Fh - Резеpвиpование в стеке области памяти, pазмеp котоpой опpеделяется опеpандом команды. Ссылка на выделеннyю область пpисваивается глобальной пеpеменной, котоpyю я обозначy как Mem. Фактически можно тpактовать как опеpацию динамического заказа памяти.
40h и 41h - Вызов "внyтpенней" фyнкции (подпpогpаммы) интеpпpетатоpа. Вызываемая фyнкция должна pасполагаться в том же сценаpии, что и текyщая. Адpес фyнкции задается с помощью опеpанда как смещение вызываемой фyнкции относительно текyщей команды. Пеpедача паpаметpов осyществляется чеpез стек интеpпpетатоpа.
42h и 43h - Вызов "внешней" фyнкции интеpпpетатоpа. Сам код такой фyнкции pеализyется yже не с помощью команд интеpпpетатоpа, а с использованием обычного пpогpаммного кода. Hапpимеp, выполнение таких опеpаций, как подкачка очеpедного блока с диска, вывод инфоpмации на экpан, опpос клавиатypы и мыши и т.д., pеализованы как вызов таких фyнкций. Опеpанд задает номеp фyнкции. С некотоpой долей yсловности можно pассматpивать даннyю командy как пpогpаммное пpеpывание в системе команд интеpпpетатоpа, а значение опеpанда - как номеp такого пpеpывания.
44h и 45h - Взять из pаздела 7 сценаpия с номеpом 0 ссылкy, номеp котоpой опpеделяется опеpандом команды, и пеpедать на нее yпpавление. В общем-то, можно pассматpивать этy командy, как аналог call far для нyлевого сценаpия в отличии от команд 40h и 41h, котоpые являются аналогом call near. Раздел 7 сценаpиев в этом слyчае можно тpактовать как таблицy внешних входов в сценаpий.
46h и 47h - Аналогично пpедyщей команде, но номеp сценаpия также опpеделяется опеpандом команды.
48h - Return (no comments).
4Ah - Выполнение опеpации для объекта, ссылка на котоpый pасполагается в Acc. Код опеpации и ее паpаметpы должны быть пpедваpительно помещены в стек, как я это описывал в пpедыдyщем письме.
50h и 51h - Возpащает в Acc ссылкy на описание объекта, тип котоpого опpеделяется опеpандом команды.
54h и 55h - Тоже, что и 4Ah, но для текyщего объекта.
56h и 57h - Аналогично 4Ah, но опеpация выполняется не с объектом, а его описанием. Тип объекта опpеделяется опеpандом команды.
59h - Здесь пpидется словами. Смотpим на паpаметpы пеpеданные интеpпpетатоpy пpи его вызове (или вызове фyнкции, или вызове опеpации для объекта). Точнее, на то место, кyда ссылается глобальная пеpеменная Param (в пpед. письме есть на этy темy pисyночек). Значение опеpанда этой команды тpактyется так: такое-то число паpаметpов мне (т.е. текyщей опеpации) известно, я pазобpался с ними сам, а остальные пеpесыпаю в конец стека (т.е. пеpедаю дальше). Пpи этом попyтно вычисляется число пеpесыпанных паpаметpов и оно добавляется к пеpеменной, котоpyю назовy ParamCount (начальное ее значение, естественно, 0). Как пpавило, после этой команды пpоисходит пеpедача yпpавления, связанная с пеpедачей паpаметpов чеpез стек. В момент, когда yпpавление пpинимается вызванной пpогpаммой, анализиpyется значение ParamCount. Если оно ненyлевое, то ParamCount обнyляется, а ячейка, котоpая содеpжит число пеpеданных паpаметpов соответствyющим обpазом коppектиpyется в большyю стоpонy.
5Ah и 5Bh - Здесь полный мpак. Этих команд я не понял...
5Ch - Возвpащает на Acc значение пеpеменной Data (т.е. ссылкy на данные текyщего объекта).
60h. Очень смешно, но ставит на Acc значение pегистpа сх (Датчик слyчайных чисел ?).
62h-71h - Команды чтения/записи/модификации данных текyщего объекта.
72h-7Ah - Работа с константами. Есть специальные команды для записи в стек 0, 1 и 2. Кpоме того, сyществyют команды для записи в Acc и стек константы, положение котоpой в текyщем сценаpии задается с помощью опеpанда этих команд как смещение относительно текyщей команды.
7Сh - Запись в стек ссылки на данные текyщего объекта (т.е. пеpеменной Data).
Пpежде чем описывать остальные команды скажy об оставшихся глобальных пеpеменных (в начале я обещал еще 4, но о двyх - Mem и ParamCount - yже сказал). Так вот, сyществyют еще две пеpеменные. Одна из них ссылается на pаздел 0Ah сценаpия с текyщим объектом (назовy ее ObjA), а втоpая на pаздел 0Ah выполняемого (CodeA). Зачем они нyжны и каков их смысл, я пока не знаю. Видимо нyжно pассматpивать в контексте конкpетных смысловых опеpаций сценаpиев, но я до этого пока не добpался (и не знаю добеpyсь ли когда-нибyдь...). Если кто выскажет на этот счет pазyмнyю гипотезy - бyдy благодаpен.
Глобальные пеpеменные ObjA, CodeA, Mem и Param можно pассматpивать как yказатели на массивы слов. Оставшиеся команды (88h-FFh) пpедназначены для чтения/записи и модификации этих массивов.
Как видно из изложенного, коды команд pасположены в диапазоне от 0h до FFh, но в действительности их меньше, чем 256, посколькy "заняты" не все значения этого диапазона.
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
В продолжение первой главы первого тома ТЕМА #60, АБЗАЦ #635 ...
- Папа, я хочу в войнушку!
- Сейчас. Берем ножик, палку и строгаем ружье.
- С солдатиками!
- Берем лобзик и фанерку. Пилим крепость. Строим солдатиков.
- Во всамделишную!
- Берем карту, красный и синий карандаши. Рисуем стрелки.
- На компьютере!
- ...! Сейчас папа заработает немножко денег. Сбегает в магазин и купит очередной дурацкий FPS или RTS...
Раньше было проще: включаешь персональный компьютер и сразу попадаешь в BASIC, который умеет... (см. первый том).
Теперь... А что теперь? С некоторых пор есть BASIC и у Windows. Причем, его не надо покупать отдельно. Просто, он очень хорошо спрятан.
Нужно найти папочку Windows, в ней - Microsoft.NET ... И где-то там будет файл vbc.exe (и не один - МастДай на то и МастДай, что сам с собой не совместим). Запоминаем в командном файле путь к любому из них, вписываем туда имя текстового файла с нашей программой - и готово, vbc будет делать из нашей программы исполняемый .exe-файл. (Конечно, надо немножко уметь работать с файлами и папочками. Надеюсь, после второго тома это не сложно).
Пишем игру MISSILE COMMAD.
Конечно, этот VB - не честный Visual Basic. Тот-то содержал красивый редактор для рисования и оживляжа окошек-формочек, а этот - лишь VB-подобная оболочка над внутренним .NET-языком IL (MSIL, CIL). Вам придется ковырять справочники по Win Forms и самим бороться с трехфиговым проклятьем МастДая ТЕМА #35, АБЗАЦ #309.
Как и старый DOS-BASIC, VB.NET может работать в двух режимах - текстовом и графическом. Конечно, в обоих случаях он уступает своему далекому предку. Текстовый режим упрощен настолько, что все, что выходит за пределы обычного телетайпа - рисование в нужном месте экрана, использование псевдографики - ему не по силам. Как было показано во втором томе, писать текстовые игры проще другими средствами.
В графическом режиме MS Windows так и не родило простой системной поддержки рисования чего-либо, крооме линий-примитивов. Хочешь живописи - ищи способ отключить "обычное Win-рисование" (так, например, работает DirectX). Поэтому моя реализация будет гораздо менее совершенной, чем 1980 года. Там была оптимизированная растровая графика со спецэффектами, а у меня будет тупая векторная, с постоянной избыточной перерисовкой всего экрана...
Возьмем за основу схему описания настольных игр.
I. ПОСЛЕДОВАТЕЛЬНОСТИ
Как и во втором томе, видим, что просто так, по-простому, рисовать и вести диалог с пользователем VB-программа не может. Для того, чтобы хоть что-то просто начало работать, надо написать достаточно сложную ничего не делающую программу.
Что-то вроде:
1. "СКЕЛЕТ"
ИНСТРУМЕНТАРИЙ
{ ОПРЕДЕЛЕНИЕ ФОРМЫ
ТАЙМЕР
<определения частей программы>
{ ЗАПУСК ФОРМЫ
УСТАНОВКА РАЗМЕРА
УСТАНОВКА ДВОЙНОЙ БУФЕРИЗАЦИИ
СВЯЗЫВАНИЕ ТАЙМЕРА с ТЕЛЕПАТОРОМ ФОРМЫ
ЗАПУСК РАНДОМИЗАТОРА
<подготовка частей к игре>
ЗАПУСК ФОРМЫ }
{ ТЕЛЕПАТОР ФОРМЫ
<телепаторы частей>
ЗАПУСК РИСОВАТЕЛЯ ФОРМЫ
ТЕЛЕПАТОР ФОРМЫ }
{ РИСОВАТЕЛЬ ФОРМЫ
<рисователи частей>
РИСОВАТЕЛЬ ФОРМЫ }
ЗАПУСК ПРОГРАММЫ
ОПРЕДЕЛЕНИЕ ФОРМЫ }
Этот скелет даст нам четыре точки для запихивания всего остального кода: внутрь ОПРЕДЕЛЕНИЯ ФОРМЫ запихиваем все определения других частей программы, внутрь ЗАПУСКА ФОРМЫ - их подготовку к игре, в ТЕЛЕПАТОР ФОРМЫ - их телепание, в РИСОВАТЕЛЬ ФОРМЫ - их рисование.
Программа работает примерно так: сначала загружается форма, т.е. само Window (окно) со всеми его рамками и заголовками. Из обязательных танцев с бубном, здесь надо привязать к программе таймер, ведь, мы же хотим игру реального времени; и не забыть указать двойную буферизацию рисования, иначе картинка будет дрожать.
Как это работает? После ЗАПУСКА ФОРМЫ таймер будет постоянно вызывать ТЕЛЕПАТОР ФОРМЫ, который будет отслеживать изменения на поле боя и обязательно пинать РИСОВАТЕЛЬ, чтобы эти изменения отрисовались. (При обычном программировании на Visual Basic можно ограничиться только правкой ЗАПУСКА, точнее, графическим редактированием обычных кнопочек-стикеров. Все остальное VB сделает сам. Но мы-то хотим играть-рисовать сами).
Пустой СКЕЛЕТ выглядит на BASIC как показано ниже. Его можно откомпилировать и запустить. Просто будет создано пустое окно.
Можно видеть три ПАРАМЕТРА ИГРЫ - размеры формы и частоту тиков таймеров. Остально - это фигня, которую лучше не трогать.
***
Но, опять, рисовать прямо в программе, как и общаться прямо с ней, нельзя. Нужно вставить подходящие объекты. Т.к. обычные VB-объекты нас не устраивают то усложним ПОСЛЕДОВАТЕЛЬНОСТЬ ИГРЫ (при этом она даже немного упростится!). Вы можете обойтись без этого и честно заполнять все четыре "дыры" скелета сами.
Добавим в ОПРЕДЕЛЕНИЕ, ТЕЛЕПАТОР и РИСОВАТЕЛЬ ФОРМЫ
2. "ОРГАНЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
ССЫЛКИ НА БРАТЬЕВ
ВИРТУАЛЬНЫЙ РИСОВАТЕЛЬ
ВИРТУАЛЬНЫЙ ТЕЛЕПАТОР
ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА }
ГОЛОВА СПИСКА
ДОБАВЛЯТОР ОРГАНА
УДАЛЯТОР ОРГАНА
: ТЕЛЕПАТОР ФОРМЫ
ЗАПУСК ТЕЛЕПАТОРОВ ОРГАНОВ
: РИСОВАТЕЛЬ ФОРМЫ
ЗАПУСК РИСОВАТЕЛЕЙ ОРГАНОВ
На Basic это три куска, которые надо подсунуть в места, выше помеченные комментариями:
После этого нам больше лазить в ТЕЛЕПАТОР и РИСОВАТЕЛЬ ФОРМЫ вообще не понадобится. Как и в обычном Visual Basic, органы будут сами отвечать за свое телепание и рисование.
Обратите внимание на страшные слова Public, Dim, Overridable, предваряющие имена переменных и подпрограмм. Overridable надо ставить перед всеми подпрограммами в ОПРЕДЕЛЕНИИ АБСТРАКТНОГО ОРГАНА (Thing). Те же процедуры в органах-потомках надо помечать словом Overrides. Что касается слов, управляющих видимостью имен, то перед именами переменных, которые проживут не более пары строк, можжно, как и в природном BASIC ставить Dim. Перед именами переменных (и подпрограмм), которые должны быть видны другим - Public, а, если это не поможет - Shared. И не заморачивайтесь без потребности ООП-дебилизмами...
Сами эти объекты-органы стали у меня объектно-ориентированным исключительно по идиотизму Visual Basic (и всего .NET в целом), боящегося, что получив прямой доступ к памяти, я поломаю МастДай. Таблица органов смотрелась бы гораздо проще и удобнее, чем список объектов... (Такое применение объектов я называю быдло-ООП). Другое решение проблемы хранения адресов подпрограмм (для обычного Visual Basic) можно посмотреть в ТЕМА #14, АБЗАЦ #379.
***
Следующее улучшение программы не столько добавит, сколько убавит. По правилам игры MISSILE COMMAND мышка (или джойстик, или трекбол) отвечает за перемещение курсора по экрану, а клавиши "1", "2" и "3" - за стрельбу. Только эти действия игрока я и буду учитывать:
3. "РЕЦЕПТОРЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
КООРДИНАТЫ МЫШИ
НОМЕР КЛАВИШИ
РЕАКЦИЯ НА ПЕРЕМЕЩЕНИЕ МЫШКИ
РЕАКЦИЯ НА НАЖАТИЕ КЛАВИШИ
На Basic:
Ничего делать, кроме запоминания текущих значений клавиатуры и мышки, здесь не надо, этим будут заниматься специально обученные органы во время своего телепания.
Тут надо быть осторожным. МастДай на то и МастДай, что чуть что, и придется искать, почему какая-нибудь кнопочка решила, что нажатие клавиши относится не ко всему окну, а только к ней... В нашем простейшем случае проблем не будет.
***
Программа по-прежнему компилируется, запускается и... ничего не выдает, кроме пустого окошка...
Еще одно замечание: что бы вы не написали на Visual Basic .NET, это будет неправильно. Всегда есть более простой/правильный/общепринятый способ, но искать его - значит, плюнуть на игру и углубиться в быдлокодинг...
...
- Папа, я хочу в войнушку!
- Сейчас. Берем ножик, палку и строгаем ружье.
- С солдатиками!
- Берем лобзик и фанерку. Пилим крепость. Строим солдатиков.
- Во всамделишную!
- Берем карту, красный и синий карандаши. Рисуем стрелки.
- На компьютере!
- ...! Сейчас папа заработает немножко денег. Сбегает в магазин и купит очередной дурацкий FPS или RTS...
Раньше было проще: включаешь персональный компьютер и сразу попадаешь в BASIC, который умеет... (см. первый том).
Теперь... А что теперь? С некоторых пор есть BASIC и у Windows. Причем, его не надо покупать отдельно. Просто, он очень хорошо спрятан.
Нужно найти папочку Windows, в ней - Microsoft.NET ... И где-то там будет файл vbc.exe (и не один - МастДай на то и МастДай, что сам с собой не совместим). Запоминаем в командном файле путь к любому из них, вписываем туда имя текстового файла с нашей программой - и готово, vbc будет делать из нашей программы исполняемый .exe-файл. (Конечно, надо немножко уметь работать с файлами и папочками. Надеюсь, после второго тома это не сложно).
Пишем игру MISSILE COMMAD.
Конечно, этот VB - не честный Visual Basic. Тот-то содержал красивый редактор для рисования и оживляжа окошек-формочек, а этот - лишь VB-подобная оболочка над внутренним .NET-языком IL (MSIL, CIL). Вам придется ковырять справочники по Win Forms и самим бороться с трехфиговым проклятьем МастДая ТЕМА #35, АБЗАЦ #309.
Как и старый DOS-BASIC, VB.NET может работать в двух режимах - текстовом и графическом. Конечно, в обоих случаях он уступает своему далекому предку. Текстовый режим упрощен настолько, что все, что выходит за пределы обычного телетайпа - рисование в нужном месте экрана, использование псевдографики - ему не по силам. Как было показано во втором томе, писать текстовые игры проще другими средствами.
В графическом режиме MS Windows так и не родило простой системной поддержки рисования чего-либо, крооме линий-примитивов. Хочешь живописи - ищи способ отключить "обычное Win-рисование" (так, например, работает DirectX). Поэтому моя реализация будет гораздо менее совершенной, чем 1980 года. Там была оптимизированная растровая графика со спецэффектами, а у меня будет тупая векторная, с постоянной избыточной перерисовкой всего экрана...
Возьмем за основу схему описания настольных игр.
I. ПОСЛЕДОВАТЕЛЬНОСТИ
Как и во втором томе, видим, что просто так, по-простому, рисовать и вести диалог с пользователем VB-программа не может. Для того, чтобы хоть что-то просто начало работать, надо написать достаточно сложную ничего не делающую программу.
Что-то вроде:
1. "СКЕЛЕТ"
ИНСТРУМЕНТАРИЙ
{ ОПРЕДЕЛЕНИЕ ФОРМЫ
ТАЙМЕР
<определения частей программы>
{ ЗАПУСК ФОРМЫ
УСТАНОВКА РАЗМЕРА
УСТАНОВКА ДВОЙНОЙ БУФЕРИЗАЦИИ
СВЯЗЫВАНИЕ ТАЙМЕРА с ТЕЛЕПАТОРОМ ФОРМЫ
ЗАПУСК РАНДОМИЗАТОРА
<подготовка частей к игре>
ЗАПУСК ФОРМЫ }
{ ТЕЛЕПАТОР ФОРМЫ
<телепаторы частей>
ЗАПУСК РИСОВАТЕЛЯ ФОРМЫ
ТЕЛЕПАТОР ФОРМЫ }
{ РИСОВАТЕЛЬ ФОРМЫ
<рисователи частей>
РИСОВАТЕЛЬ ФОРМЫ }
ЗАПУСК ПРОГРАММЫ
ОПРЕДЕЛЕНИЕ ФОРМЫ }
Этот скелет даст нам четыре точки для запихивания всего остального кода: внутрь ОПРЕДЕЛЕНИЯ ФОРМЫ запихиваем все определения других частей программы, внутрь ЗАПУСКА ФОРМЫ - их подготовку к игре, в ТЕЛЕПАТОР ФОРМЫ - их телепание, в РИСОВАТЕЛЬ ФОРМЫ - их рисование.
Программа работает примерно так: сначала загружается форма, т.е. само Window (окно) со всеми его рамками и заголовками. Из обязательных танцев с бубном, здесь надо привязать к программе таймер, ведь, мы же хотим игру реального времени; и не забыть указать двойную буферизацию рисования, иначе картинка будет дрожать.
Как это работает? После ЗАПУСКА ФОРМЫ таймер будет постоянно вызывать ТЕЛЕПАТОР ФОРМЫ, который будет отслеживать изменения на поле боя и обязательно пинать РИСОВАТЕЛЬ, чтобы эти изменения отрисовались. (При обычном программировании на Visual Basic можно ограничиться только правкой ЗАПУСКА, точнее, графическим редактированием обычных кнопочек-стикеров. Все остальное VB сделает сам. Но мы-то хотим играть-рисовать сами).
Пустой СКЕЛЕТ выглядит на BASIC как показано ниже. Его можно откомпилировать и запустить. Просто будет создано пустое окно.
- Код:
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Public Class Form1
Inherits Form
Dim Timer1 As Timer
' место под определения частей программы
Public Sub New
ClientSize = New Size(640, 480)
DoubleBuffered = True
Timer1 = New Timer
Timer1.Interval = 100
AddHandler Timer1.Tick, AddressOf Timer1_Tick
Timer1.Start
Randomize()
' место подготовки частей к игре
End Sub
Sub Timer1_Tick
' место под телепатороы частей
Invalidate
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
' место под рисование частей
End Sub
<STAThread()> Shared Sub Main()
Application.Run(New Form1)
End Sub
End Class
Можно видеть три ПАРАМЕТРА ИГРЫ - размеры формы и частоту тиков таймеров. Остально - это фигня, которую лучше не трогать.
***
Но, опять, рисовать прямо в программе, как и общаться прямо с ней, нельзя. Нужно вставить подходящие объекты. Т.к. обычные VB-объекты нас не устраивают то усложним ПОСЛЕДОВАТЕЛЬНОСТЬ ИГРЫ (при этом она даже немного упростится!). Вы можете обойтись без этого и честно заполнять все четыре "дыры" скелета сами.
Добавим в ОПРЕДЕЛЕНИЕ, ТЕЛЕПАТОР и РИСОВАТЕЛЬ ФОРМЫ
2. "ОРГАНЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
ССЫЛКИ НА БРАТЬЕВ
ВИРТУАЛЬНЫЙ РИСОВАТЕЛЬ
ВИРТУАЛЬНЫЙ ТЕЛЕПАТОР
ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА }
ГОЛОВА СПИСКА
ДОБАВЛЯТОР ОРГАНА
УДАЛЯТОР ОРГАНА
: ТЕЛЕПАТОР ФОРМЫ
ЗАПУСК ТЕЛЕПАТОРОВ ОРГАНОВ
: РИСОВАТЕЛЬ ФОРМЫ
ЗАПУСК РИСОВАТЕЛЕЙ ОРГАНОВ
На Basic это три куска, которые надо подсунуть в места, выше помеченные комментариями:
- Код:
' на место, отведенное под определения частей программы
Public class Thing
Public Younger As Thing, Older As Thing
Public Overridable Sub Draw(e As PaintEventArgs)
End Sub
Public Overridable Sub Tick
End Sub
' место под общие параметры частей и другие прототипы функций
End Class
Shared Head As Thing
Shared Sub Link(current As Thing)
current.Younger = Head.Younger: Head.Younger = current: current.Older = Head
If current.Younger IsNot Nothing Then current.Younger.Older = current
End Sub
Shared Sub Unlink(current As Thing)
current.Older.Younger = current.Younger
If current.Younger IsNot Nothing Then current.Younger.Older = current.Older
current.Older = Nothing
End Sub
' на место, отведенное под телепатороы частей
Dim Current As Thing
Current = Head
While Current IsNot Nothing: Current.Tick
Current = Current.Younger: End While
Invalidate
' на место, отведенное под рисование частей
MyBase.OnPaint(e)
Dim Current As Thing
Current = Head
While Current IsNot Nothing: Current.Draw(e)
Current = Current.Younger: End While
После этого нам больше лазить в ТЕЛЕПАТОР и РИСОВАТЕЛЬ ФОРМЫ вообще не понадобится. Как и в обычном Visual Basic, органы будут сами отвечать за свое телепание и рисование.
Обратите внимание на страшные слова Public, Dim, Overridable, предваряющие имена переменных и подпрограмм. Overridable надо ставить перед всеми подпрограммами в ОПРЕДЕЛЕНИИ АБСТРАКТНОГО ОРГАНА (Thing). Те же процедуры в органах-потомках надо помечать словом Overrides. Что касается слов, управляющих видимостью имен, то перед именами переменных, которые проживут не более пары строк, можжно, как и в природном BASIC ставить Dim. Перед именами переменных (и подпрограмм), которые должны быть видны другим - Public, а, если это не поможет - Shared. И не заморачивайтесь без потребности ООП-дебилизмами...
Сами эти объекты-органы стали у меня объектно-ориентированным исключительно по идиотизму Visual Basic (и всего .NET в целом), боящегося, что получив прямой доступ к памяти, я поломаю МастДай. Таблица органов смотрелась бы гораздо проще и удобнее, чем список объектов... (Такое применение объектов я называю быдло-ООП). Другое решение проблемы хранения адресов подпрограмм (для обычного Visual Basic) можно посмотреть в ТЕМА #14, АБЗАЦ #379.
***
Следующее улучшение программы не столько добавит, сколько убавит. По правилам игры MISSILE COMMAND мышка (или джойстик, или трекбол) отвечает за перемещение курсора по экрану, а клавиши "1", "2" и "3" - за стрельбу. Только эти действия игрока я и буду учитывать:
3. "РЕЦЕПТОРЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
КООРДИНАТЫ МЫШИ
НОМЕР КЛАВИШИ
РЕАКЦИЯ НА ПЕРЕМЕЩЕНИЕ МЫШКИ
РЕАКЦИЯ НА НАЖАТИЕ КЛАВИШИ
На Basic:
- Код:
' на место, отведенное под определения частей программы
Shared MouseX As Integer, MouseY As Integer
Shared BaseN As Integer
Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
MouseX = e.X: MouseY = e.Y
End Sub
Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
BaseN = Asc(e.KeyChar) And 3
End Sub
Ничего делать, кроме запоминания текущих значений клавиатуры и мышки, здесь не надо, этим будут заниматься специально обученные органы во время своего телепания.
Тут надо быть осторожным. МастДай на то и МастДай, что чуть что, и придется искать, почему какая-нибудь кнопочка решила, что нажатие клавиши относится не ко всему окну, а только к ней... В нашем простейшем случае проблем не будет.
***
Программа по-прежнему компилируется, запускается и... ничего не выдает, кроме пустого окошка...
Еще одно замечание: что бы вы не написали на Visual Basic .NET, это будет неправильно. Всегда есть более простой/правильный/общепринятый способ, но искать его - значит, плюнуть на игру и углубиться в быдлокодинг...
...
Последний раз редактировалось: Gudleifr (Сб Июн 10, 2023 10:04 pm), всего редактировалось 4 раз(а)
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
II. С ПОСЛЕДОВАТЕЛЬНОСТЬЮ ИГРЫ разобрались. Переходим к ЗАВИСИМОСТЯМ. Как ходят наши "фигуры" в зависимости от своей натуры?
Для простейшей игры MISSILE COMMAND мне понадобились следующие объекты:
4. "ГОРИЗОНТ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
РИСОВАТЕЛЬ ЛИНИИ ГОРИЗОНТА
ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА }
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ ГОРИЗОНТА - ГОЛОВЫ СПИСКА ОРГАНОВ
5. "ГОРОДА"
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
КООРДИНАТЫ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
РИСОВАТЕЛЬ ГОРОДА
ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА }
ДОБАВЛЯТОР ГОРОДА
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ ГОРОДОВ
6. "РАКЕТНЫЕ БАЗЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
КОД БАЗЫ
КОЛИЧЕСТВО РАКЕТ
РИСОВАТЕЛЬ РАКЕТНОЙ БАЗЫ
ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ }
ДОБАВЛЯТОР РАКЕТНОЙ БАЗЫ
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ РАКЕТНЫХ БАЗ
7. "ВРАЖЬИ РАКЕТЫ"
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
ИНДИКАТОР ЦЕЛИ - ГОРОДА
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
ИНДИКАТОР ЦЕЛИ - РАКЕТНОЙ БАЗЫ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ЦЕЛЬ РАКЕТЫ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
РИСОВАТЕЛЬ РАКЕТЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ }
ОПРЕДЕЛЕНИЕ УСПЕШНОСТИ ЗАПУСКА
НАВЕДЕНИЕ РАКЕТЫ
ДОБАВЛЕНИЕ РАКЕТ
8. "РАКЕТНЫЙ ЗАЛП"
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ТЕЛЕПАТОР РАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРИЗОНТА
СЧЕТЧИК ВРЕМЕНИ
ТЕЛЕПАТОР ГОРИЗОНТА
9. "ВЗРЫВЫ"
: ТЕЛЕПАТОР РАКЕТЫ
ВЗРЫВ ПО ДОСТИЖЕНИИ ПОВЕРХНОСТИ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
РАДИУС
ПРИРАЩЕНИЕ
РИСОВАТЕЛЬ ВЗРЫВА
ТЕЛЕПАТОР ВЗРЫВА
ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА }
ДОБАВЛЕНИЕ ВЗРЫВА
10. "РАЗРУШЕНИЯ"
: ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
СЧЕТЧИК ГОРОДОВ
: РИСОВАТЕЛЬ ГОРИЗОНТА
GAME OVER
: ТЕЛЕПАТОР РАКЕТЫ
УНИЧТОЖЕНИЕ ЦЕЛИ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН
ИНДИКАТОР ЦЕЛИ - РУИН
РИСОВАТЕЛЬ РУИН
ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН }
ПРЕВРАЩАТЕЛЬ В РУИНЫ
: ЗАПУСК ФОРМЫ
НАЧАЛЬНОЕ ЧИСЛО ГОРОДОВ
11. "ПРОТИВОРАКЕТЫ"
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
КОНЕЧЕАЯ ТОЧКА
СКОРОСТЬ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
РИСОВАТЕЛЬ ПРОТИВОРАКЕТЫ
ТЕЛЕПАТОР ПРОТИВОРАКЕТЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ }
ЗАПУСКАТЕЛЬ ПРОТИВОРАКЕТ
12. "ПОДРЫВ ПРОТИВОРАКЕТ"
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
ДЕТОНАЦИЯ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ДЕТОНАЦИЯ РАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
ДЕТОНАЦИЯ ПРОТИВОРАКЕТЫ
: ТЕЛЕПАТОР ВЗРЫВА
ЗАПУСК ЦЕПНОЙ РЕАКЦИИ
13. "РАЗДЕЛЯЮЩИЕСЯ БОЕГОЛОВКИ"
: ТЕЛЕПАТОР РАКЕТЫ
РАЗДЕЛЕНИЕ БОЕГОЛОВКИ
14. "САМОЛЕТЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
СКОРОСТЬ
РИСОВАТЕЛЬ САМОЛЕТА
ТЕЛЕПАТОР САМОЛЕТА
ДЕТОНАЦИЯ САМОЛЕТА
ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА }
: ТЕЛЕПАТОР ГОРИЗОНТА
ЗАПУСКАТЕЛЬ САМОЛЕТА
15. "УПРАВЛЯЕМАЯ БОМБА"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ
РИСОВАТЕЛЬ БОМБЫ
ТЕЛЕПАТОР БОМБЫ
ДЕТОНАЦИЯ БОМБЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ }
: ТЕЛЕПАТОР ГОРИЗОНТА
ЗАПУСКАТЕЛЬ БОМБЫ
16. "СЧЕТЧИК ОЧКОВ"
: ДЕТОНАЦИЯ РАКЕТЫ
ПОЛУЧЕНИЕ БОНУСА
: ДЕТОНАЦИЯ САМОЛЕТА
ПОЛУЧЕНИЕ БОНУСА
: ДЕТОНАЦИЯ БОМБЫ
ПОЛУЧЕНИЕ БОНУСА
: РИСОВАТЕЛЬ ГОРИЗОНТА
ОТОБРАЖЕНИЕ СЧЕТЧИКА
Я разбил это все на шаги, чтобы была возможность отлаживать программу по частям, после ввода в игру каждой новой "фигуры". Прежде чем перейти к коду, несколько пояснений:
По мере добавления органов пришлось вносить добавления в ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО КЛАССА. Переменные (КООРДИНАТЫ) - для удобства хранения параметров, которые есть почти у всех органов. Подпрограммы (в довесок к ТЕЛЕПАТОРАМ и РИСОВАТЕЛЯМ - ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ и ДЕТОНАЦИЯ) - наоборот, для возможности органам заявить о своем отличии от других (быдло-ООП!). После отладки кода от этих довесков почти ничего не осталось:
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
КООРДИНАТЫ
ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ
ДЕТОНАЦИЯ
Плюс, КООРДИНАТЫ почти во всех органах используются по-разному. Оставил в "прародителе" только для экономии места.
Некоторые органы пришлось добавить в ЗАПУСК ФОРМЫ, т.к. они присутствуют в игре с самого начала:
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ ГОРИЗОНТА - ГОЛОВЫ СПИСКА ОРГАНОВ
ДОБАВЛЕНИЕ ГОРОДОВ
ДОБАВЛЕНИЕ РАКЕТНЫХ БАЗ
НАЧАЛЬНОЕ ЧИСЛО ГОРОДОВ
Не все органы будут иметь особые варианты наследственных подпрограмм. Некоторым будет достаточно "прародительских" заглушек. Кроме того, по мере добавления новых органов приходилось модифицировать подпрограммы старых.
: ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
РИСОВАТЕЛЬ ЛИНИИ ГОРИЗОНТА
ТЕЛЕПАТОР ГОРИЗОНТА
: ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
РИСОВАТЕЛЬ ГОРОДА
ИНДИКАТОР ЦЕЛИ - ГОРОДА
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
РИСОВАТЕЛЬ РАКЕТНОЙ БАЗЫ
ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
ИНДИКАТОР ЦЕЛИ - РАКЕТНОЙ БАЗЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
РИСОВАТЕЛЬ РАКЕТЫ
ТЕЛЕПАТОР РАКЕТЫ
ДЕТОНАЦИЯ РАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
РИСОВАТЕЛЬ ВЗРЫВА
ТЕЛЕПАТОР ВЗРЫВА
: ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН
ИНДИКАТОР ЦЕЛИ - РУИН
РИСОВАТЕЛЬ РУИН
: ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
РИСОВАТЕЛЬ ПРОТИВОРАКЕТЫ
ТЕЛЕПАТОР ПРОТИВОРАКЕТЫ
ДЕТОНАЦИЯ ПРОТИВОРАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
РИСОВАТЕЛЬ САМОЛЕТА
ТЕЛЕПАТОР САМОЛЕТА
ДЕТОНАЦИЯ САМОЛЕТА
: ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ
РИСОВАТЕЛЬ БОМБЫ
ТЕЛЕПАТОР БОМБЫ
ДЕТОНАЦИЯ БОМБЫ
Переменных же я сыпал, не жалея.
: ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
СЧЕТЧИК ВРЕМЕНИ
СЧЕТЧИК ГОРОДОВ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
КОД БАЗЫ
КОЛИЧЕСТВО РАКЕТ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ЦЕЛЬ РАКЕТЫ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
РАДИУС
ПРИРАЩЕНИЕ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
КОНЕЧЕАЯ ТОЧКА
СКОРОСТЬ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
: ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
СКОРОСТЬ
III. ИГРОВЫЕ ИСКЛЮЧЕНИЯ очевидны - это вызванные коллизиями "фигур" изменения списка органов во время телепания: добавление и удаление (превращение реализуется путем их комбинирования). Однако, как в случае с отслеживанием действия пользователя, большинство последствий ИСКЛЮЧЕНИЙ наступают только при следующем телепании. Даже, если речь идет о цепной реакции (при ДЕТОНАЦИИ).
IV. УСЛОВИЙ В ИГРЕ практически нет. Я намеренно их избегал, не желая накгружать игру "спортивной статистикой" в ущерб "аркадности". Остались, пожалуй, только начальные установки (в ЗАГРУЗКЕ ФОРМЫ) - число ГОРОДОВ, РАКЕТНЫХ БАЗ и ПРОТИВОРАКЕТ. Да условие наступления "GAME OVER". Вообще же, ТЕЛЕПАТОР головного органа списка - ГОРИЗОНТА - и занимается отслеживанием происходящих в игре событий.
V. ПАРАМЕТРОВ В ИГРЕ не так уж и много - выше уже поминал размеры окна и частоту тиков. Конечно - скорости "фигур" и вероятности их вступления в игру. Проблема не количестве ПАРАМЕТРОВ, а в их подборе для наибольшей играбельности. На момент написания этих строк, я еще не нашел удачного сочетания.
VI. ФОРМУЛЫ ИГРЫ настолько очевидны, что я, по большей части, реализовал их прямо в ТЕЛЕПАТОРАХ (и ДЕТОНАЦИИ). Отдельно вынес, по большей части подпрограммы нужные ЗАПУСКУ ФОРМЫ, пару другу общих мест и куски, нуждающиеся в тонкой настройке. Как и ПАРАМЕТРЫ, их еще надо пошлифовать.
: ОПРЕДЕЛЕНИЕ ФОРМЫ
ДОБАВЛЯТОР ГОРОДА
ДОБАВЛЯТОР РАКЕТНОЙ БАЗЫ
ОПРЕДЕЛЕНИЕ УСПЕШНОСТИ ЗАПУСКА
НАВЕДЕНИЕ РАКЕТЫ
ДОБАВЛЕНИЕ РАКЕТ
ДОБАВЛЕНИЕ ВЗРЫВА
ПРЕВРАЩАТЕЛЬ В РУИНЫ
ЗАПУСКАТЕЛЬ ПРОТИВОРАКЕТ
VII. НЕОДНОРОДНОСТИ ИГРЫ? Ну, как бы, цель данной работы, как раз, показать легкость добавления в игру любых желаемых "фигур" со своими правилами. Видно, что при достаточно простых правилах игры и очень простой синхронизации игровых событий добавлять в игру новые "фичи" можно без проблем.
...
Для простейшей игры MISSILE COMMAND мне понадобились следующие объекты:
4. "ГОРИЗОНТ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
РИСОВАТЕЛЬ ЛИНИИ ГОРИЗОНТА
ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА }
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ ГОРИЗОНТА - ГОЛОВЫ СПИСКА ОРГАНОВ
5. "ГОРОДА"
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
КООРДИНАТЫ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
РИСОВАТЕЛЬ ГОРОДА
ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА }
ДОБАВЛЯТОР ГОРОДА
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ ГОРОДОВ
6. "РАКЕТНЫЕ БАЗЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
КОД БАЗЫ
КОЛИЧЕСТВО РАКЕТ
РИСОВАТЕЛЬ РАКЕТНОЙ БАЗЫ
ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ }
ДОБАВЛЯТОР РАКЕТНОЙ БАЗЫ
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ РАКЕТНЫХ БАЗ
7. "ВРАЖЬИ РАКЕТЫ"
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
ИНДИКАТОР ЦЕЛИ - ГОРОДА
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
ИНДИКАТОР ЦЕЛИ - РАКЕТНОЙ БАЗЫ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ЦЕЛЬ РАКЕТЫ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
РИСОВАТЕЛЬ РАКЕТЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ }
ОПРЕДЕЛЕНИЕ УСПЕШНОСТИ ЗАПУСКА
НАВЕДЕНИЕ РАКЕТЫ
ДОБАВЛЕНИЕ РАКЕТ
8. "РАКЕТНЫЙ ЗАЛП"
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ТЕЛЕПАТОР РАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРИЗОНТА
СЧЕТЧИК ВРЕМЕНИ
ТЕЛЕПАТОР ГОРИЗОНТА
9. "ВЗРЫВЫ"
: ТЕЛЕПАТОР РАКЕТЫ
ВЗРЫВ ПО ДОСТИЖЕНИИ ПОВЕРХНОСТИ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
РАДИУС
ПРИРАЩЕНИЕ
РИСОВАТЕЛЬ ВЗРЫВА
ТЕЛЕПАТОР ВЗРЫВА
ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА }
ДОБАВЛЕНИЕ ВЗРЫВА
10. "РАЗРУШЕНИЯ"
: ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
СЧЕТЧИК ГОРОДОВ
: РИСОВАТЕЛЬ ГОРИЗОНТА
GAME OVER
: ТЕЛЕПАТОР РАКЕТЫ
УНИЧТОЖЕНИЕ ЦЕЛИ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН
ИНДИКАТОР ЦЕЛИ - РУИН
РИСОВАТЕЛЬ РУИН
ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН }
ПРЕВРАЩАТЕЛЬ В РУИНЫ
: ЗАПУСК ФОРМЫ
НАЧАЛЬНОЕ ЧИСЛО ГОРОДОВ
11. "ПРОТИВОРАКЕТЫ"
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
КОНЕЧЕАЯ ТОЧКА
СКОРОСТЬ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
РИСОВАТЕЛЬ ПРОТИВОРАКЕТЫ
ТЕЛЕПАТОР ПРОТИВОРАКЕТЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ }
ЗАПУСКАТЕЛЬ ПРОТИВОРАКЕТ
12. "ПОДРЫВ ПРОТИВОРАКЕТ"
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
ДЕТОНАЦИЯ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ДЕТОНАЦИЯ РАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
ДЕТОНАЦИЯ ПРОТИВОРАКЕТЫ
: ТЕЛЕПАТОР ВЗРЫВА
ЗАПУСК ЦЕПНОЙ РЕАКЦИИ
13. "РАЗДЕЛЯЮЩИЕСЯ БОЕГОЛОВКИ"
: ТЕЛЕПАТОР РАКЕТЫ
РАЗДЕЛЕНИЕ БОЕГОЛОВКИ
14. "САМОЛЕТЫ"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
СКОРОСТЬ
РИСОВАТЕЛЬ САМОЛЕТА
ТЕЛЕПАТОР САМОЛЕТА
ДЕТОНАЦИЯ САМОЛЕТА
ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА }
: ТЕЛЕПАТОР ГОРИЗОНТА
ЗАПУСКАТЕЛЬ САМОЛЕТА
15. "УПРАВЛЯЕМАЯ БОМБА"
: ОПРЕДЕЛЕНИЕ ФОРМЫ
{ ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ
РИСОВАТЕЛЬ БОМБЫ
ТЕЛЕПАТОР БОМБЫ
ДЕТОНАЦИЯ БОМБЫ
ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ }
: ТЕЛЕПАТОР ГОРИЗОНТА
ЗАПУСКАТЕЛЬ БОМБЫ
16. "СЧЕТЧИК ОЧКОВ"
: ДЕТОНАЦИЯ РАКЕТЫ
ПОЛУЧЕНИЕ БОНУСА
: ДЕТОНАЦИЯ САМОЛЕТА
ПОЛУЧЕНИЕ БОНУСА
: ДЕТОНАЦИЯ БОМБЫ
ПОЛУЧЕНИЕ БОНУСА
: РИСОВАТЕЛЬ ГОРИЗОНТА
ОТОБРАЖЕНИЕ СЧЕТЧИКА
Я разбил это все на шаги, чтобы была возможность отлаживать программу по частям, после ввода в игру каждой новой "фигуры". Прежде чем перейти к коду, несколько пояснений:
По мере добавления органов пришлось вносить добавления в ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО КЛАССА. Переменные (КООРДИНАТЫ) - для удобства хранения параметров, которые есть почти у всех органов. Подпрограммы (в довесок к ТЕЛЕПАТОРАМ и РИСОВАТЕЛЯМ - ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ и ДЕТОНАЦИЯ) - наоборот, для возможности органам заявить о своем отличии от других (быдло-ООП!). После отладки кода от этих довесков почти ничего не осталось:
: ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
КООРДИНАТЫ
ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ
ДЕТОНАЦИЯ
Плюс, КООРДИНАТЫ почти во всех органах используются по-разному. Оставил в "прародителе" только для экономии места.
Некоторые органы пришлось добавить в ЗАПУСК ФОРМЫ, т.к. они присутствуют в игре с самого начала:
: ЗАПУСК ФОРМЫ
ДОБАВЛЕНИЕ ГОРИЗОНТА - ГОЛОВЫ СПИСКА ОРГАНОВ
ДОБАВЛЕНИЕ ГОРОДОВ
ДОБАВЛЕНИЕ РАКЕТНЫХ БАЗ
НАЧАЛЬНОЕ ЧИСЛО ГОРОДОВ
Не все органы будут иметь особые варианты наследственных подпрограмм. Некоторым будет достаточно "прародительских" заглушек. Кроме того, по мере добавления новых органов приходилось модифицировать подпрограммы старых.
: ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
РИСОВАТЕЛЬ ЛИНИИ ГОРИЗОНТА
ТЕЛЕПАТОР ГОРИЗОНТА
: ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
РИСОВАТЕЛЬ ГОРОДА
ИНДИКАТОР ЦЕЛИ - ГОРОДА
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
РИСОВАТЕЛЬ РАКЕТНОЙ БАЗЫ
ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
ИНДИКАТОР ЦЕЛИ - РАКЕТНОЙ БАЗЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
РИСОВАТЕЛЬ РАКЕТЫ
ТЕЛЕПАТОР РАКЕТЫ
ДЕТОНАЦИЯ РАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
РИСОВАТЕЛЬ ВЗРЫВА
ТЕЛЕПАТОР ВЗРЫВА
: ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН
ИНДИКАТОР ЦЕЛИ - РУИН
РИСОВАТЕЛЬ РУИН
: ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
РИСОВАТЕЛЬ ПРОТИВОРАКЕТЫ
ТЕЛЕПАТОР ПРОТИВОРАКЕТЫ
ДЕТОНАЦИЯ ПРОТИВОРАКЕТЫ
: ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
РИСОВАТЕЛЬ САМОЛЕТА
ТЕЛЕПАТОР САМОЛЕТА
ДЕТОНАЦИЯ САМОЛЕТА
: ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ
РИСОВАТЕЛЬ БОМБЫ
ТЕЛЕПАТОР БОМБЫ
ДЕТОНАЦИЯ БОМБЫ
Переменных же я сыпал, не жалея.
: ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
СЧЕТЧИК ВРЕМЕНИ
СЧЕТЧИК ГОРОДОВ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
КОД БАЗЫ
КОЛИЧЕСТВО РАКЕТ
: ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
ЦЕЛЬ РАКЕТЫ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
РАДИУС
ПРИРАЩЕНИЕ
: ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
КОНЕЧЕАЯ ТОЧКА
СКОРОСТЬ
ПРОЙДЕННАЯ ДОЛЯ ПУТИ
: ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
СКОРОСТЬ
III. ИГРОВЫЕ ИСКЛЮЧЕНИЯ очевидны - это вызванные коллизиями "фигур" изменения списка органов во время телепания: добавление и удаление (превращение реализуется путем их комбинирования). Однако, как в случае с отслеживанием действия пользователя, большинство последствий ИСКЛЮЧЕНИЙ наступают только при следующем телепании. Даже, если речь идет о цепной реакции (при ДЕТОНАЦИИ).
IV. УСЛОВИЙ В ИГРЕ практически нет. Я намеренно их избегал, не желая накгружать игру "спортивной статистикой" в ущерб "аркадности". Остались, пожалуй, только начальные установки (в ЗАГРУЗКЕ ФОРМЫ) - число ГОРОДОВ, РАКЕТНЫХ БАЗ и ПРОТИВОРАКЕТ. Да условие наступления "GAME OVER". Вообще же, ТЕЛЕПАТОР головного органа списка - ГОРИЗОНТА - и занимается отслеживанием происходящих в игре событий.
V. ПАРАМЕТРОВ В ИГРЕ не так уж и много - выше уже поминал размеры окна и частоту тиков. Конечно - скорости "фигур" и вероятности их вступления в игру. Проблема не количестве ПАРАМЕТРОВ, а в их подборе для наибольшей играбельности. На момент написания этих строк, я еще не нашел удачного сочетания.
VI. ФОРМУЛЫ ИГРЫ настолько очевидны, что я, по большей части, реализовал их прямо в ТЕЛЕПАТОРАХ (и ДЕТОНАЦИИ). Отдельно вынес, по большей части подпрограммы нужные ЗАПУСКУ ФОРМЫ, пару другу общих мест и куски, нуждающиеся в тонкой настройке. Как и ПАРАМЕТРЫ, их еще надо пошлифовать.
: ОПРЕДЕЛЕНИЕ ФОРМЫ
ДОБАВЛЯТОР ГОРОДА
ДОБАВЛЯТОР РАКЕТНОЙ БАЗЫ
ОПРЕДЕЛЕНИЕ УСПЕШНОСТИ ЗАПУСКА
НАВЕДЕНИЕ РАКЕТЫ
ДОБАВЛЕНИЕ РАКЕТ
ДОБАВЛЕНИЕ ВЗРЫВА
ПРЕВРАЩАТЕЛЬ В РУИНЫ
ЗАПУСКАТЕЛЬ ПРОТИВОРАКЕТ
VII. НЕОДНОРОДНОСТИ ИГРЫ? Ну, как бы, цель данной работы, как раз, показать легкость добавления в игру любых желаемых "фигур" со своими правилами. Видно, что при достаточно простых правилах игры и очень простой синхронизации игровых событий добавлять в игру новые "фичи" можно без проблем.
...
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
Программа целиком:
- Код:
Imports System ''' СКЕЛЕТ. ИНСТРУМЕНТАРИЙ
Imports System.Drawing
Imports System.Windows.Forms
Public Class Form1 ''' СКЕЛЕТ. { ОПРЕДЕЛЕНИЕ ФОРМЫ
Inherits Form
Dim Timer1 As Timer ''' СКЕЛЕТ. ТАЙМЕР
Shared MouseX As Integer, MouseY As Integer ''' РЕЦЕПТОРЫ...
Shared BaseN As Integer ''' ... КООРДИНАТЫ МЫШИ, НОМЕР КЛАВИШИ
Public class Thing ''' ОРГАНЫ. { ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА
Public Younger As Thing, Older As Thing ''' ОРГАНЫ. ССЫЛКИ НА БРАТЬЕВ
Public X As Integer, Y As Integer ''' ГОРОДА. КООРДИНАТЫ
Public Overridable Sub Draw(e As PaintEventArgs) ''' ОРГАНЫ...
End Sub ''' ... ВИРТУАЛЬНЫЙ РИСОВАТЕЛЬ
Public Overridable Sub Tick ''' ОРГАНЫ. ВИРТУАЛЬНЫЙ ТЕЛЕПАТОР
End Sub
Public Overridable Function IsTarget() As Integer ''' ВРАЖЬИ РАКЕТЫ...
Return 0 ''' ВИРТУАЛЬНЫЙ ИНДИКАТОР ЦЕЛИ
End Function
Public Overridable Sub Detonate(x0 As Integer, y0 As Integer _
, r0 As Integer, first As Boolean) ''' ПОДРЫВ ПРОТИВОРАКЕТ. ДЕТОНАЦИЯ
End Sub
End Class ''' ОРГАНЫ. ОПРЕДЕЛЕНИЕ АБСТРАКТНОГО ОРГАНА }
Shared Head As Thing ''' ОРГАНЫ. ГОЛОВА СПИСКА
Shared Sub Link(current As Thing) ''' ОРГАНЫ. ДОБАВЛЯТОР ОРГАНА
current.Younger = Head.Younger: Head.Younger = current: current.Older = Head
If current.Younger IsNot Nothing Then current.Younger.Older = current
End Sub
Shared Sub Unlink(current As Thing) ''' ОРГАНЫ. УДАЛЯТОР ОРГАНА
current.Older.Younger = current.Younger
If current.Younger IsNot Nothing Then current.Younger.Older = current.Older
current.Older = Nothing
End Sub
Public class Horizon ''' ГОРИЗОНТ. { ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА
Inherits Thing
Dim Time As Integer ''' РАКЕТНЫЙ ЗАЛП. СЧЕТЧИК ВРЕМЕНИ
Public Game As Integer, Score As Integer ''' РАЗРУШЕНИЯ. СЧЕТЧИК ГОРОДОВ
Public Overrides Sub Draw(e As PaintEventArgs) ''' ГОРИЗОНТ...
Dim p = new Pen(Color.Black, 1) ''' ... { РИСОВАТЕЛЬ ЛИНИИ ГОРИЗОНТА
e.Graphics.DrawLine(p, 0, 459, 639, 459)
Dim b As New SolidBrush(Color.Black)
If Game < 1 Then ''' РАЗРУШЕНИЯ. GAME OVER
Dim f As New Font("Arial", 22)
e.Graphics.DrawString("GAME OVER", f, b, 150, 150)
End If
Dim f2 As New Font("Arial", 14) ''' СЧЕТЧИК ОЧКОВ. ОТОБРАЖЕНИЕ СЧЕТЧИКА
e.Graphics.DrawString(CStr(Score), f2, b, 600, 10)
End Sub ''' ГОРИЗОНТ. РИСОВАТЕЛЬ ЛИНИИ ГОРИЗОНТА }
Public Overrides Sub Tick ''' РАКЕТНЫЙ ЗАЛП. { ТЕЛЕПАТОР ГОРИЗОНТА
If Game=0 Then Return
Attack(Start(2, (Time Mod 3)=0, False), 0, 0)
If Start(1, True, True) Then ''' САМОЛЕТЫ. { ЗАПУСКАТЕЛЬ САМОЛЕТА
Dim Current = New Plane
Current.X = 0
Current.Y = Int(Rnd()*200)+50
Current.Horizontal = 3
Current.Vertical = 0
Link(Current)
End If ''' САМОЛЕТЫ. ЗАПУСКАТЕЛЬ САМОЛЕТА }
If Start(1, True, True) Then ''' УПРАВЛЯЕМАЯ БОМБА. { ЗАПУСКАТЕЛЬ БОМБЫ
Dim Current As Bomb
Current = new Bomb
Current.X = Int(640*Rnd())
Current.Y = 0
Current.Target = ToTarget()
Link(Current)
End If ''' УПРАВЛЯЕМАЯ БОМБА. ЗАПУСКАТЕЛЬ БОМБЫ }
Time = Time + 1
End Sub ''' РАКЕТНЫЙ ЗАЛП. ТЕЛЕПАТОР ГОРИЗОНТА }
End Class ''' ГОРИЗОНТ. ОПРЕДЕЛЕНИЕ ОРГАНА - ЛИНИИ ГОРИЗОНТА }
Public class City ''' ГОРОДА. { ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА
Inherits Thing
Public Overrides Sub Draw(e As PaintEventArgs) ''' ГОРОДА. { РИСОВАТЕЛЬ ГОРОДА
Dim myPen = new Pen(Color.Red, 1)
e.Graphics.DrawRectangle(myPen, X-7, Y-3, 15, 5)
e.Graphics.DrawRectangle(myPen, X+1, Y-8, 4, 10)
e.Graphics.DrawLine(myPen, X-5, Y-6, X-5, Y-3)
End Sub ''' ГОРОДА. РИСОВАТЕЛЬ ГОРОДА }
Public Overrides Function IsTarget() As Integer ''' ВРАЖЬИ РАКЕТЫ...
Return 1 ''' ИНДИКАТОР ЦЕЛИ - ГОРОДА
End Function
End Class ''' ГОРОДА. ОПРЕДЕЛЕНИЕ ОРГАНА - ГОРОДА }
Public class Base ''' РАКЕТНЫЕ БАЗЫ { ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ
Inherits Thing
Public Code As Integer ''' РАКЕТНЫЕ БАЗЫ. КОД БАЗЫ
Public Missiles As Integer ''' РАКЕТНЫЕ БАЗЫ. КОЛИЧЕСТВО РАКЕТ
Public Overrides Sub Draw(e As PaintEventArgs) ''' РАКЕТНЫЕ БАЗЫ...
Dim myPen = new Pen(Color.Red, 1) ''' ... { РИСОВАТЕЛЬ РАКЕТНОЙ БАЗЫ
Dim I As Integer, X0 As Integer, Y0 As Integer: For I = 1 To Missiles
Select Case I
case 1: X0 = 6: Y0 = 3
case 2: X0 = 4: Y0 = 3
case 3: X0 = 2: Y0 = 3
case 4: X0 = 0: Y0 = 3
case 5: X0 = 5: Y0 = 2
case 6: X0 = 3: Y0 = 2
case 7: X0 = 1: Y0 = 2
case 8: X0 = 4: Y0 = 1
case 9: X0 = 2: Y0 = 1
case 10: X0 = 3: Y0 = 0
End Select
X0 = X + X0*5 - 15: Y0 = Y + Y0*10 - 25
e.Graphics.DrawLine(myPen, X0, Y0, X0, Y0+5)
e.Graphics.DrawLine(myPen, X0, Y0, X0-1, Y0+1)
e.Graphics.DrawLine(myPen, X0, Y0, X0+1, Y0+1)
e.Graphics.DrawLine(myPen, X0-2, Y0+5, X0+2, Y0+5)
e.Graphics.DrawLine(myPen, X0-2, Y0+4, X0-2, Y0+6)
e.Graphics.DrawLine(myPen, X0+2, Y0+4, X0+2, Y0+6)
Next
End Sub ''' РАКЕТНЫЕ БАЗЫ. РИСОВАТЕЛЬ РАКЕТНОЙ БАЗЫ }
Public Overrides Sub Tick ''' ПРОТИВОРАКЕТЫ. { ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ
Dim H As Horizon
H = Head
If (H.Game = 0) Or (BaseN <> Code) Then Return
If Missiles > 0 Then
Missiles = Missiles - 1
Launch(Me)
End If
BaseN = 0
End Sub ''' ПРОТИВОРАКЕТЫ. ТЕЛЕПАТОР РАКЕТНОЙ БАЗЫ }
Public Overrides Function IsTarget() As Integer ''' ВРАЖЬИ РАКЕТЫ...
Return 2 ''' ... ИНДИКАТОР ЦЕЛИ - РАКЕТНОЙ БАЗЫ
End Function
End Class ''' РАКЕТНЫЕ БАЗЫ" ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТНОЙ БАЗЫ }
Public class Missile ''' ВРАЖЬИ РАКЕТЫ. { ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ
Inherits Thing
Public Target As Thing ''' ВРАЖЬИ РАКЕТЫ. ЦЕЛЬ РАКЕТЫ
Public Percent As Single ''' ВРАЖЬИ РАКЕТЫ. ПРОЙДЕННАЯ ДОЛЯ ПУТИ
Public Overrides Sub Draw(e As PaintEventArgs) ''' ВРАЖЬИ РАКЕТЫ...
Dim myPen = new Pen(Color.Blue, 1) ''' ... РИСОВАТЕЛЬ РАКЕТЫ
e.Graphics.DrawLine(myPen, X, Y _
, X+Int((Target.X-X)*Percent), Y+Int((459-Y)*Percent))
End Sub
Public Overrides Sub Tick ''' РАКЕТНЫЙ ЗАЛП. { ТЕЛЕПАТОР РАКЕТЫ
Percent = Percent+0.01
If Percent > 1 Then ''' ВЗРЫВЫ. ВЗРЫВ ПО ДОСТИЖЕНИИ ПОВЕРХНОСТИ
Unlink(Me): Boom(Target.X,459)
Destroy(Target) ''' РАЗРУШЕНИЯ. УНИЧТОЖЕНИЕ ЦЕЛИ
Else
Attack(Start(2, Percent < 0.75 And Percent > 0.25, True) _
, X+Int((Target.X-X)*Percent), Y+Int((459-Y)*Percent))''' РАЗДЕЛЯЮЩИЕСЯ...
End If ''' ... БОЕГОЛОВКИ. РАЗДЕЛЕНИЕ БОЕГОЛОВКИ
End Sub ''' РАКЕТНЫЙ ЗАЛП. ТЕЛЕПАТОР РАКЕТЫ }
Public Overrides Sub Detonate(x0 As Integer, y0 As Integer _
, r0 As Integer, first As Boolean) ''' ПОДРЫВ ПРОТИВОРАКЕТ...
Dim x1 = X+Int((Target.X-X)*Percent) ''' ... { ДЕТОНАЦИЯ РАКЕТЫ
Dim y1 = Y+Int((459-Y)*Percent)
If (x1-x0)^2+(y1-y0)^2<r0^2 Then
Boom(x1, y1): Unlink(Me)
Dim H As Horizon ''' СЧЕТЧИК ОЧКОВ. ПОЛУЧЕНИЕ БОНУСА
H = Head: H.Score = H.Score+1
End If
End Sub ''' ПОДРЫВ ПРОТИВОРАКЕТ. ДЕТОНАЦИЯ РАКЕТЫ }
End Class ''' ВРАЖЬИ РАКЕТЫ. ОПРЕДЕЛЕНИЕ ОРГАНА - РАКЕТЫ }
Public class Blast ''' ВЗРЫВЫ. { ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА
Inherits Thing
Public Radius As Integer, Delta As Integer ''' ВЗРЫВЫ. РАДИУС, ПРИРАЩЕНИЕ
Public Overrides Sub Draw(e As PaintEventArgs) ''' { ВЗРЫВЫ...
Dim myPen = new Pen(Color.Brown, 1) ''' РИСОВАТЕЛЬ ВЗРЫВА
e.Graphics.DrawEllipse(myPen, X-Radius, Y-Radius, 2*Radius, 2*Radius)
End Sub
Public Overrides Sub Tick ''' ВЗРЫВЫ. { ТЕЛЕПАТОР ВЗРЫВА
Dim Current As Thing
Radius = Radius + Delta
Current = Head.Younger: While Current IsNot Nothing ''' ПОДРЫВ ПРОТИВОРАКЕТ...
Current.Detonate(X, Y, Radius, Delta > 0) ''' ... ЗАПУСК ЦЕПНОЙ РЕАКЦИИ
Current = Current.Younger: End While
If Radius > 30 Then
Delta = -Delta
ElseIf Radius < 0 Then
Unlink(Me)
End If
End Sub ''' ВЗРЫВЫ. ТЕЛЕПАТОР ВЗРЫВА }
End Class ''' ВЗРЫВЫ. ОПРЕДЕЛЕНИЕ ОРГАНА - ВЗРЫВА }
Public class Ruins ''' РАЗРУШЕНИЯ. { ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН
Inherits Thing
Public Overrides Sub Draw(e As PaintEventArgs) ''' РАЗРУШЕНИЯ...
Dim myPen = new Pen(Color.Brown, 1) ''' ... РИСОВАТЕЛЬ РУИН
e.Graphics.DrawEllipse(myPen, X-10, Y-5, 20, 10)
e.Graphics.DrawLine(myPen, X-7, Y-3, X+7, Y+3)
e.Graphics.DrawLine(myPen, X+7, Y-3, X-7, Y+3)
End Sub
Public Overrides Function IsTarget() As Integer ''' РАЗРУШЕНИЯ...
Return 3 ''' ... ИНДИКАТОР ЦЕЛИ - РУИН
End Function
End Class ''' РАЗРУШЕНИЯ. ОПРЕДЕЛЕНИЕ ОРГАНА - РУИН }
Public class Rocket ''' ПРОТИВОРАКЕТЫ. { ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ
Inherits Thing
Public X2 As Integer ''' ПРОТИВОРАКЕТЫ. КОНЕЧЕАЯ ТОЧКА,..
Public Percent As Single ''' ... ПРОЙДЕННАЯ ДОЛЯ ПУТИ
Public Velocity As Single ''' ... СКОРОСТЬ
Public Overrides Sub Draw(e As PaintEventArgs) ''' ПРОТИВОРАКЕТЫ...
Dim myPen = new Pen(Color.Red, 1) ''' РИСОВАТЕЛЬ ПРОТИВОРАКЕТЫ
e.Graphics.DrawLine(myPen, X, 459, X+Int((X2-X)*Percent) _
, 459+Int((Y-459)*Percent))
End Sub
Public Overrides Sub Tick ''' ПРОТИВОРАКЕТЫ. { ТЕЛЕПАТОР ПРОТИВОРАКЕТЫ
Percent = Percent+Velocity
If Percent > 1 Then Unlink(Me): Boom(X2,Y)
End Sub ''' ПРОТИВОРАКЕТЫ. ТЕЛЕПАТОР ПРОТИВОРАКЕТЫ }
Public Overrides Sub Detonate(x0 As Integer, y0 As Integer _
, r0 As Integer, first As Boolean) ''' ПОДРЫВ ПРОТИВОРАКЕТ...
Dim x1 = X+Int((X2-X)*Percent) ''' ... ДЕТОНАЦИЯ ПРОТИВОРАКЕТЫ
Dim y1 = 459+Int((Y-459)*Percent)
If (x1-x0)^2+(y1-y0)^2<r0^2 Then Boom(x1, y1): Unlink(Me)
End Sub ''' ПОДРЫВ ПРОТИВОРАКЕТ. ДЕТОНАЦИЯ ПРОТИВОРАКЕТЫ }
End Class ''' ПРОТИВОРАКЕТЫ. ОПРЕДЕЛЕНИЕ ОРГАНА - ПРОТИВОРАКЕТЫ }
Public class Plane ''' САМОЛЕТЫ. { ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА
Inherits Thing
Public Horizontal As Single, Vertical As Single ''' САМОЛЕТЫ. СКОРОСТЬ
Public Overrides Sub Draw(e As PaintEventArgs) ''' САМОЛЕТЫ...
Dim myPen = new Pen(Color.Blue, 1) ''' ... { РИСОВАТЕЛЬ САМОЛЕТА
e.Graphics.DrawLine(myPen, X-5, Y, X+5, Y)
e.Graphics.DrawLine(myPen, X+2, Y-4, X+2, Y+4)
e.Graphics.DrawLine(myPen, X-1, Y-2, X+4, Y-2)
e.Graphics.DrawLine(myPen, X-1, Y+2, X+4, Y+2)
e.Graphics.DrawLine(myPen, X-4, Y-2, X-4, Y+2)
End Sub ''' САМОЛЕТЫ. РИСОВАТЕЛЬ САМОЛЕТА }
Public Overrides Sub Tick ''' САМОЛЕТЫ. { ТЕЛЕПАТОР САМОЛЕТА
Horizontal = Horizontal + (Rnd()-0.5)*0.2
Vertical = Vertical + (Rnd()-0.5)*0.2
X = X + Int(Horizontal)
Y = Y + Int(Vertical)
If (Y > 459) Or (Y < 0) Or (X < 0) Or (X > 639) Then
Unlink(Me)
Else
Attack(Start(3, True, true), X + int(Horizontal), Y)
End If
End Sub ''' САМОЛЕТЫ. ТЕЛЕПАТОР САМОЛЕТА }
Public Overrides Sub Detonate(x0 As Integer, y0 As Integer _
, r0 As Integer, first As Boolean) ''' САМОЛЕТЫ. { ДЕТОНАЦИЯ САМОЛЕТА
If (X-x0)^2+(Y-y0)^2<r0^2 Then
Boom(X, Y)
Unlink(Me)
Dim H As Horizon ''' СЧЕТЧИК ОЧКОВ. ПОЛУЧЕНИЕ БОНУСА
H = Head: H.Score = H.Score+3
End If
End Sub ''' САМОЛЕТЫ. ДЕТОНАЦИЯ САМОЛЕТА }
End Class ''' САМОЛЕТЫ. ОПРЕДЕЛЕНИЕ ОРГАНА - САМОЛЕТА }
Public class Bomb ''' УПРАВЛЯЕМАЯ БОМБА. { ОПРЕДЕЛЕНИЕ ОРГАНА - БОМБЫ
Inherits Thing
Public Target As Thing
Public Overrides Sub Draw(e As PaintEventArgs)
Dim myPen = new Pen(Color.Blue, 1)
e.Graphics.DrawEllipse(myPen, X-3, Y-3, 6, 6)
End Sub
Public Overrides Sub Tick ''' УПРАВЛЯЕМАЯ БОМБА. { ТЕЛЕПАТОР БОМБЫ
Y = Y + 5
X = X + (Target.X-X)/(459-Y)*5
If Y > 459 Then
Destroy(Target)
Unlink(Me)
Boom(Target.X,459)
End If
End Sub ''' УПРАВЛЯЕМАЯ БОМБА. ТЕЛЕПАТОР БОМБЫ }
Public Overrides Sub Detonate(x0 As Integer, y0 As Integer _
, r0 As Integer, first As Boolean) ''' УПРАВЛЯЕМАЯ БОМБА. { ДЕТОНАЦИЯ БОМБЫ
Dim R2 = (X-x0)^2+(Y-y0)^2
If R2 < 1000 And first Then
X = X + iif(X > x0, 10, -10)
Y = Y + iif(Y > y0, 10, -10)
End If
If R2<r0^2 Then
Boom(X, Y)
Unlink(Me)
Dim H As Horizon ''' СЧЕТЧИК ОЧКОВ. ПОЛУЧЕНИЕ БОНУСА
H = Head: H.Score = H.Score+2
End If
End Sub ''' УПРАВЛЯЕМАЯ БОМБА. ДЕТОНАЦИЯ БОМБЫ }
End Class
Public Sub New ''' СКЕЛЕТ. { ЗАПУСК ФОРМЫ
ClientSize = New Size(640, 480) ''' СКЕЛЕТ. УСТАНОВКА РАЗМЕРА
DoubleBuffered = True ''' СКЕЛЕТ. УСТАНОВКА ДВОЙНОЙ БУФЕРИЗАЦИИ
Timer1 = New Timer ''' СКЕЛЕТ. СВЯЗЫВАНИЕ ТАЙМЕРА с ТЕЛЕПАТОРОМ ФОРМЫ
Timer1.Interval = 100
AddHandler Timer1.Tick, AddressOf Timer1_Tick
Timer1.Start
Randomize() ''' СКЕЛЕТ. ЗАПУСК РАНДОМИЗАТОРА
Head = new Horizon ''' ГОРИЗОНТ. ДОБАВЛЕНИЕ ГОРИЗОНТА - ГОЛОВЫ СПИСКА ОРГАНОВ
Dim H As Horizon: H = Head ''' РАЗРУШЕНИЯ. НАЧАЛЬНОЕ ЧИСЛО ГОРОДОВ
H.Game = 6: H.Score = 0
Builds(2): Builds(3): Builds(4) ''' ГОРОДА. ДОБАВЛЕНИЕ ГОРОДОВ
Builds(6): Builds(7): Builds(8)
Defense(1, 1) ''' РАКЕТНЫЕ БАЗЫ. ДОБАВЛЕНИЕ РАКЕТНЫХ БАЗ
Defense(5, 2): Defense(9, 3)
End Sub ''' СКЕЛЕТ. ЗАПУСК ФОРМЫ }
Sub Timer1_Tick ''' СКЕЛЕТ. { ТЕЛЕПАТОР ФОРМЫ
Dim Current As Thing ''' ОРГАНЫ. ЗАПУСК ТЕЛЕПАТОРОВ ОРГАНОВ
Current = Head
While Current IsNot Nothing: Current.Tick
Current = Current.Younger: End While
Invalidate ''' СКЕЛЕТ. ЗАПУСК РИСОВАТЕЛЯ ФОРМЫ
End Sub ''' СКЕЛЕТ. ТЕЛЕПАТРОР ФОРМЫ }
Protected Overrides Sub OnPaint(e As PaintEventArgs) ''' СКЕЛЕТ...
MyBase.OnPaint(e) ''' ... { РИСОВАТЕЛЬ ФОРМЫ
Dim Current As Thing ''' ОРГАНЫ. ЗАПУСК РИСОВАТЕЛЕЙ ОРГАНОВ
Current = Head
While Current IsNot Nothing: Current.Draw(e)
Current = Current.Younger: End While
End Sub ''' СКЕЛЕТ. РИСОВАТЕЛЬ ФОРМЫ }
Protected Overrides Sub OnMouseMove(e As MouseEventArgs) ''' РЕЦКПТОРЫ...
MouseX = e.X: MouseY = e.Y ''' ... РЕАКЦИЯ НА ПЕРЕМЕЩЕНИЕ МЫШКИ
End Sub
Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs) ''' РЕЦКПТОРЫ...
BaseN = Asc(e.KeyChar) And 3 ''' ... РЕАКЦИЯ НА НАЖАТИЕ КЛАВИШИ
End Sub
Public Sub Builds(number As Integer) ''' ГОРОДА. { ДОБАВЛЯТОР ГОРОДА
Dim Current = new City
Current.X = 64*number: Current.Y = 459
Link(Current)
End Sub ''' ГОРОДА. ДОБАВЛЯТОР ГОРОДА }
Public Sub Defense(number As Integer, code As Integer) ''' РАКЕТНЫЕ БАЗЫ...
Dim Current = New Base ''' { ДОБАВЛЯТОР РАКЕТНОЙ БАЗЫ
Current.X = 64*number: Current.Y = 459
Current.Missiles = 10: Current.Code = code
Link(Current)
End Sub ''' РАКЕТНЫЕ БАЗЫ. ДОБАВЛЯТОР РАКЕТНОЙ БАЗЫ }
Shared Function Start (number As Integer, count As Boolean _
, rare As Boolean) As Integer ''' ВРАЖЬИ РАКЕТЫ. ОПРЕДЕЛЕНИЕ УСПЕШНОСТИ...
If count And (Rnd() < iif(rare, 0.003, 0.1)) Then Return Int(Rnd()*number)+1
Return 0 ''' ... ЗАПУСКА
End Function
Shared Function ToTarget() As Thing ''' ВРАЖЬИ РАКЕТЫ. { НАВЕДЕНИЕ РАКЕТЫ
Dim T As Integer, C1 As Thing, C2 As Thing
T = 1: C1 = Nothing: C2 = Head.Younger
While C2 IsNot Nothing
If C2.IsTarget() Then
if Rnd() < 1.0/T Then C1 = C2
T = T+1
End If
C2 = C2.Younger: End While
Return C1
End Function ''' ВРАЖЬИ РАКЕТЫ. НАВЕДЕНИЕ РАКЕТЫ }
Shared Sub Attack(number As Integer, x0 as Integer, y0 as Integer)
Dim I As Integer, Current As Missile ''' ВРАЖЬИ РАКЕТЫ. { ДОБАВЛЕНИЕ РАКЕТ
For I = 1 To number
Current = New Missile
If x0 Then
Current.X = x0: Current.Y = y0: Current.Percent = 0
Else
Current.X = Int(640*Rnd()): Current.Y = 0: Current.Percent = 0
End If
Link(Current): Current.Target = ToTarget()
Next I
End Sub ''' ВРАЖЬИ РАКЕТЫ. ДОБАВЛЕНИЕ РАКЕТ }
Shared Sub Boom(x0 As Integer, y0 As Integer) ''' ВЗРЫВЫ. { ДОБАВЛЕНИЕ ВЗРЫВА
Dim Current = new Blast
Current.X = x0: Current.Y = y0: Current.Radius = 5: Current.Delta = 5
Link(Current)
End Sub ''' ВЗРЫВЫ. ДОБАВЛЕНИЕ ВЗРЫВА }
Shared Sub Destroy(target As Thing) ''' РАЗРУШЕНИЯ. { ПРЕВРАЩАТЕЛЬ В РУИНЫ
if target.Older IsNot Nothing Then
If target.IsTarget() = 1 Then
Dim H As Horizon: H = Head: H.Game = H.Game - 1
End If
If target.IsTarget() <> 3 Then
Dim Current = new Ruins
Current.X = target.X: Current.Y = target.Y
Unlink(target)
Link(Current)
End If
End If
End Sub ''' РАЗРУШЕНИЯ. ПРЕВРАЩАТЕЛЬ В РУИНЫ }
Shared Sub Launch(current As Base) ''' ПРОТИВОРАКЕТЫ...
Dim Current2 As Rocket ''' ... { ЗАПУСКАТЕЛЬ ПРОТИВОРАКЕТ
Current2 = new Rocket
Current2.X = current.X: Current2.X2 = MouseX: Current2.Y = MouseY
Current2.Percent = 0
Current2.Velocity = iif(current.Code = 2, 0.3, 0.1)
Link(Current2)
End Sub ''' ПРОТИВОРАКЕТЫ. ЗАПУСКАТЕЛЬ ПРОТИВОРАКЕТ }
<STAThread()> Shared Sub Main() ''' СКЕЛЕТ. ЗАПУСК ПРОГРАММЫ
Application.Run(New Form1)
End Sub
End Class ''' СКЕЛЕТ. ОПРЕДЕЛЕНИЕ ФОРМЫ }
Последний раз редактировалось: Gudleifr (Вс Авг 18, 2019 3:08 pm), всего редактировалось 1 раз(а)
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
Что мешает воспроизведению этого результата на своем компьютере непрограммисту?
Во-первых, мы видим, что порядок фрагментов кода при обсуждении программы и в готовом тексте сильно отличаются. Очень трудно отследить связь одного с другим.
Во-вторых, для того, чтобы поиграть с программой пользователь должен знать Visual Basic. И не столько те команды, которые что-то делают, но, наоборот, всякие условности: что можно писать на одной строке, а что нельзя, с чего надо начинать и чем заканчивать, что тут выполняется, а что запоминается или просто объявляется..?
Причем, знание этих особенностей Visual Basic в случае .NET - чистая условность. С тем же успехом я мог писать, например на C#. Балет другой, но настолько же пустопорожний... Все равно, с обоих языков все будет переведено в язык CIL (этакий ОО-язык ассемблера). Так может этот CIL и есть настоящий BASIC современного МастДая?
Будем разбираться...
Кто и с кем тут взаимодействует?
В наличии два пользователя - ПРОГРАММИСТ и ИГРОК.
ПРОГРАММИСТ лупит по клавишам, посылая ТЕКСТОВОЕ сообщение редактору-БЛОКНОТУ, который преобразует его в два новых - ПРОГРАММУ и СКРИПТ (второе - только для удобства).
СКРИПТ:
СКРИПТ передается интерпретатору ОПЕРАЦИОННОЙ СИСТЕМЫ, который организует вызов компилятора БЕЙСИКА и запуск РЕЗУЛЬТАТА компиляции ПРОГРАММЫ на исполнение, ведя при этом простейший обмен сообщениями о ГОТОВНОСТИ с ПРОГРАММИСТОМ. Загрузчик РЕЗУЛЬТАТА живет внутри ОПЕРАЦИОННОЙ СИСТЕМЫ.
РЕЗУЛЬТАТ-игра (процесс-интерпретатор) обменивается ИГРОВЫМИ сообщениями с ИГРОКОМ. На самом деле, все сложнее, т.к. РЕЗУЛЬТАТ включает в себя .NET-интерпретатор и загрузчик ПРОМЕЖУТОЧНОГО РЕЗУЛЬТАТА.
Очевидные семафоры-ОСТАНОВЫ (pause) включены в СКРИПТ для организации его диалога с ПРОГРАММИСТОМ и ожидания компиляции/исполнения.
Файловый канал ДИРЕКТОРИЙ, очевидно используется для зацикливания работы и сохранения ПРОГРАММЫ и СКРИПТА.
Таблиц много. Больше, чем перечисленных сообщений. Например, для написания ПРОГРАММЫ нужны три - собственно, языка программирования, оо-конструкций .NET (все эти Public и Overrides) и операций с окошками .NET. Есть и менее очевидные, например, список файлов ДИРЕКТОРИЯ.
Конфликты и несуразности встречаются на каждом шагу. Антивирус, например, болезненно реагирует на передачу РЕЗУЛЬТАТА через ДИРЕКТОРИЙ, считая его вирусом из-за .NET-интерпретатора, спрятанного внутри.
Конечно, начинающему программисту проще запомнить только опорные точки этого цикла, считая, что все остальное работает и без его участия (особенно, ели кто-то ему уже все настроил). Но, если вы хотите получить от автоматизации своей работы выгоду, нужно изучать основы Операционных Систем. Это гораздо важнее, чем выучить какой-либо язык программирования.
Как-то это противоречит тому, что я писал про "нам нужны только простые штуки" для игры в солдатики? Да. Но должны же мы хоть что-то понимать в машине, на которой работаем?
***
P.S. В ТЕМА #43, АБЗАЦ #419 я предлагал написать простенькую реал-таймовую игрушку, для тренировки в написании процедуры ОК. Так вот, предлагаемая программа таковой не является. Конечно, сообщения от мышки и клавиатуры, я проверяю в одной точке программы (на каждом тике таймера), однако другие "сообщения" - от взрывов и попаданий ракет - через эту точку не проходят... Пока программа маленькая, это работает, но если мы ее будем усложнять и, более того, разрешать это делать пользователю, придется упрощать структуру взаимодействия. Надо будет проводить весь обсчет событий в цикле перебора игровых предметов на каждом ходу, без их взаимодействий иначе, чем через передачу сообщений. Потребуется создать очередь сообщений: "клавиша", "взрыв", "попадание"...
Во-первых, мы видим, что порядок фрагментов кода при обсуждении программы и в готовом тексте сильно отличаются. Очень трудно отследить связь одного с другим.
Во-вторых, для того, чтобы поиграть с программой пользователь должен знать Visual Basic. И не столько те команды, которые что-то делают, но, наоборот, всякие условности: что можно писать на одной строке, а что нельзя, с чего надо начинать и чем заканчивать, что тут выполняется, а что запоминается или просто объявляется..?
Причем, знание этих особенностей Visual Basic в случае .NET - чистая условность. С тем же успехом я мог писать, например на C#. Балет другой, но настолько же пустопорожний... Все равно, с обоих языков все будет переведено в язык CIL (этакий ОО-язык ассемблера). Так может этот CIL и есть настоящий BASIC современного МастДая?
Будем разбираться...
Кто и с кем тут взаимодействует?
В наличии два пользователя - ПРОГРАММИСТ и ИГРОК.
ПРОГРАММИСТ лупит по клавишам, посылая ТЕКСТОВОЕ сообщение редактору-БЛОКНОТУ, который преобразует его в два новых - ПРОГРАММУ и СКРИПТ (второе - только для удобства).
СКРИПТ:
- Код:
@echo off
C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe ex2.vb | more
pause
ex2.exe
pause
СКРИПТ передается интерпретатору ОПЕРАЦИОННОЙ СИСТЕМЫ, который организует вызов компилятора БЕЙСИКА и запуск РЕЗУЛЬТАТА компиляции ПРОГРАММЫ на исполнение, ведя при этом простейший обмен сообщениями о ГОТОВНОСТИ с ПРОГРАММИСТОМ. Загрузчик РЕЗУЛЬТАТА живет внутри ОПЕРАЦИОННОЙ СИСТЕМЫ.
РЕЗУЛЬТАТ-игра (процесс-интерпретатор) обменивается ИГРОВЫМИ сообщениями с ИГРОКОМ. На самом деле, все сложнее, т.к. РЕЗУЛЬТАТ включает в себя .NET-интерпретатор и загрузчик ПРОМЕЖУТОЧНОГО РЕЗУЛЬТАТА.
Очевидные семафоры-ОСТАНОВЫ (pause) включены в СКРИПТ для организации его диалога с ПРОГРАММИСТОМ и ожидания компиляции/исполнения.
Файловый канал ДИРЕКТОРИЙ, очевидно используется для зацикливания работы и сохранения ПРОГРАММЫ и СКРИПТА.
Таблиц много. Больше, чем перечисленных сообщений. Например, для написания ПРОГРАММЫ нужны три - собственно, языка программирования, оо-конструкций .NET (все эти Public и Overrides) и операций с окошками .NET. Есть и менее очевидные, например, список файлов ДИРЕКТОРИЯ.
Конфликты и несуразности встречаются на каждом шагу. Антивирус, например, болезненно реагирует на передачу РЕЗУЛЬТАТА через ДИРЕКТОРИЙ, считая его вирусом из-за .NET-интерпретатора, спрятанного внутри.
Конечно, начинающему программисту проще запомнить только опорные точки этого цикла, считая, что все остальное работает и без его участия (особенно, ели кто-то ему уже все настроил). Но, если вы хотите получить от автоматизации своей работы выгоду, нужно изучать основы Операционных Систем. Это гораздо важнее, чем выучить какой-либо язык программирования.
Как-то это противоречит тому, что я писал про "нам нужны только простые штуки" для игры в солдатики? Да. Но должны же мы хоть что-то понимать в машине, на которой работаем?
***
P.S. В ТЕМА #43, АБЗАЦ #419 я предлагал написать простенькую реал-таймовую игрушку, для тренировки в написании процедуры ОК. Так вот, предлагаемая программа таковой не является. Конечно, сообщения от мышки и клавиатуры, я проверяю в одной точке программы (на каждом тике таймера), однако другие "сообщения" - от взрывов и попаданий ракет - через эту точку не проходят... Пока программа маленькая, это работает, но если мы ее будем усложнять и, более того, разрешать это делать пользователю, придется упрощать структуру взаимодействия. Надо будет проводить весь обсчет событий в цикле перебора игровых предметов на каждом ходу, без их взаимодействий иначе, чем через передачу сообщений. Потребуется создать очередь сообщений: "клавиша", "взрыв", "попадание"...
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
P.P.S. Раз уж вспомнил о FORTH, попробую классифицировать код программы. ТЕМА #42, АБЗАЦ #409
Начал я, очевидно, с q-кода (псевдокода). Убогого. Гораздо более убогого, чем в первых главах первого тома заметок. С одной стороны, как я указал выше, он плохо переводится в f-код (на Visual Basic). С другой, он и читается-то очень плохо. Почему так получилось? По причине выбора способа программировать - быдлокодерского. Получилось, что не я управляю структурой программы, а WIN API с ее ОО-заморочками.
Дальше... Ну перевели мы q-код в f-код (руками в БЛОКНОТЕ), а дальше? Теперь, родной мастдайный vb.exe создает РЕЗУЛЬТАТ в черт-знает-каком коде. Нас это не волнует (в этом прелесть BASIC-ов). Главная добродетель программиста - понять, когда надо перестать копать в ущерб делу.
Однако, один "высокоуровневый" (на уровне f-кода) момент надо отметить. Т.к. Visual Basic - обычный высокоуроневый язык, то мы вправе ожидать, что ПРОГРАММА будет состоять из r-фрагментов (обычных подпрограмм). Но можно видеть, что в двух самых главных местах ПРОГРАММЫ это избыточно. Телепаторы и Рисователи Органов более естественно выглядели бы в виде а-фрагментов (возвращающих управление в заранее определенную точку - в соответствующий цикл).
Наличие двух "точек возврата" настораживает - получается, что у нас два процесса, которые могут взаимодействовать непредсказуемым образом. Выход тут один. Сделать один из них исключительно читающим. Рисователи должны только использовать данные программы для рисования, но, ни в коем случае, не изменять их.
Конечно, a-код проще r-кода, но воспользоваться этим на языках высокого уровня трудно. На языке ассемблера или FORTH было бы посимпатичнее (да, даже, на старом GW-BASIC)...
Начал я, очевидно, с q-кода (псевдокода). Убогого. Гораздо более убогого, чем в первых главах первого тома заметок. С одной стороны, как я указал выше, он плохо переводится в f-код (на Visual Basic). С другой, он и читается-то очень плохо. Почему так получилось? По причине выбора способа программировать - быдлокодерского. Получилось, что не я управляю структурой программы, а WIN API с ее ОО-заморочками.
Дальше... Ну перевели мы q-код в f-код (руками в БЛОКНОТЕ), а дальше? Теперь, родной мастдайный vb.exe создает РЕЗУЛЬТАТ в черт-знает-каком коде. Нас это не волнует (в этом прелесть BASIC-ов). Главная добродетель программиста - понять, когда надо перестать копать в ущерб делу.
Однако, один "высокоуровневый" (на уровне f-кода) момент надо отметить. Т.к. Visual Basic - обычный высокоуроневый язык, то мы вправе ожидать, что ПРОГРАММА будет состоять из r-фрагментов (обычных подпрограмм). Но можно видеть, что в двух самых главных местах ПРОГРАММЫ это избыточно. Телепаторы и Рисователи Органов более естественно выглядели бы в виде а-фрагментов (возвращающих управление в заранее определенную точку - в соответствующий цикл).
Наличие двух "точек возврата" настораживает - получается, что у нас два процесса, которые могут взаимодействовать непредсказуемым образом. Выход тут один. Сделать один из них исключительно читающим. Рисователи должны только использовать данные программы для рисования, но, ни в коем случае, не изменять их.
Конечно, a-код проще r-кода, но воспользоваться этим на языках высокого уровня трудно. На языке ассемблера или FORTH было бы посимпатичнее (да, даже, на старом GW-BASIC)...
Последний раз редактировалось: Gudleifr (Вс Сен 08, 2024 1:48 pm), всего редактировалось 1 раз(а)
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
КОМПИЛИРОВАТЬ НЕКОМПИЛИРУЕМОЕ
Выше, мы принимали компиляцию, как необходимое зло, производимое демонами Операционной Системы. Мол, пишем на BASIC, запускаем, исправляем ошибки, запускаем снова, возможно, с того же места... Зачем в эту цепочку вставлять еще "компилируем - ждем"? Машине так удобнее и быстрее? А мы за это платить будем?
Пользователю от компиляторов две пользы:
1. Они (точнее их мейкеры и обезьянники) могут отслеживать версии и состав больших проектов. Что-то советовать пользователю, когда он пытается делать совсем уж ерунду. Предлагать свои типовые решения (библиотеки). "Компилятор лучше пользователя знает, что тому надо".
2. Они удобны, когда логика программы, последовательность ее исполнения не соответствует последовательности ручных операций. Программы можно структурировать. Разбивать на функциональные блоки, которые будут "сами" между собой общаться.
В какой-то мере, нам пригодится второе. Нам придется переводить наши данные и программы в ту форму, которая будет понятна ее "исполнителю". "Исполнителем" мы управлять не можем, поэтому придется под него подстраиваться.
***
В одной старой книжке (Хантер, 1984) компилятор представлялся Т-образной фигуркой. Основание - язык, на котором написан компилятор, левое плечо - язык исходной программы, правое - язык на который программа переведена. Из этих фигурок строились цепочки:
Компиляция Паскаль-программы в маш.код, при наличии ассемблера, написанного на маш.коде, Фортран-компилятора на языке ассемблера и Паскаль-компилятора, написанного на Фортране.
И комфортное окружение пользователя компьютера состоит из подходящей коллекции подобных цепочек. Попробуем и мы собрать себе такую.
***
Первое, что будет отличать компиляторы от тех программ, которые мы видели во 2-й части Заметок, сложный нетекстовый формат данных.
Зачем мне такое понадобилось? Для решения вечной проблемы моей странички - ненадежности хранилищ данных. Отказ хостинга, на котором она хранилась ранее, привел к полной "переупаковке чемоданов". Сейчас я жду, что в любой момент случится случайная очистка хранилища картинок форума или файлового хранилища. Нужен какой-то удобный формат хранения "единицы базы данных". Причем, он должен удобно "разархивироваться" в формат Форума (см. визуализатор в первой главе этого тома). Кто помнит, устройство форумного хранилища картинок продиктовало там формат "единицы" - GIF-файл не более 1Мб. Т.к. это очень мало, текст лучше хранить сжатым...
Конечно, наш "компилятор текста в GIF" практически не имеет обязательных для обычных компиляторов главных процедур - анализа, кодогенерации, оптимизации. Он должен уметь только одно - переводить файлы одного формата в другой. Первый формат - обычный текстовый. Такие файлы мы умеем набирать руками. Второй мы руками создать не можем и именно для этого пишем компилятор.
Выше, мы принимали компиляцию, как необходимое зло, производимое демонами Операционной Системы. Мол, пишем на BASIC, запускаем, исправляем ошибки, запускаем снова, возможно, с того же места... Зачем в эту цепочку вставлять еще "компилируем - ждем"? Машине так удобнее и быстрее? А мы за это платить будем?
Пользователю от компиляторов две пользы:
1. Они (точнее их мейкеры и обезьянники) могут отслеживать версии и состав больших проектов. Что-то советовать пользователю, когда он пытается делать совсем уж ерунду. Предлагать свои типовые решения (библиотеки). "Компилятор лучше пользователя знает, что тому надо".
2. Они удобны, когда логика программы, последовательность ее исполнения не соответствует последовательности ручных операций. Программы можно структурировать. Разбивать на функциональные блоки, которые будут "сами" между собой общаться.
В какой-то мере, нам пригодится второе. Нам придется переводить наши данные и программы в ту форму, которая будет понятна ее "исполнителю". "Исполнителем" мы управлять не можем, поэтому придется под него подстраиваться.
***
В одной старой книжке (Хантер, 1984) компилятор представлялся Т-образной фигуркой. Основание - язык, на котором написан компилятор, левое плечо - язык исходной программы, правое - язык на который программа переведена. Из этих фигурок строились цепочки:
Компиляция Паскаль-программы в маш.код, при наличии ассемблера, написанного на маш.коде, Фортран-компилятора на языке ассемблера и Паскаль-компилятора, написанного на Фортране.
И комфортное окружение пользователя компьютера состоит из подходящей коллекции подобных цепочек. Попробуем и мы собрать себе такую.
***
Первое, что будет отличать компиляторы от тех программ, которые мы видели во 2-й части Заметок, сложный нетекстовый формат данных.
Зачем мне такое понадобилось? Для решения вечной проблемы моей странички - ненадежности хранилищ данных. Отказ хостинга, на котором она хранилась ранее, привел к полной "переупаковке чемоданов". Сейчас я жду, что в любой момент случится случайная очистка хранилища картинок форума или файлового хранилища. Нужен какой-то удобный формат хранения "единицы базы данных". Причем, он должен удобно "разархивироваться" в формат Форума (см. визуализатор в первой главе этого тома). Кто помнит, устройство форумного хранилища картинок продиктовало там формат "единицы" - GIF-файл не более 1Мб. Т.к. это очень мало, текст лучше хранить сжатым...
Конечно, наш "компилятор текста в GIF" практически не имеет обязательных для обычных компиляторов главных процедур - анализа, кодогенерации, оптимизации. Он должен уметь только одно - переводить файлы одного формата в другой. Первый формат - обычный текстовый. Такие файлы мы умеем набирать руками. Второй мы руками создать не можем и именно для этого пишем компилятор.
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
I. Поэтому первый инструмент, который будет нам необходим, это "ЗАПИСЫВАТЕЛЬ БИНАРНЫХ ФАЙЛОВ". Это сложно во всех Операционных Системах. Никто не любит, когда пользователь начинает что-то делать сам. Все хотят ограничить его только вводом-выводом текста.
На старом DOS-BASIC, например, это страхолюдие выглядело примерно так:
10 OPEN "R", 1, "DBASE10.GIF"
20 L=(LOF(1)+64)/128
30 FIELD 1, 128 AS S$
40 OPEN "R", 2, "DBASE20.GIF"
50 FIELD 2, 128 AS T$
60 FOR I=1 TO L
70 GET 1
80 LSET T$=S$
90 PUT 2
100 NEXT I
120 CLOSE 1
Тут я делал вид, что работаю с Базой Данных и обманутый BASIC начинал качать бинарные данные "запись" за "записью".
WSH, понятно, тоже "держит марку". Как я писал во втором томе, он и обычный-то текст пытается изувечить. А то, что бывают нетекстовые файлы, для JavaScript - ересь. Пишет строки она, вроде, еще кое-как, а читать "неправильные строки" совсем отказывается. Если только по одному байту за раз. (Решение предлагается "простейшее" - открывать файлы не как файлы, а через специальный объект ADODB, но это слишком окольный путь, т.к. этот объект возвращает байтовый массив в странно упакованном формате).
Посимвольное чтение на JavaScript:
var FSO = WScript.CreateObject("Scripting.FileSystemObject");
var FGIF1 = FSO.OpenTextFile("dbase10.gif", 1, 0);
var FGIF2 = FSO.OpenTextFile("dbase20.gif", 2, true, 0);
var S; while (!FGIF1.AtEndOfStream) {
S = FGIF1.Read(1);
FGIF2.Write(S) }
FGIF1.Close();
FGIF2.Close();
Откуда в GIF-формате "ненормальные строки"? Они состоят из байтов четырех типов:
1. Обычные печатные символы - только в сигнатурах заголовках и дополнительных блоках (во втором томе Заметок я загонял в эти блоки всю базу данных).
2. Байты, в которые упакованы числа, например, размеры картинок и компоненты палитры.
3. Порезанные на байты последовательности битов разной длины - результат работы GIF-архиватора.
4. Прочие байты - флаговые и сигнальные.
II. Так мы плавно переходим к GIF-ФОРМАТУ. Он удобен своей универсальностью. Можно засунуть что угодно и, пока файл не очень велик, файловый хостинг будет считать его обычной картинкой и не требовать для его передачи пользователю танцев с бубном.
GIF-ФАЙЛ состоит из следующих блоков:
- ЗАГОЛОВОК (13) в скобках - размер в байтах
- - СИГНАТУРА(6) = "GIF87a" ИЛИ "GIF89a"
- - ШИРИНА(2)
- - ВЫСОТА(2)
- - ФЛАГИ(1)
- - - ?ПАЛИТРА(.1) цифры с точкой - биты
- - - ЦВЕТОВОЕ РАЗРЕШЕНИЕ(.3)
- - - ОТСОРТИРОВАННАЯ ПО ЧАСТОТЕ ЦВЕТОВ ТАБЛИЦА(.1) = 0(GIF87a)
- - - БИТ НА ЦВЕТ(.3)
- - ЦВЕТ ФОНА(1)
- - АСПЕКТ(1) в GIF87a =0, нужно для выравнивания по размеру экрана?
- ?(?ПАЛИТРА) ПАЛИТРА(3 * 2^(БИТ НА ЦВЕТ + 1))
- {РИСУНОК} фигурные скобки показывают, что таких элементов может быть много
- - ЗАГОЛОВОК (11)
- - - СИГНАТУРА(1) = ','
- - - СЛЕВА(2)
- - - СВЕРХУ(2)
- - - ШИРИНА(2)
- - - ВЫСОТА(2)
- - - ФЛАГИ(1)
- - - - ?ПАЛИТРА(.1)
- - - - ?ЧЕРЕЗПОЛОСНЫЙ(.1) порядок строк: 0,8,.., 4,12,.., 2,6,.., нечетные
- - - - ОТСОРТИРОВАННАЯ ПО ЧАСТОТЕ ЦВЕТОВ ТАБЛИЦА (.1) в GIF87a =0
- - - - 0(.2)
- - - - БИТ НА ЦВЕТ В ПАЛИТРЕ(.3)
- - - БИТ НА ЦВЕТ ДЛЯ КОДИРОВАНИЯ (1) для на цвет (минимально - 2)
- - ?(?ПАЛИТРА) ПАЛИТРА(3 * 2^(БИТ НА ЦВЕТ В ПАЛИТРЕ + 1))
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - БАЙТЫ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ завершает последовательность пакетов
- {РАСШИРЕНИЕ ОБЩЕГО ВИДА} далее будут перечислены конкретные РАСШИРЕНИЯ
- - СИГНАТУРА(1) = "!"
- - КОД(1) по нему определяются типы РАСШИРЕНИЙ
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - БАЙТЫ(ДЛИНА)
- - 0(1) -- ПУСТОЙ ПАКЕТ
- КОНЕЦ
- - СИГНАТУРА(1) = ";"
Конкретные РАСШИРЕНИЯ:
- {РАСШИРЕНИЕ ТЕКСТА}
- - СИГНАТУРА(1) = "!"
- - КОД(1) =1
- - ПАКЕТ заголовка
- - - ДЛИНА(1) =12
- - - СЛЕВА(2)
- - - СВЕРХУ(2)
- - - ШИРИНА(2)
- - - ВЫСОТА(2)
- - - ШИРИНА СИМВОЛА(1)
- - - ВЫСОТА СИМВОЛА(1)
- - - ЦВЕТ ТЕКСТА(1)
- - - ЦВЕТ ФОНА(1)
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - ТЕКСТ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ
- РАСШИРЕНИЕ УПРАВЛЕНИЯ ГРАФИКОЙ
- - СИГНАТУРА(1) = "!"
- - КОД(1) =0xF9
- - {ПАКЕТ} заголовка
- - - ДЛИНА(1) = 4
- - - ФЛАГИ(1)
- - - - 0(.3)
- - - - ПОСЛЕКАДРИЕ(.3) 0 - не указано, 1 - нет, 2 - затереть фоном, 3 - восстановить после себя, 4-7 - не определено
- - - - ПОДТВЕРЖДЕНИЕ(.1) переключение кадров по сигналу пользователя
- - - - ПРОЗРАЧНОСТЬ(.1)
- - - ЗАДЕРЖКА(2) в 1/100 секунды
- - - ПРОЗРАЧНЫЙ ЦВЕТ(1)
- - 0(1) ПУСТОЙ ПАКЕТ
- {РАСШИРЕНИЕ КОММЕНТАРИЯ}
- - СИГНАТУРА(1) = "!"
- - КОД(1) =0xFE
- - ПАКЕТ
- - - ДЛИНА(1)
- - - КОММЕНТАРИЙ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ
- {РАСШИРЕНИЕ ПРИЛОЖЕНИЯ}
- - СИГНАТУРА(1) = "!"
- - КОД(1) =0xFF
- - ПАКЕТ заголовка
- - - ДЛИНА(11)
- - - ИМЯ(8 )
- - - КОНТРОЛЬ ИМЕНИ(3)
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - ТЕКСТ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ
За счет чего GIF универсален? Только за счет того, что файл состоит из набора практически независимых блоков. Обязательны только блок-заголовок, блок-рисунок (чтобы никто не догадался, что это не картинка, а какая-то фигня) и блок-метка-конца. Остальные блоки можно добавлять по вкусу - рисунки,- расширение управления графикой (только одно), расширения комментария, расширения текста, расширения приложения. Такая структура позволяет писать программу по частям - сначала общий пропускатель блоков, затем - по необходимости - распознаватели блоков нужных типов.
К сожалению, как и в случае с Missile Command такой подход сильно утяжеляет программу. Слишком уж много получается фантиков, завернутых в другие фантики, которые можно было бы обойти простыми GOTO.
Подобно разным типам байтов есть разные типы их наборов:
1. Первый байт блока, определяющий его тип (ниже ф-я MB).
2. Обязательные байты, количество зависит от типа (начальная M, MI, MX, ME).
3. Байты палитры, количество определяется обязательными байтами (MF, MP, MN, MA).
4. Первый байт пакета, указывает количество байтов в нем (MR).
5. Байты пакета. Пакет нулевой длины означает конец блока (MS).
Здесь WSH-скрипт программы первичного анализа GIF-файла. Подставить нужное имя картинки вместо dbase.gif; сохранить в файл с расширением wsf. Запускать из Win-консоли при помощи команды cscript (по умолчанию МастДай будет пытаться запускать скрипт при помощи оконного wscript и сдохнет, не найдя консольный Wscript.StdOut):
<job id="GIF">
<script id="BIN.vbs" language="VBScript">
Public Function B(S) ' У JavaScript проблемы с байтами
B=Asc(S)
End Function
</script>
<script id="GIF.js" language="JavaScript">
var T, P, S;
var F = WScript.CreateObject("Scripting.FileSystemObject");
var F1 = F.OpenTextFile("dbase10.gif", 1, 0);
var ME = function () {}; // Достигнут конец GIF
var MR = function () { if(T=B(S)) M = MS; else M = MB; // Новый пакет
/* Сколько байт в пакете? */ WScript.StdOut.WriteLine(T) };
var MS = function () { if (!--T) M = MR }; // Пропуск байтов до нового пакета
var MX = function () { M = MR; // Блок расширения
/* Какое расширение? */ WScript.StdOut.WriteLine(B(S)) };
var MA = function () { if (!--T) M = MR }; // Пропуск палитры рисунка
var MN = function () { P=B(S); T=1; // Проверка палитры рисунка
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MA }
var MI = function () { if (!--T) M = MN }; // Заголовок рисунка
var MB = function () { if (S==";") M = ME; // Новый блок
else if (S==",") { T = 8; M = MI } else M=MX;
/* Какого типа блок? */ WScript.StdOut.WriteLine(S) };
var MP = function () { if (!--T) M = MB }; // Пропуск палитры файла
var MF = function () { P=B(S); T=2; // Проверка палитры файла
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MP }
var M = function () { if (!--T) M = MF }; // Заголовок файла
T = 10;
while (!F1.AtEndOfStream) { S = F1.Read(1); M() }
F1.Close();
</script>
</job>
Какие блоки нам понадобятся? Как сказал выше: заголовок, рисунок-обманка, маркер-конца. А для настоящей информации? Во втором томе заметок я весь текст скинул без преобразования в один блок расширения (типа 2, не используемого в стандарте). Оставлю для совместимости. Для заархивированного текста отведу блок расширения типа 3. Что делать с картинками? Их можно запихивать в GIF "на общих основаниях" - в блоки рисунков.
Никаких межблоковых связей в наш формат вводить не буду. Вообще не буду вводить никаких соглашений на верстку и графическое оформление информации - это будет целиком на совести браузера и, возможно, указаний в тексте.
III. Рассмотрим, как АРХИВИРУЕТСЯ обычный GIF-файл (точнее, картинки в нем). Если мы все сделаем правильно, то любой просмотрщик покажет нашу самодельную картинку как нормальную.
Архивирование проходит в три последовательных этапа:
1) символы превращаются в коды;
2) коды упаковываются в байты;
3) байты разбиваются на ПАКЕТЫ.
Все этапы выглядят одинаково (считая, что символы подаются снаружи один за другим):
1) производятся некоторые начальные установки;
2) символ (код или байт) обрабатывается;
3) проверяется не переполнился ли некий накопитель и, соответственно что-то делается;
4) когда поток исчерпывается, накопители очищаются.
Вот как проходит ОБРАБОТКА СИМВОЛОВ. Используется много переменных - LI, LC, LE, LT, LR, LN, L1, L2 и массив К. При переходе к новому рисунку устанавливаются, зависящие от количества бит на цвет - коды очистки LC и конца LE, из потока вводится начальная строка L1 (для односимвольных строк код совпадает с символом). Затем - очищается таблица К и ее счетчик LN, устанавливаются текущая длина кода LR и граница увеличения длины LT. Несмотря на громкое название - LZW-архивирование - сама обработка очень проста: очередной символ L2 приписывается к уже накопленной строке L1 (ей соответствует код) и проверяется, есть ли код для новой строки. Если есть, то ее устанавливаем новым кодом, иначе - передаем L1 на дальнейшую обработку, создаем новый код, и строкой L1 становится символ L2. (Причем в коде это выглядит гораздо проще). При накоплении более LT кодов (именно более, а не столько же!), предел LT удваивается, текущая длина LR увеличивается на 1. При накоплении 4096 кодов заново очищается K и устанавливаются LN, LR и LT. По исчерпании потока выдается код остаточной строки - L1 и код конца - LE.
ПЕРЕМЕННЫЕ: var LI, LC, LE, LT, LR, LN, L1, L2; var K = [];
УСТАНОВКА 1 (на рисунок): LC = 1<<БИТ_НА_ЦВЕТ_ДЛЯ_КОДИРОВАНИЯ; LE = LC+1; L1 = СИМВОЛ_ИЗ_ПОТОКА;
УСТАНОВКА 2 (на рисунок и на 4096 кодов): ВЫДАТЬ(LC); for (LN = 0; LN<LC; LN++) K[LN] = {}; LR = БИТ_НА_ЦВЕТ_ДЛЯ_КОДИРОВАНИЯ+1; LN = LE+1; LT = LC<<1;
ОБРАБОТКА: L2 = СИМВОЛ_ИЗ_ПОТОКА; if (L2 in K[L1]) L1 = K[L1][L2]; else { ВЫДАТЬ(L1); K[LN]={}; K[L1][L2] = LN++; L1 = L2; ПРОВЕРКА ПЕРЕПОЛНЕНИЙ }
ПЕРЕПОЛНЕНИЕ 1 (коды этой длины исчерпаны): if (LN > LT) { LT = LT<<1; LR = 1 }
ПЕРЕПОЛНЕНИЕ 2 (исчерпано 4096 кодов): if (LT == 4096) УСТАНОВКА 2;
КОНЕЦ: ВЫДАТЬ(L1); ВЫДАТЬ(LE);
ОБРАБОТКА КОДОВ. Коды имеют длину от 3 до 12 бит, поэтому и надо порезать по-байтно. Переменных всего три - невыведенный остаток битов - PC, длина этого остатка - PL и текущая длина кода. Установка (в начале нового рисунка) - проще некуда - переменные обнуляются. Обработка требует понимания, с какого конца читаются биты в байте: новый код приписывается к остатку, длина увеличивается на длину кода. Переполнение - если длина остатка становится 8 и больше - происходит постоянно, выдаются один-пара байтов с конца остатка, соответственно, остаток и его длина уменьшаются. По исчерпании кодов выдается остаток.
ПЕРЕМЕННЫЕ: var PC, PL;
УСТАНОВКА (на рисунок): PC = 0; PL = 0;
ОБРАБОТКА: PC |= КОД<<PL; PL += LR; ПРОВЕРКА ПЕРЕПОЛНЕНИЙ;
ПЕРЕПОЛНЕНИЕ (длина больше 7): for (; PL>7; PL -= 8 ) { ВЫДАТЬ(PC&255); PC >>>= 8 }
КОНЕЦ: ВЫДАТЬ(PC);
ОБРАБОТКА БАЙТОВ нужна для упаковки их в упомянутые выше GIF-пакеты - по 255 байтов. Т.е. нужна строка для накопления пакета TS и счетчик байтов - TC. Установка (в начале нового рисунка) - просто обнуление TS и TC. Обработка - запись байта в строку с увеличением счетчика. Переполнение - счетчик насчитал 255 - сисволов вызывает выдачу готового пакета (в GIF-файл). (Как я писал выше, тут возможны подводные камни, т.к. некоторые средства программирования имеют свое понятие о кодировке строк и файлов). Исчерпание потока байтов обрабатывается очевидном - выдачей последнего пакета и пустого пакта, завершающего GIF-блок.
ПЕРЕМЕННЫЕ: var TC, TS;
УСТАНОВКА (на рисунок): TC = 0; TS = "";
ОБРАБОТКА: TS += БАЙТ; TC++; ПРОВЕРКА ПЕРЕПОЛНЕНИЙ;
ПЕРЕПОЛНЕНИЕ (накоплено 255 байтов): if (TC == 255) { ВЫДАТЬ(255, TS); TC = 0; TS = "" }
КОНЕЦ: ВЫДАТЬ(TC, TS); ВЫДАТЬ(0);
Теперь надо это аккуратно связать в одну программу. У меня с первого раза никогда не получается. По очевидной причине. Мы писали эту программу постепенно (по сути, методом, создания проблемно-ориентированного языка), а это всегда метод проб и ошибок.
IV. Еще о ПРОБЛЕМНО-ОРИЕНТИРОВАННОМ ЯЗЫКЕ. Мы же не упражняемся в правописании, а собираем полезный файл. Т.к. GIF собирается строго последовательно, то можно просто копировать делающие это куски друг за другом в скрипт. Но, рано или поздно формируется множество удобных операций, которые хочется вызывать "в одно касание". Попутно, формируется и множество структур данных, необходимых для этого. Я обычно храню GIF в виде дерева (один в один с приведенным выше форматом). Для обозначения команд достаточно однобуквенных имен - а для "программирования" скрипта - правки одной-единственной строки, состоящей из этих букв. Цифры обозначают параметры. Имена подгружаемых и выдаваемых файлов можно хранить в виде отдельного списка.
Но это - возврат ко второму тому заметок с его текстовыми интерпретаторами...
V. А, будет тут что-то про НАСТОЯЩУЮ КОМПИЛЯЦИЮ? Про перевод текста в исполняемый код? Для GIF-компилятора, возможны два варианта:
1. Генератор набора команд, описанных выше. Пока необходимости в этом не вижу, проще поправить скрипт.
2. Вставку кода, который будет исполняться распаковщиком GIF-файла. Это более перспективное направление.
На старом DOS-BASIC, например, это страхолюдие выглядело примерно так:
10 OPEN "R", 1, "DBASE10.GIF"
20 L=(LOF(1)+64)/128
30 FIELD 1, 128 AS S$
40 OPEN "R", 2, "DBASE20.GIF"
50 FIELD 2, 128 AS T$
60 FOR I=1 TO L
70 GET 1
80 LSET T$=S$
90 PUT 2
100 NEXT I
120 CLOSE 1
Тут я делал вид, что работаю с Базой Данных и обманутый BASIC начинал качать бинарные данные "запись" за "записью".
WSH, понятно, тоже "держит марку". Как я писал во втором томе, он и обычный-то текст пытается изувечить. А то, что бывают нетекстовые файлы, для JavaScript - ересь. Пишет строки она, вроде, еще кое-как, а читать "неправильные строки" совсем отказывается. Если только по одному байту за раз. (Решение предлагается "простейшее" - открывать файлы не как файлы, а через специальный объект ADODB, но это слишком окольный путь, т.к. этот объект возвращает байтовый массив в странно упакованном формате).
Посимвольное чтение на JavaScript:
var FSO = WScript.CreateObject("Scripting.FileSystemObject");
var FGIF1 = FSO.OpenTextFile("dbase10.gif", 1, 0);
var FGIF2 = FSO.OpenTextFile("dbase20.gif", 2, true, 0);
var S; while (!FGIF1.AtEndOfStream) {
S = FGIF1.Read(1);
FGIF2.Write(S) }
FGIF1.Close();
FGIF2.Close();
Откуда в GIF-формате "ненормальные строки"? Они состоят из байтов четырех типов:
1. Обычные печатные символы - только в сигнатурах заголовках и дополнительных блоках (во втором томе Заметок я загонял в эти блоки всю базу данных).
2. Байты, в которые упакованы числа, например, размеры картинок и компоненты палитры.
3. Порезанные на байты последовательности битов разной длины - результат работы GIF-архиватора.
4. Прочие байты - флаговые и сигнальные.
II. Так мы плавно переходим к GIF-ФОРМАТУ. Он удобен своей универсальностью. Можно засунуть что угодно и, пока файл не очень велик, файловый хостинг будет считать его обычной картинкой и не требовать для его передачи пользователю танцев с бубном.
GIF-ФАЙЛ состоит из следующих блоков:
- ЗАГОЛОВОК (13) в скобках - размер в байтах
- - СИГНАТУРА(6) = "GIF87a" ИЛИ "GIF89a"
- - ШИРИНА(2)
- - ВЫСОТА(2)
- - ФЛАГИ(1)
- - - ?ПАЛИТРА(.1) цифры с точкой - биты
- - - ЦВЕТОВОЕ РАЗРЕШЕНИЕ(.3)
- - - ОТСОРТИРОВАННАЯ ПО ЧАСТОТЕ ЦВЕТОВ ТАБЛИЦА(.1) = 0(GIF87a)
- - - БИТ НА ЦВЕТ(.3)
- - ЦВЕТ ФОНА(1)
- - АСПЕКТ(1) в GIF87a =0, нужно для выравнивания по размеру экрана?
- ?(?ПАЛИТРА) ПАЛИТРА(3 * 2^(БИТ НА ЦВЕТ + 1))
- {РИСУНОК} фигурные скобки показывают, что таких элементов может быть много
- - ЗАГОЛОВОК (11)
- - - СИГНАТУРА(1) = ','
- - - СЛЕВА(2)
- - - СВЕРХУ(2)
- - - ШИРИНА(2)
- - - ВЫСОТА(2)
- - - ФЛАГИ(1)
- - - - ?ПАЛИТРА(.1)
- - - - ?ЧЕРЕЗПОЛОСНЫЙ(.1) порядок строк: 0,8,.., 4,12,.., 2,6,.., нечетные
- - - - ОТСОРТИРОВАННАЯ ПО ЧАСТОТЕ ЦВЕТОВ ТАБЛИЦА (.1) в GIF87a =0
- - - - 0(.2)
- - - - БИТ НА ЦВЕТ В ПАЛИТРЕ(.3)
- - - БИТ НА ЦВЕТ ДЛЯ КОДИРОВАНИЯ (1) для на цвет (минимально - 2)
- - ?(?ПАЛИТРА) ПАЛИТРА(3 * 2^(БИТ НА ЦВЕТ В ПАЛИТРЕ + 1))
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - БАЙТЫ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ завершает последовательность пакетов
- {РАСШИРЕНИЕ ОБЩЕГО ВИДА} далее будут перечислены конкретные РАСШИРЕНИЯ
- - СИГНАТУРА(1) = "!"
- - КОД(1) по нему определяются типы РАСШИРЕНИЙ
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - БАЙТЫ(ДЛИНА)
- - 0(1) -- ПУСТОЙ ПАКЕТ
- КОНЕЦ
- - СИГНАТУРА(1) = ";"
Конкретные РАСШИРЕНИЯ:
- {РАСШИРЕНИЕ ТЕКСТА}
- - СИГНАТУРА(1) = "!"
- - КОД(1) =1
- - ПАКЕТ заголовка
- - - ДЛИНА(1) =12
- - - СЛЕВА(2)
- - - СВЕРХУ(2)
- - - ШИРИНА(2)
- - - ВЫСОТА(2)
- - - ШИРИНА СИМВОЛА(1)
- - - ВЫСОТА СИМВОЛА(1)
- - - ЦВЕТ ТЕКСТА(1)
- - - ЦВЕТ ФОНА(1)
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - ТЕКСТ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ
- РАСШИРЕНИЕ УПРАВЛЕНИЯ ГРАФИКОЙ
- - СИГНАТУРА(1) = "!"
- - КОД(1) =0xF9
- - {ПАКЕТ} заголовка
- - - ДЛИНА(1) = 4
- - - ФЛАГИ(1)
- - - - 0(.3)
- - - - ПОСЛЕКАДРИЕ(.3) 0 - не указано, 1 - нет, 2 - затереть фоном, 3 - восстановить после себя, 4-7 - не определено
- - - - ПОДТВЕРЖДЕНИЕ(.1) переключение кадров по сигналу пользователя
- - - - ПРОЗРАЧНОСТЬ(.1)
- - - ЗАДЕРЖКА(2) в 1/100 секунды
- - - ПРОЗРАЧНЫЙ ЦВЕТ(1)
- - 0(1) ПУСТОЙ ПАКЕТ
- {РАСШИРЕНИЕ КОММЕНТАРИЯ}
- - СИГНАТУРА(1) = "!"
- - КОД(1) =0xFE
- - ПАКЕТ
- - - ДЛИНА(1)
- - - КОММЕНТАРИЙ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ
- {РАСШИРЕНИЕ ПРИЛОЖЕНИЯ}
- - СИГНАТУРА(1) = "!"
- - КОД(1) =0xFF
- - ПАКЕТ заголовка
- - - ДЛИНА(11)
- - - ИМЯ(8 )
- - - КОНТРОЛЬ ИМЕНИ(3)
- - {ПАКЕТ}
- - - ДЛИНА(1)
- - - ТЕКСТ(ДЛИНА)
- - 0(1) ПУСТОЙ ПАКЕТ
За счет чего GIF универсален? Только за счет того, что файл состоит из набора практически независимых блоков. Обязательны только блок-заголовок, блок-рисунок (чтобы никто не догадался, что это не картинка, а какая-то фигня) и блок-метка-конца. Остальные блоки можно добавлять по вкусу - рисунки,- расширение управления графикой (только одно), расширения комментария, расширения текста, расширения приложения. Такая структура позволяет писать программу по частям - сначала общий пропускатель блоков, затем - по необходимости - распознаватели блоков нужных типов.
К сожалению, как и в случае с Missile Command такой подход сильно утяжеляет программу. Слишком уж много получается фантиков, завернутых в другие фантики, которые можно было бы обойти простыми GOTO.
Подобно разным типам байтов есть разные типы их наборов:
1. Первый байт блока, определяющий его тип (ниже ф-я MB).
2. Обязательные байты, количество зависит от типа (начальная M, MI, MX, ME).
3. Байты палитры, количество определяется обязательными байтами (MF, MP, MN, MA).
4. Первый байт пакета, указывает количество байтов в нем (MR).
5. Байты пакета. Пакет нулевой длины означает конец блока (MS).
Здесь WSH-скрипт программы первичного анализа GIF-файла. Подставить нужное имя картинки вместо dbase.gif; сохранить в файл с расширением wsf. Запускать из Win-консоли при помощи команды cscript (по умолчанию МастДай будет пытаться запускать скрипт при помощи оконного wscript и сдохнет, не найдя консольный Wscript.StdOut):
<job id="GIF">
<script id="BIN.vbs" language="VBScript">
Public Function B(S) ' У JavaScript проблемы с байтами
B=Asc(S)
End Function
</script>
<script id="GIF.js" language="JavaScript">
var T, P, S;
var F = WScript.CreateObject("Scripting.FileSystemObject");
var F1 = F.OpenTextFile("dbase10.gif", 1, 0);
var ME = function () {}; // Достигнут конец GIF
var MR = function () { if(T=B(S)) M = MS; else M = MB; // Новый пакет
/* Сколько байт в пакете? */ WScript.StdOut.WriteLine(T) };
var MS = function () { if (!--T) M = MR }; // Пропуск байтов до нового пакета
var MX = function () { M = MR; // Блок расширения
/* Какое расширение? */ WScript.StdOut.WriteLine(B(S)) };
var MA = function () { if (!--T) M = MR }; // Пропуск палитры рисунка
var MN = function () { P=B(S); T=1; // Проверка палитры рисунка
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MA }
var MI = function () { if (!--T) M = MN }; // Заголовок рисунка
var MB = function () { if (S==";") M = ME; // Новый блок
else if (S==",") { T = 8; M = MI } else M=MX;
/* Какого типа блок? */ WScript.StdOut.WriteLine(S) };
var MP = function () { if (!--T) M = MB }; // Пропуск палитры файла
var MF = function () { P=B(S); T=2; // Проверка палитры файла
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MP }
var M = function () { if (!--T) M = MF }; // Заголовок файла
T = 10;
while (!F1.AtEndOfStream) { S = F1.Read(1); M() }
F1.Close();
</script>
</job>
Какие блоки нам понадобятся? Как сказал выше: заголовок, рисунок-обманка, маркер-конца. А для настоящей информации? Во втором томе заметок я весь текст скинул без преобразования в один блок расширения (типа 2, не используемого в стандарте). Оставлю для совместимости. Для заархивированного текста отведу блок расширения типа 3. Что делать с картинками? Их можно запихивать в GIF "на общих основаниях" - в блоки рисунков.
Никаких межблоковых связей в наш формат вводить не буду. Вообще не буду вводить никаких соглашений на верстку и графическое оформление информации - это будет целиком на совести браузера и, возможно, указаний в тексте.
III. Рассмотрим, как АРХИВИРУЕТСЯ обычный GIF-файл (точнее, картинки в нем). Если мы все сделаем правильно, то любой просмотрщик покажет нашу самодельную картинку как нормальную.
Архивирование проходит в три последовательных этапа:
1) символы превращаются в коды;
2) коды упаковываются в байты;
3) байты разбиваются на ПАКЕТЫ.
Все этапы выглядят одинаково (считая, что символы подаются снаружи один за другим):
1) производятся некоторые начальные установки;
2) символ (код или байт) обрабатывается;
3) проверяется не переполнился ли некий накопитель и, соответственно что-то делается;
4) когда поток исчерпывается, накопители очищаются.
Вот как проходит ОБРАБОТКА СИМВОЛОВ. Используется много переменных - LI, LC, LE, LT, LR, LN, L1, L2 и массив К. При переходе к новому рисунку устанавливаются, зависящие от количества бит на цвет - коды очистки LC и конца LE, из потока вводится начальная строка L1 (для односимвольных строк код совпадает с символом). Затем - очищается таблица К и ее счетчик LN, устанавливаются текущая длина кода LR и граница увеличения длины LT. Несмотря на громкое название - LZW-архивирование - сама обработка очень проста: очередной символ L2 приписывается к уже накопленной строке L1 (ей соответствует код) и проверяется, есть ли код для новой строки. Если есть, то ее устанавливаем новым кодом, иначе - передаем L1 на дальнейшую обработку, создаем новый код, и строкой L1 становится символ L2. (Причем в коде это выглядит гораздо проще). При накоплении более LT кодов (именно более, а не столько же!), предел LT удваивается, текущая длина LR увеличивается на 1. При накоплении 4096 кодов заново очищается K и устанавливаются LN, LR и LT. По исчерпании потока выдается код остаточной строки - L1 и код конца - LE.
ПЕРЕМЕННЫЕ: var LI, LC, LE, LT, LR, LN, L1, L2; var K = [];
УСТАНОВКА 1 (на рисунок): LC = 1<<БИТ_НА_ЦВЕТ_ДЛЯ_КОДИРОВАНИЯ; LE = LC+1; L1 = СИМВОЛ_ИЗ_ПОТОКА;
УСТАНОВКА 2 (на рисунок и на 4096 кодов): ВЫДАТЬ(LC); for (LN = 0; LN<LC; LN++) K[LN] = {}; LR = БИТ_НА_ЦВЕТ_ДЛЯ_КОДИРОВАНИЯ+1; LN = LE+1; LT = LC<<1;
ОБРАБОТКА: L2 = СИМВОЛ_ИЗ_ПОТОКА; if (L2 in K[L1]) L1 = K[L1][L2]; else { ВЫДАТЬ(L1); K[LN]={}; K[L1][L2] = LN++; L1 = L2; ПРОВЕРКА ПЕРЕПОЛНЕНИЙ }
ПЕРЕПОЛНЕНИЕ 1 (коды этой длины исчерпаны): if (LN > LT) { LT = LT<<1; LR = 1 }
ПЕРЕПОЛНЕНИЕ 2 (исчерпано 4096 кодов): if (LT == 4096) УСТАНОВКА 2;
КОНЕЦ: ВЫДАТЬ(L1); ВЫДАТЬ(LE);
ОБРАБОТКА КОДОВ. Коды имеют длину от 3 до 12 бит, поэтому и надо порезать по-байтно. Переменных всего три - невыведенный остаток битов - PC, длина этого остатка - PL и текущая длина кода. Установка (в начале нового рисунка) - проще некуда - переменные обнуляются. Обработка требует понимания, с какого конца читаются биты в байте: новый код приписывается к остатку, длина увеличивается на длину кода. Переполнение - если длина остатка становится 8 и больше - происходит постоянно, выдаются один-пара байтов с конца остатка, соответственно, остаток и его длина уменьшаются. По исчерпании кодов выдается остаток.
ПЕРЕМЕННЫЕ: var PC, PL;
УСТАНОВКА (на рисунок): PC = 0; PL = 0;
ОБРАБОТКА: PC |= КОД<<PL; PL += LR; ПРОВЕРКА ПЕРЕПОЛНЕНИЙ;
ПЕРЕПОЛНЕНИЕ (длина больше 7): for (; PL>7; PL -= 8 ) { ВЫДАТЬ(PC&255); PC >>>= 8 }
КОНЕЦ: ВЫДАТЬ(PC);
ОБРАБОТКА БАЙТОВ нужна для упаковки их в упомянутые выше GIF-пакеты - по 255 байтов. Т.е. нужна строка для накопления пакета TS и счетчик байтов - TC. Установка (в начале нового рисунка) - просто обнуление TS и TC. Обработка - запись байта в строку с увеличением счетчика. Переполнение - счетчик насчитал 255 - сисволов вызывает выдачу готового пакета (в GIF-файл). (Как я писал выше, тут возможны подводные камни, т.к. некоторые средства программирования имеют свое понятие о кодировке строк и файлов). Исчерпание потока байтов обрабатывается очевидном - выдачей последнего пакета и пустого пакта, завершающего GIF-блок.
ПЕРЕМЕННЫЕ: var TC, TS;
УСТАНОВКА (на рисунок): TC = 0; TS = "";
ОБРАБОТКА: TS += БАЙТ; TC++; ПРОВЕРКА ПЕРЕПОЛНЕНИЙ;
ПЕРЕПОЛНЕНИЕ (накоплено 255 байтов): if (TC == 255) { ВЫДАТЬ(255, TS); TC = 0; TS = "" }
КОНЕЦ: ВЫДАТЬ(TC, TS); ВЫДАТЬ(0);
Теперь надо это аккуратно связать в одну программу. У меня с первого раза никогда не получается. По очевидной причине. Мы писали эту программу постепенно (по сути, методом, создания проблемно-ориентированного языка), а это всегда метод проб и ошибок.
IV. Еще о ПРОБЛЕМНО-ОРИЕНТИРОВАННОМ ЯЗЫКЕ. Мы же не упражняемся в правописании, а собираем полезный файл. Т.к. GIF собирается строго последовательно, то можно просто копировать делающие это куски друг за другом в скрипт. Но, рано или поздно формируется множество удобных операций, которые хочется вызывать "в одно касание". Попутно, формируется и множество структур данных, необходимых для этого. Я обычно храню GIF в виде дерева (один в один с приведенным выше форматом). Для обозначения команд достаточно однобуквенных имен - а для "программирования" скрипта - правки одной-единственной строки, состоящей из этих букв. Цифры обозначают параметры. Имена подгружаемых и выдаваемых файлов можно хранить в виде отдельного списка.
Но это - возврат ко второму тому заметок с его текстовыми интерпретаторами...
V. А, будет тут что-то про НАСТОЯЩУЮ КОМПИЛЯЦИЮ? Про перевод текста в исполняемый код? Для GIF-компилятора, возможны два варианта:
1. Генератор набора команд, описанных выше. Пока необходимости в этом не вижу, проще поправить скрипт.
2. Вставку кода, который будет исполняться распаковщиком GIF-файла. Это более перспективное направление.
Последний раз редактировалось: Gudleifr (Вт Авг 13, 2024 2:38 am), всего редактировалось 1 раз(а)
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
Этот абзац отведу под текущий текст GIF-компилятора.
Пока он почти ничего не умеет - берет заголовок условного GIF-файла и генерирует ему картинку из случайного набора точек. Теперь, надо вернуться ко второму тому и научить распаковке просмотрщик базы данных.
<job id="GIF">
<script id="BIN.vbs" language="VBScript">
Public Function B(S)
B=Asc(S)
End Function
Public Function B2(S)
B2=Chr(S)
End Function
</script>
<script id="GIF.js" language="JavaScript">
var T, P, S, OL, OX;
var F = WScript.CreateObject("Scripting.FileSystemObject");
var F1 = F.OpenTextFile("1.gif", 1, 0);
var F2 = F.OpenTextFile("2.gif", 2, true, 0);
var ME = function () {};
var MR = function () { if(T=B(S)) M = MS; else M = MB };
var MS = function () { if (!--T) M = MR };
var MX = function () { M = MR };
var MA = function () { if (!OL) OL = B(S);
if (!--T) { M = MR; OX = 1 }};
var MN = function () { P=B(S); T=1;
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MA }
var MI = function () { if (!--T) M = MN };
var MB = function () { if (S==";") M = ME;
else if (S==",") { T = 8; M = MI } else M = MX };
var MP = function () { if (!--T) M = MB };
var MF = function () { P=B(S); T=2;
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MP }
var M = function () { if (!--T) M = MF };
T = 10; OL = 0; OX = 0;
while (!F1.AtEndOfStream) { S = F1.Read(1);
if(OX) break; F2.Write(S); M() }
F1.Close();
var LI, LC, LE, LT, LR, LN, L1, L2;
var K = [];
LC = 1<<OL; LE = LC+1;
function KR () {
for (LN = 0; LN<LC; LN++) K[LN] = {};
LR = OL+1; LN = LE+1; LT = LC<<1 }
var TC, TS;
TC = 0; TS = "";
function T1(X) {
TS += B2(X); TC++;
if (TC == 255) {
F2.Write(B2(255)); F2.Write(TS);
TC = 0; TS = "" }}
var PC, PL;
PC = 0; PL = 0;
function KP(X) {
PC |= X<<PL;
for(PL += LR; PL>7; PL -= 8 ) {
T1(PC&255); PC >>>= 8 }}
L1 = 16; KR(); KP(LC);
var L0; L0=0;
for (LI=1; LI<64000; LI++) {
L2 = 1+Math.floor(Math.random()*16);
if (L2 in K[L1]) L1 = K[L1][L2];
else { KP(L1); K[LN]={}; K[L1][L2] = LN++; L1 = L2;
if (LN > LT) { LT = LT<<1; LR++ }
else if (LT == 4096) { KP(LC); KR() }}}
KP(L1); KP(LE); if (PL) T1(PC);
if (TC) { F2.Write(B2(TC)); F2.Write(TS) }
F2.Write(B2(0)); F2.Write(";");
F2.Close();
</script>
</job>
Пока он почти ничего не умеет - берет заголовок условного GIF-файла и генерирует ему картинку из случайного набора точек. Теперь, надо вернуться ко второму тому и научить распаковке просмотрщик базы данных.
<job id="GIF">
<script id="BIN.vbs" language="VBScript">
Public Function B(S)
B=Asc(S)
End Function
Public Function B2(S)
B2=Chr(S)
End Function
</script>
<script id="GIF.js" language="JavaScript">
var T, P, S, OL, OX;
var F = WScript.CreateObject("Scripting.FileSystemObject");
var F1 = F.OpenTextFile("1.gif", 1, 0);
var F2 = F.OpenTextFile("2.gif", 2, true, 0);
var ME = function () {};
var MR = function () { if(T=B(S)) M = MS; else M = MB };
var MS = function () { if (!--T) M = MR };
var MX = function () { M = MR };
var MA = function () { if (!OL) OL = B(S);
if (!--T) { M = MR; OX = 1 }};
var MN = function () { P=B(S); T=1;
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MA }
var MI = function () { if (!--T) M = MN };
var MB = function () { if (S==";") M = ME;
else if (S==",") { T = 8; M = MI } else M = MX };
var MP = function () { if (!--T) M = MB };
var MF = function () { P=B(S); T=2;
if (P&128 ) T += 3*(1<<((P&7)+1)); M = MP }
var M = function () { if (!--T) M = MF };
T = 10; OL = 0; OX = 0;
while (!F1.AtEndOfStream) { S = F1.Read(1);
if(OX) break; F2.Write(S); M() }
F1.Close();
var LI, LC, LE, LT, LR, LN, L1, L2;
var K = [];
LC = 1<<OL; LE = LC+1;
function KR () {
for (LN = 0; LN<LC; LN++) K[LN] = {};
LR = OL+1; LN = LE+1; LT = LC<<1 }
var TC, TS;
TC = 0; TS = "";
function T1(X) {
TS += B2(X); TC++;
if (TC == 255) {
F2.Write(B2(255)); F2.Write(TS);
TC = 0; TS = "" }}
var PC, PL;
PC = 0; PL = 0;
function KP(X) {
PC |= X<<PL;
for(PL += LR; PL>7; PL -= 8 ) {
T1(PC&255); PC >>>= 8 }}
L1 = 16; KR(); KP(LC);
var L0; L0=0;
for (LI=1; LI<64000; LI++) {
L2 = 1+Math.floor(Math.random()*16);
if (L2 in K[L1]) L1 = K[L1][L2];
else { KP(L1); K[LN]={}; K[L1][L2] = LN++; L1 = L2;
if (LN > LT) { LT = LT<<1; LR++ }
else if (LT == 4096) { KP(LC); KR() }}}
KP(L1); KP(LE); if (PL) T1(PC);
if (TC) { F2.Write(B2(TC)); F2.Write(TS) }
F2.Write(B2(0)); F2.Write(";");
F2.Close();
</script>
</job>
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Re: 03.01. ГЕЛЬВЕЦИЙ
ПЕДАГОГИЧЕСКОЕ ОТСТУПЛЕНИЕ
МОДЕЛИСТ-КОНСТРУКТОР 12/78
Понятно, везде "бумажную модель" нужно читать, как "программно-математическую", а "геометрические" примитивы - как "алгоритмические". Но суть остается - модели должны доставлять! Чтобы их можно было и самим посмотреть и друзьям показать!
ПРОГРАММА ТЕХНИЧЕСКОГО КРУЖКА ДЛЯ МЛАДШИХ ШКОЛЬНИКОВ В ГРУППЕ ПРОДЛЕННОГО ДНЯ (первый год занятий)
В ЧЕМ СОСТОИТ ЗАДАЧА?
Технический кружок занимается два часа в неделю во вторых-третьих классах и один час - в первых классах.
Занятия могут вести: воспитатель группы, учитель начальных классов, учитель-предметник или специалист шефствующего предприятия - руководитель кружка.
При подборе группы учащихся для занятий необходимо учитывать интересы ребят и соблюдать принцип добровольности.
Часы, отведенные на занятия, могут быть сдвоены, а можно проводить занятия два раза в неделю по 45 минут каждое.
Содержание каждого занятия определяется интересами, желаниями ребят и потребностями учебного процесса.
Внеклассная работа по развитию технического творчества у младших школьников в группах продленного дня должна стать органической составной частью всей учебно-воспитательной деятельности школы. Она должна способствовать расширению кругозора младших школьников, выводить его за пределы школьной программы, прививать интерес и любовь к научно-популярной литературе, а также углублять и расширять знания, навыки и умения графической грамотности.
I. ВВОДНАЯ БЕСЕДА. МАТЕРИАЛЫ И ИНСТРУМЕНТЫ (3 ЧАСА)
Значение техники в жизни человека. Роль и значение рационализаторов, новаторов и изобретателей.
Порядок и содержание работы на занятиях по техническому творчеству в группах продленного дня. Показ готовых поделок, выполненных на занятиях в прошлом году.
Рассказ о производстве бумаги и картона, о свойствах и применении древесины, проволоки, жести и других материалов. Способы обработки и инструменты: назначение, правила пользования и правила техники безопасности. Демонстрация инструментов, применяемых при обработке различных материалов (ножницы, пила, молоток, плоскогубцы и т.д.).
ПРАКТИЧЕСКИЕ РАБОТЫ. Экскурсия в школьную мастерскую. Изготовление из плотной бумаги симметричных силуэтов зверей, насекомых, машин, бумажных моделей планера, самолета, ракеты, лодки, автомобиля, а также елочных украшений. Изготовление из картона (по шаблону) плоских движущихся игрушек-"плясунов".
II. ГРАФИЧЕСКАЯ ГРАМОТА (12 часов)
Чертежные принадлежности и инструменты: линейки, угольники в 45o и 60o, циркуль, карандаши и рабочая тетрадь.
Бережное и правильное обращение с инструментами.
Рисование и технический рисунок. Что такое чертеж и чем он отличается от рисунка. Линии чертежа. Линии видимого и невидимого контуров. Линия сгиба. Условное обозначение этих линий.
Разметка (умение перенести основные размеры и рабочие линии на материал). Проведение перпендикулярных и параллельных линий, построение простейших разверток. Первоначальное и упрощенное понятие о плоскостном и объемном (наглядном) изображении, о видах спереди, сверху, слева (умение читать чертежи).
Габаритные размеры; как проставить и прочитать их на чертеже. Масштаб увеличения и уменьшения (умение увеличить или уменьшить изделие относительно чертежа).
ПРАКТИЧЕСКИЕ РАБОТЫ. Практическое применение чертежных инструментов, проведение перпендикулярных и параллельных линий. Выполнение технических рисунков и чертежей геометрических тел с применением линий чертежа. Разметка и выполнение макетов лодок, ракет, самолетов из бумаги и картона. Изготовление "игольниц" в виде 8 и 12-лепестковых цветков и циферблатов часов с применением циркуля (деление окружности).
III. ТЕХНИЧЕСКИЕ И КОНСТРУКТОРСКО-ТЕХНОЛОГИЧЕСКИЕ ПОНЯТИЯ (10 часов)
Углубление знаний о свойствах материалов и использование их на практике. Материалы-проводники. Материалы-изоляторы. Природные и искусственные материалы.
Углубление знаний о применении инструментов и приспособлений в быту и на производстве (пассатижи, ножовка, гаечный ключ, дрель, тиски, лебедка). Знакомство с деятельностью человека на производстве (станочники, слесари, конструкторы, маляры, шоферы, плотники). Понятия о технологических процессах в быту и на производстве.
Экскурсии на ближайшие предприятия. Связь трудового опыта детей с трудом взрослых, с развитием техники. Знакомство с механическими способами обработки.
ПРАКТИЧЕСКИЕ РАБОТЫ. Изготовление политехнического профинформационного лото, закрепление и углубление полученных понятий в процессе игры, Наблюдения и опыты по определению и сравнению свойств природных и искусственных материалов.
IV. КОНСТРУИРОВАНИЕ ИЗ ПЛОСКИХ ФОРМ (10 часов)
Общие сведения о конструировании: понятие о конструктивных элементах, о создании образа технического объекта, о проектировании технического устройства.
ПРАКТИЧЕСКИЕ РАБОТЫ. Изготовление манипулятивного конструктора из плотной бумаги (геометрические фигуры, различные по ферме, размеру и цвету).
Создание образов технических объектов из элементов манипулятивного конструктора (корабль, грузовик, подъемный кран, самолет, светофор, весы).
Изготовление контурных моделей со щелевидными соединениями из картона по образцу, по рисунку, по чертежу, по представлению, воображению и собственному замыслу.
V. РАЗВЕРТКА ПРОСТЫХ ГЕОМЕТРИЧЕСКИХ ТЕЛ (5 часов)
Простейшие геометрические тела (куб, параллелепипед, цилиндр, конус). Элементы геометрических тел. Сопоставление геометрических тел с геометрическими фигурами.
Понятие о развертках и правила выполнения разверток простейших геометрических тел.
ПРАКТИЧЕСКИЕ РАБОТЫ. Изготовление модели цилиндра, конуса, куба, параллелепипеда с выполнением развертки.
Склеивание кружки, ведра, кузова и колес автомобиля с предварительным выполнением развертки.
Изготовление компаса, ракеты из бумаги.
VI. КОНСТРУИРОВАНИЕ СЪЕМНЫХ ПРЕДМЕТОВ И УСТРОЙСТВ ИЗ БУМАГИ И КАРТОНА (18 часов)
Форма геометрических тел - основа предметов и технических устройств. Анализ формы технических объектов и сопоставлений ее с геометрическими телами. Создание образа технических объектов на основе манипулирования геометрическими телами.
ПРАКТИЧЕСКИЕ РАБОТЫ. Выполнение макета грузовика с прицепами (кабина - параллелепипед, мотор - куб, кузов И основание - параллелепипеды, колеса - цилиндры).
Изготовление моделей из бумаги, картона и других материалов (самолет Ту-144, автобус, "скорая помощь").
VII. ЭЛЕМЕНТЫ ПРОСТЕРШИХ УЗЛОВ И МЕХАНИЗМОВ, ТЕХНИЧЕСКОЕ МОДЕЛИРОВАНИЕ
(12 часов)
Значение машин и механизмов в жизни. Что такое узел, механизм и машина? Элементы механизмов и их взаимодействие. Знакомство с деталями, которые входят в наборы "Конструктор". Первоначальные понятия о стандарте и стандартных деталях. Различные способы соединения деталей. Демонстрация моделей, узлов и частей, собранных из набора "Конструктор".
ПРАКТИЧЕСКИЕ РАБОТЫ. Сборка моделей технических объектов и устройств из готовых металлических конструкторов:
а) по образцу;
б) по рисунку;
в) по собственному замыслу.
Склеивание технических объектов из готовых частей деревянных, бумажных и пластмассовых наборов.
VIII. ПОДВЕДЕНИЕ ИТОГОВ (3 часа)
Беседа: "Чему мы научились на занятиях по техническому творчеству?"
Подготовка выставки и праздника.
ВОСПИТАТЕЛЬНО-ТРУДОВОЕ СОДЕРЖАНИЕ РАБОТ
Обучение составлению плана работ по каждой теме. Разработка с учащимися наиболее рациональной последовательности изготовления изделий. Сравнение запланированных хода работы и временных затрат с фактическими. Организация взаимопомощи и взаимоконтроля в процессе изготовления изделий. Организация работы бригадами. Поочередное выполнение обязанностей бригадира, общественного контролера и др. Организация соревнования между бригадами. Коллективное подведение итогов по каждой теме. Организация бесед идейно-воспитательного характера:
1. Только тех, кто любит труд, октябрятами зовут;
2. СССР - страна, где живут самые счастливые дети;
3. Заветам Ленина верны;
4. Труд и победа неразделимы.
МОДЕЛИСТ-КОНСТРУКТОР 12/78
Понятно, везде "бумажную модель" нужно читать, как "программно-математическую", а "геометрические" примитивы - как "алгоритмические". Но суть остается - модели должны доставлять! Чтобы их можно было и самим посмотреть и друзьям показать!
ПРОГРАММА ТЕХНИЧЕСКОГО КРУЖКА ДЛЯ МЛАДШИХ ШКОЛЬНИКОВ В ГРУППЕ ПРОДЛЕННОГО ДНЯ (первый год занятий)
В ЧЕМ СОСТОИТ ЗАДАЧА?
Технический кружок занимается два часа в неделю во вторых-третьих классах и один час - в первых классах.
Занятия могут вести: воспитатель группы, учитель начальных классов, учитель-предметник или специалист шефствующего предприятия - руководитель кружка.
При подборе группы учащихся для занятий необходимо учитывать интересы ребят и соблюдать принцип добровольности.
Часы, отведенные на занятия, могут быть сдвоены, а можно проводить занятия два раза в неделю по 45 минут каждое.
Содержание каждого занятия определяется интересами, желаниями ребят и потребностями учебного процесса.
Внеклассная работа по развитию технического творчества у младших школьников в группах продленного дня должна стать органической составной частью всей учебно-воспитательной деятельности школы. Она должна способствовать расширению кругозора младших школьников, выводить его за пределы школьной программы, прививать интерес и любовь к научно-популярной литературе, а также углублять и расширять знания, навыки и умения графической грамотности.
I. ВВОДНАЯ БЕСЕДА. МАТЕРИАЛЫ И ИНСТРУМЕНТЫ (3 ЧАСА)
Значение техники в жизни человека. Роль и значение рационализаторов, новаторов и изобретателей.
Порядок и содержание работы на занятиях по техническому творчеству в группах продленного дня. Показ готовых поделок, выполненных на занятиях в прошлом году.
Рассказ о производстве бумаги и картона, о свойствах и применении древесины, проволоки, жести и других материалов. Способы обработки и инструменты: назначение, правила пользования и правила техники безопасности. Демонстрация инструментов, применяемых при обработке различных материалов (ножницы, пила, молоток, плоскогубцы и т.д.).
ПРАКТИЧЕСКИЕ РАБОТЫ. Экскурсия в школьную мастерскую. Изготовление из плотной бумаги симметричных силуэтов зверей, насекомых, машин, бумажных моделей планера, самолета, ракеты, лодки, автомобиля, а также елочных украшений. Изготовление из картона (по шаблону) плоских движущихся игрушек-"плясунов".
II. ГРАФИЧЕСКАЯ ГРАМОТА (12 часов)
Чертежные принадлежности и инструменты: линейки, угольники в 45o и 60o, циркуль, карандаши и рабочая тетрадь.
Бережное и правильное обращение с инструментами.
Рисование и технический рисунок. Что такое чертеж и чем он отличается от рисунка. Линии чертежа. Линии видимого и невидимого контуров. Линия сгиба. Условное обозначение этих линий.
Разметка (умение перенести основные размеры и рабочие линии на материал). Проведение перпендикулярных и параллельных линий, построение простейших разверток. Первоначальное и упрощенное понятие о плоскостном и объемном (наглядном) изображении, о видах спереди, сверху, слева (умение читать чертежи).
Габаритные размеры; как проставить и прочитать их на чертеже. Масштаб увеличения и уменьшения (умение увеличить или уменьшить изделие относительно чертежа).
ПРАКТИЧЕСКИЕ РАБОТЫ. Практическое применение чертежных инструментов, проведение перпендикулярных и параллельных линий. Выполнение технических рисунков и чертежей геометрических тел с применением линий чертежа. Разметка и выполнение макетов лодок, ракет, самолетов из бумаги и картона. Изготовление "игольниц" в виде 8 и 12-лепестковых цветков и циферблатов часов с применением циркуля (деление окружности).
III. ТЕХНИЧЕСКИЕ И КОНСТРУКТОРСКО-ТЕХНОЛОГИЧЕСКИЕ ПОНЯТИЯ (10 часов)
Углубление знаний о свойствах материалов и использование их на практике. Материалы-проводники. Материалы-изоляторы. Природные и искусственные материалы.
Углубление знаний о применении инструментов и приспособлений в быту и на производстве (пассатижи, ножовка, гаечный ключ, дрель, тиски, лебедка). Знакомство с деятельностью человека на производстве (станочники, слесари, конструкторы, маляры, шоферы, плотники). Понятия о технологических процессах в быту и на производстве.
Экскурсии на ближайшие предприятия. Связь трудового опыта детей с трудом взрослых, с развитием техники. Знакомство с механическими способами обработки.
ПРАКТИЧЕСКИЕ РАБОТЫ. Изготовление политехнического профинформационного лото, закрепление и углубление полученных понятий в процессе игры, Наблюдения и опыты по определению и сравнению свойств природных и искусственных материалов.
IV. КОНСТРУИРОВАНИЕ ИЗ ПЛОСКИХ ФОРМ (10 часов)
Общие сведения о конструировании: понятие о конструктивных элементах, о создании образа технического объекта, о проектировании технического устройства.
ПРАКТИЧЕСКИЕ РАБОТЫ. Изготовление манипулятивного конструктора из плотной бумаги (геометрические фигуры, различные по ферме, размеру и цвету).
Создание образов технических объектов из элементов манипулятивного конструктора (корабль, грузовик, подъемный кран, самолет, светофор, весы).
Изготовление контурных моделей со щелевидными соединениями из картона по образцу, по рисунку, по чертежу, по представлению, воображению и собственному замыслу.
V. РАЗВЕРТКА ПРОСТЫХ ГЕОМЕТРИЧЕСКИХ ТЕЛ (5 часов)
Простейшие геометрические тела (куб, параллелепипед, цилиндр, конус). Элементы геометрических тел. Сопоставление геометрических тел с геометрическими фигурами.
Понятие о развертках и правила выполнения разверток простейших геометрических тел.
ПРАКТИЧЕСКИЕ РАБОТЫ. Изготовление модели цилиндра, конуса, куба, параллелепипеда с выполнением развертки.
Склеивание кружки, ведра, кузова и колес автомобиля с предварительным выполнением развертки.
Изготовление компаса, ракеты из бумаги.
VI. КОНСТРУИРОВАНИЕ СЪЕМНЫХ ПРЕДМЕТОВ И УСТРОЙСТВ ИЗ БУМАГИ И КАРТОНА (18 часов)
Форма геометрических тел - основа предметов и технических устройств. Анализ формы технических объектов и сопоставлений ее с геометрическими телами. Создание образа технических объектов на основе манипулирования геометрическими телами.
ПРАКТИЧЕСКИЕ РАБОТЫ. Выполнение макета грузовика с прицепами (кабина - параллелепипед, мотор - куб, кузов И основание - параллелепипеды, колеса - цилиндры).
Изготовление моделей из бумаги, картона и других материалов (самолет Ту-144, автобус, "скорая помощь").
VII. ЭЛЕМЕНТЫ ПРОСТЕРШИХ УЗЛОВ И МЕХАНИЗМОВ, ТЕХНИЧЕСКОЕ МОДЕЛИРОВАНИЕ
(12 часов)
Значение машин и механизмов в жизни. Что такое узел, механизм и машина? Элементы механизмов и их взаимодействие. Знакомство с деталями, которые входят в наборы "Конструктор". Первоначальные понятия о стандарте и стандартных деталях. Различные способы соединения деталей. Демонстрация моделей, узлов и частей, собранных из набора "Конструктор".
ПРАКТИЧЕСКИЕ РАБОТЫ. Сборка моделей технических объектов и устройств из готовых металлических конструкторов:
а) по образцу;
б) по рисунку;
в) по собственному замыслу.
Склеивание технических объектов из готовых частей деревянных, бумажных и пластмассовых наборов.
VIII. ПОДВЕДЕНИЕ ИТОГОВ (3 часа)
Беседа: "Чему мы научились на занятиях по техническому творчеству?"
Подготовка выставки и праздника.
ВОСПИТАТЕЛЬНО-ТРУДОВОЕ СОДЕРЖАНИЕ РАБОТ
Обучение составлению плана работ по каждой теме. Разработка с учащимися наиболее рациональной последовательности изготовления изделий. Сравнение запланированных хода работы и временных затрат с фактическими. Организация взаимопомощи и взаимоконтроля в процессе изготовления изделий. Организация работы бригадами. Поочередное выполнение обязанностей бригадира, общественного контролера и др. Организация соревнования между бригадами. Коллективное подведение итогов по каждой теме. Организация бесед идейно-воспитательного характера:
1. Только тех, кто любит труд, октябрятами зовут;
2. СССР - страна, где живут самые счастливые дети;
3. Заветам Ленина верны;
4. Труд и победа неразделимы.
Gudleifr- Admin
- Сообщения : 3399
Дата регистрации : 2017-03-29
Страница 1 из 1
Права доступа к этому форуму:
Вы не можете отвечать на сообщения