Вообще я не специалист, но эффективные решения - это то, над чем мне нравится ломать голову.
Hidden text
И кстати о движках вообще любых мне представляется мыслить немаловажным. И о движетелях (на последний патентуемый мной движетель (физический, не виртуальный) - мне против правил сместили дату приоритета в ФИПСе, и я решил не тратить средства на то, что кто-то за мой счёт будет нарушать мои права, и забросил заявку).
И вот эта публикация и по движку генератора карт трассировок маршрута следования (предыдущая публикация) - но про новый вариант реализации, но гораздо шире - инкапсуляцию самодельных циклов. Мне как-то сделали замечание, что технически плохо развернуто, поэтому (хотя скорее всего я полировал-бы код в любом случае) я решил исправить положение и заодно убрать лишний ассемблер, он мне самому мозолил глаз, но очень помог. И хочется заметить - у каждого могут быть свои методы решения задач, поэтому и представления о коде могут принципиально отличаться. Но это не о разности, это больше о остатках, если уже смотреть с точки зрения математики.
Данный конвейер генерирует-нарезает карты, такова его суть. Но генератор - это всего лишь инструмент, на его месте может оказаться всё что угодно. Что делает данный манипулятор вкупе с генератором...
ну наверное это можно сравнить с построчной генерацией не строковых объектов (векторов), таково моё видение оптимального решения ТЗ алгоритма, и нарезкой разных отрезков на линии. Это в общем не суть публикации. А так может быть любой инструмент для работы с данными в многоуровневом конвейере. Это не простая пятиминутная публикация делалась по вечерам после работы (1-1.5 часа в сутки), и хоть это и не от профессионала, но может быть интересно по выводу.
Hidden text
И кроме того, когда мне усиленно навязывали алгоритм Брезенхема (ТЗ которого выполняется попиксельно), я сразу сказал что мой "трактор" будет ехать всё время не сворачивая по пути - прямо, и этой концепции и придерживался до конца, просто автоматически генерируя данные о трассе, которая вовсе не прямая. И когда мне потом сказали что ТЗ - генерации карт тупое (так и не сумев наверное приспособить алгоритм Брезенхема под сэйв карты), я понял что это победа, так как по карте работать всегда лучше и надёжнее, а мне пишет просто хейтер всего (как выяснилось позже он даже хейтит человеческие языки цивилизации, переписывая их правила разом, такие уникумы оказывается тоже бывают, и при этом ещё и заявляя что никогда и никаких правил не нарушал, ну видимо всё человечество для этого индивида - ничто). Напомню, что мой алгоритм, против алгоритма Брезенхема, в ТЗ генерации карты выполняет работы меньше, правда хейтер тоже вероятно попотел, так как публикация на вики, об алгоритме Брезенхема сильно изменилась с того времени, а сам он называл предыдущую странной, ну это правда уже увидев что сделано мной. Когда я читал старую публикацию - сразу понял что вариант не мой, и моё ТЗ в корне отличалось - мне не было нужды генерировать линию, мне была нужна карта. Что касается хейтера - он и правда пытался искажать правила человеческих языков, заявляя, что законченное предложение речи или письма не может передавать смысла и интонации. Ну может это был свихнувшийся бот, который возомнил себя божеством, я не уверен, но на этом с ним я и закончил. Такого кощунства терпеть вряд-ли кто стал-бы, тем более тот заявлял, что его миссия - восстановление справедливости, и поэтому правила человеческой речи теперь вот такие, то-ли он сверхчеловек, я так и не понял, но сделав неоднократное замечание на очевидность его искажений действительности - я оставил его общество (это вообще на другом форуме, кишащем мошенниками и напоминающем дно болота, что даже не хочется давать ссылку). Но благо, Хабр хранит все публикации. И имеется такое свидетельство о алгоритме Брезенхема.
Принцип работы алгоритма Брезенхема очень простой. Берётся отрезок и его начальная координата x. К иксу в цикле прибавляем по единичке в сторону конца отрезка. На каждом шаге вычисляется ошибка — расстояние между реальной координатой y в этом месте и ближайшей ячейкой сетки. Если ошибка не превышает половину высоты ячейки, то она заполняется. Вот и весь алгоритм.
https://habr.com/ru/articles/185086/
Спасибо автору публикации. Надеюсь, что перекраивание всех человеческих языков цивилизации, тому хейтеру (что я столкнулся на другом форуме) точно не "по зубам", а до установления чьей-то личной справедливости и вовсе до коллапса вселенной.
Я не противник алгоритма Брезенхема, но я понимаю что у него совершенно другое задание, хоть и есть общие грани. Пусть моё ТЗ кому-то кажется тупым - меня это мало трогает, уверен что движение по картам - удобно и практично.
Сначала небольшое отступление по объяснениям, под спойлером.
Hidden text
С объяснения почему ассемблер всё-же интересен: когда конечное представление кода всё ещё не проглядывается целиком и чётко (всё это в рамках проекта описываемого и в прошлой публикации, чисто для себя я назову его "Око", это если что про машинное зрение).
Здравствуйте. Есть желание поделиться опытом организации циклов (превью я выбрал из сгенерированных больше тянущее на образовательные настроения, поскольку в итоге я и сам для себя уяснил нечто, чего ранее не знал, да и превью мне нравится - это как раз то как я примерно начинал писать движок - у робота вроде как всего одна рука, вторую он поднял и всем своим видом выражает полное недоумение, потому как ТЗ у него по описанию картинки жонглировать шариками капсулами, а по его виду у него вместо выполнения ТЗ в результате имеется один вопрос - "КАК?!?!?" :), когда он даже не знает что такое жонглировать, но не смотря на то, что робот с картинки однорук и однобок - он выполняет ТЗ делая намного меньше операций, чем популярные алгоритмы близкие по ТЗ, но тем не менее имеющие другое ТЗ ). Не ожидайте что это прямо такая лекция от специалиста, это просто то, чем я мог-бы поделиться интересным на данном этапе в рамках проекта. Из прошлой публикации по ссылкам можно перейти на код, не блещущий глянцем, но зато "матовый" оттенок ассемблера там имел место быть по следующим причинам:
для доведения до глянца алгоритма генератора нужно было иметь точное представление формул, а их у меня не было, и формулы расчётов буквально отлаживались в отладчике - искались ошибки, после обнаружения таковых автоматического тестировщика, и формулы для отладки движка генератора карт ещё при этом и растасовывались по коду, для сокращения числа их применения, ну или их частей - такое тоже было. Почему я не использовал case тоже можно объяснить достаточно просто : в конечно счёте применение ассемблера имело место - ознакомится с требуемым его функционалом как можно больше, наработать навык и потом применить. Конечно это не бог весть какое достижение, но тем не менее "рабочая камера" движка генератора карт преобразилась с такого вида, но это часть замысла, точнее небольшая часть его реализации, кому-то может показаться что мало значащая, но тем не менее:
{%REGION 'Engine'}
asm
JMP p
end;
p1:
StringGrid1.Cells[px^, py^] :=
IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1';
// запись правого столбца
p := @p3;
goto p4;
p2:
x1:=x+1;
y1:=y+1;
StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0';
// автоматическое забивание координат в местах скоса трассы
p := @p3;
goto p4;
p3:
x1:=x+1;
y1:=y;
StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0';
// автоматическое забивание координат трассы на прямых участках
p4:
x:=x+xInc;
{%ENDREGION}
до такого
{%REGION 'Engine'}
asm
JMP p
end;
p1:
StringGrid1.Cells[px^, py^] := IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1';
//запись правого столбца
p := @p3;
goto p4;
p2:
y1:=y+1;
//автоматическое забивание координат в местах скоса трассы
p := @p3;
goto p5;
p3:
y1:=y;
p5:
x1:=x-xInc;
StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0';
//автоматическое забивание координат трассы на прямых участках
p4:
x:=x+xInc;
{%ENDREGION}
. Не такое уж и большое достижение в модификации структуры, но тем не менее это радует, и это опыт. Замена в строке 18 строки 19 связана с тем, что далее у меня от входящего параметра изменяется направление отсчёта, и только, то-есть это штатная замена кода в ходе смены этапов работы, а не улучшение.
Итак алгоритм работает уже по однажды сделанным расчётам отрезков на горизонтальной линии, а ходе в исполнения заполнения карты делает один раз расчёт в каждом первом пикселе каждой строки, по мной выведенной формуле, и в начале каждого описываемого отрезка последней строки карты (вычисляет откуда по трассировке можно попасть в текущий пиксел после обрыва предыдущей линии, то есть карта - чередование параллельных трасс (линий)), но по более сложной формуле, которая заменяет собой блок кода проверок условий (решил не заморачиваться и описать всё математическими условиями, это лучше чем список противоречивых условий, сопрягающимся по правилам, которые тоже пришлось-бы описать).
Ближе к теме
Снова отступление с пояснительным материалом.
Hidden text
Задача генератора карт раскрыта в моей предыдущей публикации, впрочем как и проекта в целом. Суть в том, как генератор генерирует трассировку для алгоритма читающего карту трассировок. А генерирует он её совсем иначе - сам генератор может следовать совсем иным путём, чем описывает сам это на карте. Это происходит в местах скоса прямой линии. Сама карта трассировки представляет собой описание следования по клеткам карты, и обозначает всегда окончание линии, чтобы алгоритм читающий карту всегда знал что он следует уже по новой линии. Таким образом карта трассировки - это набор параллельных трасс, которыми заполнена вся карта. Оканчивается чтение карты через счёт пройденных пикселов, информация о необходимом количестве которых прилагается к описанию карты, как и о координатах входа в карту, для алгоритма читающего карту.
Сам генератор всегда следует только ровными горизонтальными линиями так, как указано на четвёртом слое анимации над цифрой 4
, но в случае когда угол параллельных линий больше 90 градусов, направление следования счётчиков генератора будет меняться, сейчас это не реализовано, но доделка до 180 градусов будет означать, что алгоритм генератора закончен, так как все линии, что от 180 до 360 градусов, параллельны линиям, что от 0 до 180 градусов. То-есть генератор в месте скоса просто пишет, что пришёл, в данные координаты, с тех же где и был, но координатой Y выше. В этом его основная хитрость - не нужно обязательно ходить теми путями что описываешь, просто нужно обязательно их знать наверняка. Скос описывается только назад, чтобы избежать проверки условия не выхода за пределы карты, ведь при приличных размерах карты такое стало бы большим довеском к объёму производимых генератором работ.
Сам способ обработки визуальной информации - замены свёрточных нейросетей, мной был описан в сети начале 22-го года (в марте), и только потом тут, так как я вообще работаю на стройках, и доступ к компьютеру у меня до пенсии ограниченный.
Публикация была не однодневная, во время её редактирования я переделывал и код, и всё это где-то около часа в день. Несколько раз оформление изменялось, позиционно, в целом её трудно обозревать, сам алгоритм - результат работы с собственным подходом, и разумеется такие работы влекут за собой множество выводов, может дерзких, а может скромных, может кто-то будет против, а кто-то напротив - за. И это не суть, суть в материале и в некотором обозрении - выводе, с уникальной точки зрения, с которой каждый волен соглашаться или нет.
Итак манипулятор, нарезающий следуя горизонтальной линии разные отрезки в местах скоса трассы. По заданию получается что есть частые шаги - несколько стандартных и одинаковых по размеру, которые через определённое количество дадут один редкий - изменённый на 1 пиксел. Как это рассчитывается можно было бы написать, но я думаю что с этой задачей может справиться практически любой. Я опишу самые интересные, на мой взгляд, расчёты.
Итак имеем частый шаг (стандартный) - stepFrequent, и редкий - stepRare.
Hidden text
В расчётах всё достаточно интересно, начиная от самого начала, а именно - расчётов. У нас по ТЗ линии прямые, и стало быть в случае скоса - скос по оси Y будет происходить всегда через определённое число пикселов, с небольшой разницей, что всегда будет оставаться дробная часть, которая суммируясь даст разовое изменение длины шага до скоса на 1 пиксел. Но это рассчитывается один раз, и расчёты достаточно не сложные, если кто-то знаком с Pascal Lazarus и его модулем Math, может прикинуть насколько я являюсь поклонником изощрённой логики, возможности которой предоставляет математика вкупе с программированием
stepFrequent := round(CaTanDeg);
StringDiv := frac(CaTanDeg);
StringDiv1 := 1 - StringDiv;
znak := sign(0.5 - StringDiv) * sign(StringDiv);
if StringDiv > 0.5 then stepsBig := round(1 / StringDiv1)
else
stepsBig := round(1 / StringDiv);//+1
if StringDiv = 0 then stepsBig := 1;
if CaTanDeg = 1 then
begin
znak := 0;
end;
stepRare := stepFrequent + znak;
bigStep := stepsBig * stepFrequent;
if bigStep = 0 then
begin
bigStep := stepRare;
stepRare := 0;
end;
SummSteps:= stepsBig+1;
, не скрою - мечтаю о языке программирования, который предоставлял-бы все возможности воплощения идей с меньшими ограничениями и меньшими затратами памяти. Если честно - расписывать логику этого банального расчёта не имею желание - можно пропускать, впереди более интересное.
Они теперь образуют набор шагов - stepsSetapInLine, и плюс ещё, возможно, в зависимости от угла, будет оставаться остаток частых шагов remainderstepsFrequent , и так же конечный, самый малый остаток remainder
Но что самое интересное, что для простоты вычислений всех этих величин изначально всё должно является остатком remainder - первая строка кода. Открыв это для себя я решил назвать это правилом "альфы-омеги", или правилом "всё есть остаток" .
Под спойлером положу общее изображение, вместе с изображением карты
Hidden text
Расчёты под спойлером далее появились позднее, когда я реорганизовывал циклы и убирал ассемблер.
Hidden text
Основной идеей, при организации циклов при удалении ассемблера, было то, что, в этой системе расчёта , изначально всё - остаток. То-есть всё как минимум может быть рассчитано за один проход с одним остатком, а именно линия по размеру оси X, в координате Y, которая может заключать в себе всего один отрезок (остаток или один частый шаг), и так же остатка может и не быть- всё может оказаться либо набором целых частых шагов, с целым редким шагом, или ещё и с остатком целых редких шагов, а самого остатка может и не быть как такового.
remainder:=RazmerX;
remainderCicles:=0;///////////////////////////////////////////////////////////////
stepsSetapInLine:=0;
remainderstepsFrequent:=0;
remainderstepsFrequentCicles:=0;////////////////////////////////////////////////////////////////////
if remainder>bigStep+stepRare then begin
stepsSetapInLine:=trunc(RazmerX div (bigStep+stepRare));
remainder:=RazmerX - stepsSetapInLine*(bigStep+stepRare);
end;
if remainder>stepFrequent then begin
remainderstepsFrequent:=round(remainder div stepFrequent);
remainder:=remainder-remainderstepsFrequent*stepFrequent;
end;
remainderstepsFrequentCicles:=sign(remainderstepsFrequent)*2;//+sign(stepsSetapInLine);
if remainderstepsFrequentCicles=0 then remainderstepsFrequentCicles:=remainderstepsFrequentCicles+sign(stepsSetapInLine);
remainderCicles:=1+sign(remainder);//sign(remainder)+sign(remainderstepsFrequentCicles);
alternation:=2*sign(stepsSetapInLine);
if stepsSetapInLine = 0 then begin
alternation:=1;
end;
steps1:=sign(remainder)+stepsSetapInLine*(SummSteps)+remainderstepsFrequent;
То-есть итоговая задача генератора карт - создать массив (карту) с координатами попиксельного перемещения в каждую следующую позицию, с обозначением начала каждой условной параллельной линией на карте (я выбрал обозначать каждое начало нового линии).
Hidden text
Hidden text
Для этого в блоке кода Engine, генератора следующего по горизонтальным линиям оси X (по линии на каждую координату Y, или просто построково),
for BFCount :=Biger- 1 downto 0 do begin
{%REGION 'Engine'}
asm
JMP p
end;
p1:
StringGrid1.Cells[px^, py^] := IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1';
//обозначение начала линии записью единицы в третье поле
p := @p3;
goto p4;
p2:
y1:=y+1;
//автоматическое забивание координат в местах скоса трассы: изменение координаты Y
p := @p3;
goto p5;
p3:
y1:=y;
p5:
x1:=x-xInc;// координата X меняется всегда
StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0';
//автоматическое забивание координат трассы на прямых участках
p4:
x:=x+xInc;
{%ENDREGION}
end;
предусмотрены следующие функции (описаны в комментариях в коде)
1) обозначение начала линии записью единицы в третье поле,
2)автоматическое забивание координат в местах скоса трассы: изменение координаты Y,
3)автоматическое забивание координат трассы на прямых участках.
И схема чередования циклов вырисовывается примерно такая.
Для описания схемы предположим что изначально есть всё - наборы шагов из нескольких частых и одного редкого, остатка коротких шагов, и остатка.
Стоит отметить справедливости ради, что в среде Lazarus помимо ассемблера есть всё, чтобы реализовать даже самодельные циклы достаточно просто. Их я писал свои, почему так - под спойлером. Это полностью предыдущий, почти, алгоритм генерации, но в котором уже меньше неопределённостей, лишнего ассемблера, и известно всё наперёд из предварительных расчётов, и следовательно рационально использовать цикл с параметром.
Hidden text
Сюда-же под спойлером приведу фрагмент кода программы, чтобы можно было сразу обозначать строки и переменные, и это тоже поэтапно, сначала только организация циклов (вникать даже в эту часть не просто, для этого нужно вникнуть в ТЗ, изучить схемы), этот фрагмент кода заполняет карту до последней строки, для "нарезки" которой имеется сложная формула, но и об этом позже. А сейчас сразу скажу, что мне не заходят современные парадигмы программирования и архитектур процессоров, поэтому циклы у меня самодельные, с ориентацией на флаг Zero (ноль - начало отсчёта в любой математической системе, а для всех языков программирования и любых парадигм действительным определением цикла с параметром является то, что цикл с параметром - это цикл, число повторений которого заранее известно).
//2
if Not (count1 = 0) Then begin
marking1:
Inc(y);
p := @p1;
x := xRazmerX;
TracerY := y - steps1;
formula1:= stepFrequent * y + znak *trunc(y / (SummSteps));
if TracerY < 0 then
begin
TracerX := RazmerX - formula1;
if TracerX < 0 then TracerX := 0;
TracerY := 0;
end else TracerX := 0;
//3
BremainderstepsFrequentCicles:=remainderstepsFrequentCicles;//3
BstepsSetapInLine:=stepsSetapInLine;
Balternation:=alternation;
BstepsFrequentCicles:=stepsBig;
BstepFrequent:=stepFrequent;
//4
count2:=remainderCicles;
if Not (count2 = 0) Then begin
marking2:
//5
count3:=BremainderstepsFrequentCicles;
if Not (count3 = 0) Then begin
marking3:
//6
count4:=BstepsSetapInLine;
if Not (count4 = 0) Then begin
marking4:
//7
Bstep:=BstepFrequent;
//8
count5:=Balternation;
if Not (count5 = 0) Then begin
marking5:
//9
count6:=BstepsFrequentCicles;
if Not (count6 = 0) Then
begin
marking6:
//10
count7:=Bstep;
//count7
if Not (count7 = 0) Then begin
marking7:
{%REGION 'Engine'}
asm
JMP p
end;
p1:
StringGrid1.Cells[px^, py^] := IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1';
//запись правого столбца
p := @p3;
goto p4;
p2:
y1:=y+1;
//автоматическое забивание координат в местах скоса трассы
p := @p3;
goto p5;
p3:
y1:=y;
p5:
x1:=x-xInc;
StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0';
//автоматическое забивание координат трассы на прямых участках
p4:
x:=x+xInc;
{%ENDREGION}
dec(count7);
if Not (count7 = 0) then goto marking7;
end;
p := @p2;
///10
//11
dec(count6);
if Not (count6 = 0) then goto marking6;
end;
//12
Bstep:=stepRare;
BstepsFrequentCicles:=1;
//13
dec(count5);
if Not (count5 = 0) then goto marking5;
end;
//14
BstepFrequent:=stepFrequent;
BstepsFrequentCicles:=stepsBig;
//15
dec(count4);
if Not (count4 = 0) then goto marking4;
end;
//16
BremainderstepsFrequentCicles:=1;
BstepsSetapInLine:=1;
Balternation:=1;
BstepsFrequentCicles:=remainderstepsFrequent;
//17
dec(count3);
if Not (count3 = 0) then goto marking3;
end;
//18
if BremainderstepsFrequentCicles = 0 then begin
BstepsSetapInLine:=1;
Balternation:=1;
end;
BstepFrequent:=remainder;
BremainderstepsFrequentCicles:=1;
BstepsFrequentCicles:=1;
//19
dec(count2);
if Not (count2 = 0) then goto marking2;
end;
//20
dec(count1);
if Not (count1 = 0) then goto marking1;
end;
Быть может когда-то сделаю сам что-то, типа компилятора.
Черные прямые стрелки вниз обозначают начала циклов до их конца. Синие кривые стрелки означают данные приходящие из вне. Зелёные стрелки - возвращение к исходным данным перед началом цикла, в который они возвращаются. Красные и коричневые стрелки указывают на изменение исходных данных после выполнения каких-либо циклов . В коде под спойлером каждый блок схемы обозначен цифрой в комментарии. Кто захочет в этом разобраться - разберётся. В целом получается перспектива неплохой нодовой системы организации циклов.
Вывод. Разумеется это частная точка зрения:
И если понятна вся эта схема, то можно понять и то, что я скажу далее: становится очевидным чтобы иметь хотя-бы как опцию - второй вариант компиляции цикла с параметром. А именно - старт и выход из цикла должны быть ориентированными на флаг процессора Zero , который проверяется аппаратными средствами процессора, и в оговариваемом варианте компиляции, рассчитанной на остановку цикла по значению счётчика равному нулю (если ноль, то цикл не должен запуститься, а если уже запущен - должен оборваться в конце без возврата в начало). Флаг должен проверяться при старте цикла, и если он не указывает на равенство счётчика нулю, то запустить цикл, и действие над счётчиком можно делать в конце или в начале, а вот уже при работе цикла проверять флаг Zero лучше в конце, чтобы не возвращаться лишний раз в начало, так как манипуляции над циклами могут оказаться очень сложными даже не в зависимости от кода, а от обрабатываемых в них данных. То-есть тогда используются аппаратные средства процессора для проверки условия, это и есть в моём понятии цикл с параметром.
В псевдокоде это примерно так:
count:=myVariable;
if Not (count = 0) Then begin
label1:
...;
...;
...;
dec(count);//or inc (count)
if Not (count = 0) then goto label1;
end;
Hidden text
Сейчас это компилируется с проверкой на флаг Zero, но при этом не даёт прироста к скорости, и это в общем-то следствие вшитых в процессор убеждений (так называют нынешние прошивки и процессоры). Винить за это было-бы неправильно, но зато остаётся перспектива создания своего процессора. Пусть меня сильно не ругают за дерзость, потому что я создаю свои алгоритмы, мне они видятся вполне естественными, и к естеству движется развитие любой архитектуры. Как в ранней публикации сказали в комментариях что я пишу для себя - не совсем согласен, я пишу под свободный от технологических убеждений hardware (так как эти убеждения являются по сути излишним навязываемым контролем и ограничениями, от которых больше пользы для мошенников и людей экспроприировавших чужие права, чем для безопасности общества и информационной безопасности в том числе), время которого придёт. Можете считать это хоть частной точкой зрения, хоть религией, я считаю это рациональным выбором, именно в таком тоне можно непредвзято вести диалог в обществе, потому что остальные версии больше про религию и социум, пропаганду, а мне ближе рациональное и научный агностицизм, а в обществе рационально подразумевать общественным - право, потому что вера - это личное. Это и этика и рациональный подход, хоть стиль, кому как угодно, я считаю рациональным подходом.
Да, по убеждению многих - это не правильно, но под спойлером я описал своё отношение к этой точке зрения. Почему ещё хотелось-бы создать свой компилятор, потому что с ориентацией на ноль я так-же заменяю блоки проверок условий, используя модуль Math и его математические функции. На мой взгляд это выглядит более естественно и понятнее со стороны, чем целые листинги проверок условий.
Небольшой эпитет под спойлером.
Hidden text
Вообще тенденция такая, что компьютеры больше и больше приспосабливают под симуляции процессов, и стало быть языки программирования неуклонно будут приближаться к языкам явлений, будь то физика, общество, или жизнь, архитектура компьютеров будет стремиться в одном направлении - приближение к естественному. Поэтому в будущем, мне представляется, языки станут красивее и совершеннее (скорее всего альтруисты ООП задавались именно такой целью, но мне например больше заходит более низкий уровень, на котором можно описать всё без языковых ограничений), а людей характеризуют их поступки, это не программируется.
После вывода, генерация последней строки карты
Что касается генерации последней строки карты, то я просто добавил часть циклов и немного кода, под спойлером
Hidden text
p := @p6;
x := xRazmerX;
TracerY := y - steps1;
formula1:= stepFrequent * y + znak *trunc(y / (SummSteps));
if TracerY < 0 then
begin
TracerX := RazmerX - formula1;
if TracerX < 0 then TracerX := 0;
TracerY := 0;
end else TracerX := 0;
BremainderstepsFrequentCicles:=remainderstepsFrequentCicles;//3
BstepsSetapInLine:=stepsSetapInLine;
Balternation:=alternation;
BstepsFrequentCicles:=stepsBig;
BstepFrequent:=stepFrequent;///3
count2:=remainderCicles;//4
steps := stepsBig;
if Not (count2 = 0) Then begin
marking8:
count3:=BremainderstepsFrequentCicles;//5
if Not (count3 = 0) Then begin
marking9:
count4:=BstepsSetapInLine;//6
if Not (count4 = 0) Then begin
marking10:
Bstep:=BstepFrequent;//7///7
count5:=Balternation;//8
if Not (count5 = 0) Then begin
marking11:
count6:=BstepsFrequentCicles;//9
if Not (count6 = 0) Then
begin
marking12:
count7:=Bstep;//10
//count7
if Not (count7 = 0) Then begin
marking13:
{%REGION 'Engine'}
asm
JMP p
end;
p6:
StringGrid1.Cells[px^, py^] := IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1';
//запись правого столбца
p := @p8;
goto p9;
p7:
y1:=y+1;
//автоматическое забивание координат в местах скоса трассы
p := @p8;
goto p10;
p8:
y1:=y;
p10:
x1:=x-xInc;
StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0';
//автоматическое забивание координат трассы на прямых участках
p9:
x:=x+xInc;
{%ENDREGION}
dec(count7);
if Not (count7 = 0) then goto marking13;
end;///10
//трассировку начальной ячейки каждого шага последней обрабатываемой строки кроме правого столбца
if steps = 0 then steps := SummSteps;
if RazmerX > RazmerY then begin
Dec(steps1);
TracerX := x - ( znak*sign(trunc((1+sign(y-trunc(y/(stepsBig+1))*(stepsBig+1)-steps)/2))) + formula1)+1;
if TracerX<1 then begin
TracerY := y - steps1;
TracerX := 0;
if TracerY < 0 then TracerY := 0;
end;
end else begin
Dec(steps1);
TracerY := y - steps1;
TracerX := 0;
end;
p := @p6;
Dec(steps);////////
dec(count6);
if Not (count6 = 0) then goto marking12;
end;//11
Bstep:=stepRare;//12
BstepsFrequentCicles:=1;
dec(count5);
if Not (count5 = 0) then goto marking11;
end;//13
BstepFrequent:=stepFrequent;//14
BstepsFrequentCicles:=stepsBig;
dec(count4);
if Not (count4 = 0) then goto marking10;
end;//15
BremainderstepsFrequentCicles:=1;//16
BstepsSetapInLine:=1;
Balternation:=1;
BstepsFrequentCicles:=remainderstepsFrequent;///16
dec(count3);
if Not (count3 = 0) then goto marking9;
end;//17
if BremainderstepsFrequentCicles = 0 then begin
BstepsSetapInLine:=1;
Balternation:=1;
end;
BstepFrequent:=remainder;//18
BremainderstepsFrequentCicles:=1;
BstepsFrequentCicles:=1;
dec(count2);
if Not (count2 = 0) then goto marking8;
end;//19
На борьбу с 68 строкой я потратил немалую часть выходного дня, но так и не побелил её, как ни напрягался. Всё таки языковые ограничения дают о себе знать.
И упомяну строку 71, которая заменяет блок проверки условий вот этой своей частью znak*sign(trunc((1+sign(y-trunc(y/(stepsBig+1))(stepsBig+1)-steps)/2))), я пытался составить "портянку" из списка условий, но всегда спотыкался на чём-нибудь, в итоге не выдержал и просто описал условия математическими функциями. Если кратко - там проверяется проверка условия вмещения в один интервал другого, причём размеры обоих могут меняться от случая к случаю, и описать просто словами у меня не хватило способностей. Но думаю, что моя организация циклов в принципе итак даёт понять к чему я стремлюсь в моём коде. Это не про Pascal, и наверное вообще не под нынешние оптимизации. Что касается последнего приведённого выражения математического, то в принципе любой результат вычислений попадает автоматически под выставление флага Zero в ноль или единицу, и конечно жаль что модуль Math не делает этого с заключённым в скобки, а так было-бы именно так как мне видится естественным (потому что ноль - начало отсчёта, а параметр - заведомо известное).
На этом у меня пока всё, с переделкой я провозился очень много, обидно за строку 68 с последнего листинга тут, но ничего не поделаешь. Проект генератора для Lazarus, тестировалось на ОС Ubuntu https://github.com/Andrei-Y/A-trace-map-generator/blob/main/generator_remake_public.zip
Это просто этап исправления организации циклов в связи изыманием лишнего ассемблера, который мне помог, как минимум, в отладке математических выражений, как минимум того, что заменило блок проверок условий. То-есть угол от 0 до 90, вводится в первый TEdit сверху, и два что ниже - размеры по оси X и Y. У меня пока всё. Наверное это всё-таки про алгоритмизацию, но вот чтобы это указывало на конкретный язык программирования - это вряд-ли. Но все языки программирования так или иначе стремятся к естеству, а значит наверное это всё-же как-то относится к их изучению. По мне например естественно не наделять машину какими-то сверхсложными всезаменяющими операциями, так как всё сложное состоит из простого, которое в реальности всегда изучается и совершенствуется.
Надеюсь, что технически развернул основную идею генератора карт. Осталось его немного доделать (добавить "симметричные" расчёты для углов от 90 до 180, и всё), основная часть времени ушла на общую отладку и не бесполезное "блуждание" с ассемблером вместо фонарика, и совсем немного на описание своих пожеланий и представлений о перспективах архитектур и языков программирования. Таблица мной использована для визуализации процесса отладки вне отладчика (чем больше инструментов отладки - тем лучше).
На этом пока всё, прошу простить, если отнял чьё-то время за зря. Желаю всем взаимопонимания. Для интереса добавил ещё опрос, немного с странноватой структурой, если что не так - прошу извинить.
accurate_random Автор
И ещё мне кажется странным то, что состояние флага Zero нельзя получать из любой ячейки памяти кэша. Технически это реализуемо, хоть и с костылём. Костыль не сложный - нужно в процессоре сделать шину, на которую ячейка памяти за один такт выложит содержимое, которое просто пройдёт транзисторные ключи, которые выставят флаг или в 0 или в 1. На самом деле это очень полезная вещь-бы была. Тоже сначала задумывался над своим компилятором и языком, но в итоге понял что всё это имеет мало смысла до тех пор, пока я не знаю что именно нужно. И теперь мне становится понятным что начинать надо именно с процессора, и именно с такой функцией кэша. Один набор транзисторных ключей на весь кэш - совсем немного, мне не понятно почему это до сих не реализовано.
Вывод запоздалый, во время работы над публикацией и кодом - я не смог к нему прийти, хоть и пытался слепить какое-то подобие.