KRIEGSSPIELE!
Вы хотите отреагировать на этот пост ? Создайте аккаунт всего в несколько кликов или войдите на форум.

03.01. ГЕЛЬВЕЦИЙ

Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Пт Июл 12, 2019 7:11 pm

ТОМ III. ПОСТРОЕНИЕ МАШИНЫ

ГЛАВА ВТОРАЯ. ГЕЛЬВЕЦИЙ

Ибо сказано им: "Знание некоторых принципов легко возмещает незнание некоторых фактов".

Но зачем нам эти принципы? Мы, ведь, наблюдали возможность сделать все, что нам надо, на коленке. Плюс, "для всего, что может понадобиться клиенту, уже написано приложение". Куда тут копать?

Тяжесть задач, которые мы пытались решать во второй части Заметок, лежала не в математике (т.к. задачи были в большинстве интуитивные) и не в программировании (т.к. мы старались просто записать то, что делали руками), а в подборе инструмента для запуска программы. И сложность этого подбора зависела не столько от сложности задачи/программы, сколько от сложности среды (Операционной Системы), в которой наше решение должно было работать. Может, если мы копнем чуть глубже Операционные Системы, то сможем не подстраиваться под неудобную среду, но создавать свою?

На одном из форумов программистов-неудачников, я подметил любопытную тенденцию.
- Сначала человек спрашивает, как и на чем решать нужную ему задачу. В ответ получает длинный список "современных технологий" без которых ему не обойтись.
- В поисках минимального множества необходимых "технологий" новичок быстро опускается до самого "железа".
- Он начинает экспериментировать с "железом", радуясь, как легко получаются простейшие вещи. Ему кажется, что еще чуть-чуть, и он сможет собрать из этих простых вещей что-то сложное и прекрасное.
- ... На этом все заканчивается. Пропустив точку необходимого для решения задачи погружения в программирование, человек никогда не возвращается к решению реальных задач...

Так что с мечтой о самодельном компьютере (если его построение не прописано в условиях задачи) лучше расстаться слазу. Копать нужно не глубже, чем требует решаемая задача. Значит, здесь нам понадобятся такие задачи?

Они есть?
В первую очередь, это, конечно, задачи, решение которых готовыми средствами слишком ресурсоемко. Обычно самым критическим ресурсом считают время, потраченное на рисование на экране (включая предварительную сборку/расчет рисунка в памяти компьютера)... Быстрые игры способами, описанными во втророй части Заметок, не создашь. А способы, описанные в первой части по большей части безнадежно устарели.
Во-вторых, доступ к некоторым ресурсам компьютера/Операционной Системы/чужой программы часто весьма затруднителен (иногда это сделано специально) и простой способ взлома оказывается действительно кратчайшим путем к цели, по сравнению с изучением тонкостей "правильного использования".
Gudleifr
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Пт Июл 12, 2019 7:15 pm

ЧТО ЛЕЖИТ ЗА ПРЕДЕЛАМИ МАШИНЫ ТЬЮРИНГА?

Мы, знаем, что все, что мы можем запрограммировать на любом из языков высокого уровня не выходит за возможности Машины Тьюринга (МТ). С точки зрения "собственно программирования" все эти языки сверхвысокого уровня - лишь способ удобной краткой записи МТ. Однако, с точки зрения задачи "запуска в окружении" это не верно. В реальные реализации языков программирования включены а) средства "железа", позволяющие программе исполняться, и б) средства "железа" связывающие программу с реальным миром. Например, чтобы программа могла работать с файлами, необходима Операционная Система, знающая, что такое "файл", и необходимы сами магнитные диски, на которых эти файлы должны храниться.

И именно имеющиеся в наличии средства "железа" и доступные/документированные средства управления ими и определяют, по большей части, глубину копания программистом в тонкостях кодирования.
***

С точки зрения запуска программы, я бы выделил следующие части:

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-обезьянники, скрывающие от пользователя системные имена.

Т.о. прежде чем перейти к программированию чего-либо, мы должны заранее построить модель нашего производственного цикла в приведенных терминах и точно выяснить их смысл в текущей Операционной Системе. А это не всегда просто, например, те же семафоры могут быть реализованы на очередях процессов или на очередях сообщений (см. 03.01. ГЕЛЬВЕЦИЙ Leaf10ТЕМА #39, АБЗАЦ #76403.01. ГЕЛЬВЕЦИЙ Leaf10).
Gudleifr
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Пт Июл 12, 2019 7:19 pm

Все обычные языки программирования, в тщетной попытке описать все сущее, совмещают в себе несколько слабо совместимых грамматик:
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
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Сб Авг 17, 2019 6:27 pm

В продолжение первой главы первого тома 03.01. ГЕЛЬВЕЦИЙ Leaf10ТЕМА #60, АБЗАЦ #63503.01. ГЕЛЬВЕЦИЙ Leaf10 ...

- Папа, я хочу в войнушку!
- Сейчас. Берем ножик, палку и строгаем ружье.
- С солдатиками!
- Берем лобзик и фанерку. Пилим крепость. Строим солдатиков.
- Во всамделишную!
- Берем карту, красный и синий карандаши. Рисуем стрелки.
- На компьютере!
- ...! Сейчас папа заработает немножко денег. Сбегает в магазин и купит очередной дурацкий FPS или RTS...

Раньше было проще: включаешь персональный компьютер и сразу попадаешь в BASIC, который умеет... (см. первый том).
Теперь... А что теперь? С некоторых пор есть BASIC и у Windows. Причем, его не надо покупать отдельно. Просто, он очень хорошо спрятан.
Нужно найти папочку Windows, в ней - Microsoft.NET ... И где-то там будет файл vbc.exe (и не один - МастДай на то и МастДай, что сам с собой не совместим). Запоминаем в командном файле путь к любому из них, вписываем туда имя текстового файла с нашей программой - и готово, vbc будет делать из нашей программы исполняемый .exe-файл. (Конечно, надо немножко уметь работать с файлами и папочками. Надеюсь, после второго тома это не сложно).

Пишем игру MISSILE COMMAD.

Конечно, этот VB - не честный Visual Basic. Тот-то содержал красивый редактор для рисования и оживляжа окошек-формочек, а этот - лишь VB-подобная оболочка над внутренним .NET-языком IL (MSIL, CIL). Вам придется ковырять справочники по Win Forms и самим бороться с трехфиговым проклятьем МастДая 03.01. ГЕЛЬВЕЦИЙ Leaf10ТЕМА #35, АБЗАЦ #30903.01. ГЕЛЬВЕЦИЙ Leaf10.

Как и старый 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) можно посмотреть в 03.01. ГЕЛЬВЕЦИЙ Leaf10ТЕМА #14, АБЗАЦ #37903.01. ГЕЛЬВЕЦИЙ Leaf10.
***

Следующее улучшение программы не столько добавит, сколько убавит. По правилам игры 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 (Вс Авг 18, 2019 1:28 pm), всего редактировалось 1 раз(а)
Gudleifr
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Сб Авг 17, 2019 10:21 pm

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. НЕОДНОРОДНОСТИ ИГРЫ? Ну, как бы, цель данной работы, как раз, показать легкость добавления в игру любых желаемых "фигур" со своими правилами. Видно, что при достаточно простых правилах игры и очень простой синхронизации игровых событий добавлять в игру новые "фичи" можно без проблем.

...
Gudleifr
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Вс Авг 18, 2019 2:48 pm

Программа целиком:

Код:
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
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Вс Авг 18, 2019 3:06 pm

Что мешает воспроизведению этого результата на своем компьютере непрограммисту?

Во-первых, мы видим, что порядок фрагментов кода при обсуждении программы и в готовом тексте сильно отличаются. Очень трудно отследить связь одного с другим.
Во-вторых, для того, чтобы поиграть с программой пользователь должен знать Visual Basic. И не столько те команды, которые что-то делают, но, наоборот, всякие условности: что можно писать на одной строке, а что нельзя, с чего надо начинать и чем заканчивать, что тут выполняется, а что запоминается или просто объявляется..?

Причем, знание этих особенностей Visual Basic в случае .NET - чистая условность. С тем же успехом я мог писать, например на C#. Балет другой, но настолько же пустопорожний... Все равно, с обоих языков все будет переведено в язык CIL (этакий ОО-язык ассемблера). Так может этот CIL и есть настоящий BASIC современного МастДая?
Будем разбираться...

03.01. ГЕЛЬВЕЦИЙ Mssicm10

Кто и с кем тут взаимодействует?
В наличии два пользователя - ПРОГРАММИСТ и ИГРОК.
ПРОГРАММИСТ лупит по клавишам, посылая ТЕКСТОВОЕ сообщение редактору-БЛОКНОТУ, который преобразует его в два новых - ПРОГРАММУ и СКРИПТ (второе - только для удобства).

СКРИПТ:
Код:
@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. В 03.01. ГЕЛЬВЕЦИЙ Leaf10ТЕМА #43, АБЗАЦ #41903.01. ГЕЛЬВЕЦИЙ Leaf10 я предлагал написать простенькую реал-таймовую игрушку, для тренировки в написании процедуры ОК. Так вот, предлагаемая программа таковой не является. Конечно, сообщения от мышки и клавиатуры, я проверяю в одной точке программы (на каждом тике таймера), однако другие "сообщения" - от взрывов и попаданий ракет - через эту точку не проходят... Пока программа маленькая, это работает, но если мы ее будем усложнять и, более того, разрешать это делать пользователю, придется упрощать структуру взаимодействия. Надо будет проводить весь обсчет событий в цикле перебора игровых предметов на каждом ходу, без их взаимодействий иначе, чем через передачу сообщений. Потребуется создать очередь сообщений: "клавиша", "взрыв", "попадание"...
Gudleifr
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Gudleifr в Чт Окт 03, 2019 9:51 pm

P.P.S. Раз уж вспомнил о FORTH, попробую классифицировать код программы.

Начал я, очевидно, с q-кода (псевдокода). Убогого. Гораздо более убогого, чем в первых главах первого тома заметок. С одной стороны, как я указал выше, он плохо переводится в f-код (на Visual Basic). С другой, он и читается-то очень плохо. Почему так получилось? По причине выбора способа программировать - быдлокодерского. Получилось, что не я управляю структурой программы, а WIN API с ее ОО-заморочками.

Дальше... Ну перевели мы q-код в f-код (руками в БЛОКНОТЕ), а дальше? Теперь, родной мастдайный vb.exe создает РЕЗУЛЬТАТ в черт-знает-каком коде. Нас это не волнует (в этом прелесть BASIC-ов). Главная добродетель программиста - понять, когда надо перестать копать в ущерб делу.

Однако, один "высокоуровневый" (на уровне f-кода) момент надо отметить. Т.к. Visual Basic - обычный высокоуроневый язык, то мы вправе ожидать, что ПРОГРАММА будет состоять из r-фрагментов (обычных подпрограмм). Но можно видеть, что в двух самых главных местах ПРОГРАММЫ это избыточно. Телепаторы и Рисователи Органов более естественно выглядели бы в виде а-фрагментов (возвращающих управление в заранее определенную точку - в соответствующий цикл).

Наличие двух "точек возврата" настораживает - получается, что у нас два процесса, которые могут взаимодействовать непредсказуемым образом. выход тут один. Сделать один из них исключительно читающим. Рисователи должны только использовать данные программы для рисования, но, ни в коем случае, не изменять их.

Конечно, a-код проще r-кода, но воспользоваться этим на языках высокого уровня трудно. На языке ассемблера или FORTH было бы посимпатичнее (да, даже, на старом GW-BASIC)...
Gudleifr
Gudleifr
Admin

Сообщения : 1216
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

03.01. ГЕЛЬВЕЦИЙ Empty Re: 03.01. ГЕЛЬВЕЦИЙ

Сообщение автор Спонсируемый контент


Спонсируемый контент


Вернуться к началу Перейти вниз

Вернуться к началу


 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения