# Polishing stage. Homemade loops with a parameter in a multi-link “manipulator” for working with data (map generation)

Thanks to the author of the publication. I hope that the reshaping of all human languages of civilization is definitely not too tough for that hater (that I encountered on another forum), but before the establishment of someone’s personal justice and even before the collapse of the universe.

I am not opposed to the Bresenham algorithm, but I understand that it has a completely different task, although it has common facets. Even if my technical specification seems stupid to someone, it doesn’t bother me much, I’m sure that moving using maps is convenient and practical.

## First, a short digression on the explanations, under the spoiler.

## Hidden text

#### From an explanation of why assembler is still interesting: when the final representation of the code is still not visible entirely and clearly (all this is within the framework of the project described in the previous publication, purely for myself I will call it “Eye”, if it’s something about computer vision).

Hello. There is a desire to share my experience of organizing cycles (I chose the previews from those generated that were more educational, because in the end I figured out something for myself that I didn’t know before, and I like the previews – this is exactly how I started writing the engine – the robot seems to have only one arm, he raised the other and with his whole appearance expresses complete bewilderment, because according to the description of the picture, his technical specification is to juggle ~~balls ~~capsules, and by the look of him, instead of fulfilling the technical requirements, the result is one question – “HOW?!?!?” :), when he doesn’t even know what juggling is, but despite the fact that the robot in the picture is one-armed and one-sided, he fulfills the specifications by doing much fewer operations than popular algorithms that are similar in terms of specifications, but nevertheless have a different specification). Don’t expect this to be just such a lecture from a specialist, this is just something interesting that I could share at this stage within the project. From the previous publication, using the links, you can go to the code, which is not shiny, but there was a “matte” shade of the assembler there for the following reasons:

to polish the generator algorithm, it was necessary to have an accurate representation of the formulas, but I didn’t have them, and the calculation formulas were literally debugged in the debugger – errors were looked for after the automatic tester found them, and the formulas for debugging the map generator engine were also shuffled around code, to reduce the number of their use, or their parts – this also happened. Why didn’t I use ** case **can also be explained quite simply: in the end, the use of assembler took place – familiarize yourself with the required functionality as much as possible, develop the skill and then apply it. Of course, this is not God knows what an achievement, but nevertheless, the “working chamber” of the map generator engine has changed from this appearance, but this is part of the plan, or rather a small part of its implementation, which may seem to some to be of little significance, but nevertheless:

```
{%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}
```

before this

```
{%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}
```

. It’s not a huge achievement in modifying the structure, but it’s still satisfying and an experience. The replacement in line 18 of line 19 is due to the fact that further the counting direction changes from the input parameter, and only that is, this is a standard replacement of the code during the change of stages of work, and not an improvement.

So the algorithm works based on the calculations of segments on a horizontal line that have already been made, and during the execution of filling the map, it makes one calculation in every first pixel of each line, according to the formula I derived, and at the beginning of each described segment of the last line of the map (it calculates where from the trace you can get to the current pixel after the break of the previous line, that is, the map is an alternation of parallel traces (lines)), but using a more complex formula that replaces a block of code for checking conditions (I decided not to bother and describe everything with mathematical conditions, this is better than a list of contradictory conditions , mating according to rules that would also have to be described).

## Closer to the topic:

Again, a digression with explanatory material.

## Hidden text

The task of the map generator was disclosed in my previous publication, as well as the project as a whole. The point is how the generator generates a trace for the algorithm that reads the trace map. And it generates it in a completely different way – the generator itself can follow a completely different path than it describes on the map. This occurs where a straight line is beveled. The trace map itself is a description of following the cells of the map, and always indicates the end of the line, so that the algorithm reading the map always knows that it is already following a new line. Thus, a trace map is a set of parallel traces that fill the entire map. Reading of the map ends by counting the traversed pixels, information about the required number of which is attached to the map description, as well as about the coordinates of the entrance to the map, for the algorithm reading the map.

The generator itself always follows only straight horizontal lines as indicated on the fourth animation layer **above the number 4 **

but in the case when the angle of parallel lines is greater ** 90 **degrees, the direction of the generator counters will change, this is not implemented now, but it will be completed

**degrees will mean that the generator algorithm is finished, since all the lines that are from**

*180***before**

*180***degrees, parallel to the lines that from**

*360***before**

*0***degrees. That is, the generator at the place of the bevel simply writes that it came to the given coordinates, from the same where it was, but with the coordinate**

*180***higher. This is his main trick – you don’t necessarily need to follow the paths you describe, you just need to know them for sure. The bevel is described only backwards in order to avoid checking the condition of not going beyond the map, because with a decent size map this would be a big addition to the amount of work produced by the generator.**

*Y*The very method of processing visual information – replacing convolutional neural networks – was described by me online at the beginning of 22 (in March), and only then here, since I generally work on construction sites, and I have limited access to a computer until retirement.

The publication was not one-day, while editing it I also reworked the code, and all this for about an hour a day. The design has changed several times, positionally, in general it is difficult to review, the algorithm itself is the result of working with its own approach, and of course such work entails many conclusions, maybe bold, maybe modest, maybe someone will be against it, and someone on the contrary – for. And this is not the point, the point is in the material and in some review – conclusion, from a unique point of view, with which everyone is free to agree or not.

So a manipulator, following a horizontal line, cuts different segments in places where the route is beveled. According to the assignment, it turns out that there are frequent steps – several standard and identical in size, which after a certain number will give one rare one – changed to ** 1** pixel How this is calculated could be written down, but I think that almost anyone can cope with this task. I will describe the most interesting, in my opinion, calculations.

So we have a frequent step (standard) – ** stepFrequent**and rare –

**.**

*stepRare*## Hidden text

Everything in the calculations is quite interesting, starting from the very beginning, namely the calculations. According to the technical specifications, our lines are straight, and therefore, in the case of a bevel, the bevel along the Y axis will always occur after a certain number of pixels, with a small difference that there will always be a fractional part remaining, which, when summed up, will give a one-time change in the step length before the bevel by ** 1** pixel But this is calculated once, and the calculations are not quite complicated, if anyone is familiar with Pascal Lazarus and its Math module, you can estimate how much I am a fan of sophisticated logic, the possibilities of which are provided by mathematics coupled with programming

```
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;
```

, I won’t hide it – I dream of a programming language that would provide all the possibilities for implementing ideas with fewer restrictions and less memory consumption. To be honest, I don’t want to describe the logic of this banal calculation – you can skip it, there’s more interesting stuff ahead.

They now form a set of steps – ** stepsSetapInLine**and plus, perhaps, depending on the angle, there will be a remainder of frequent steps

**and also the final, smallest remainder**

*remainderstepsFrequent*

*remainder*But what’s most interesting is that for ease of calculation of all these quantities, initially everything should be a remainder ** remainder **– first line of code

**.**Having discovered this for myself, I decided to call it a rule “

**or rule**

*alpha-omega”***.**

*“everything is a remainder”*Under the spoiler I will put a general image, along with an image of the map

## Hidden text

The calculations under the spoiler further appeared later, when I reorganized the loops and removed the assembler.

## Hidden text

The main idea when organizing loops when removing assembler was that, in this calculation system, initially everything is a remainder. That is, everything can at least be calculated in one pass with one remainder, namely a line along the axis size ** X, **in coordinate

**which may contain only one segment (remainder or one frequent step), and also there may not be a remainder – everything can turn out to be either a set of entire frequent steps, with a whole rare step, or also with a remainder of entire rare steps, and the very there may not be a remainder as such.**

*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;
```

That is, the final task of the map generator is to create an array (map) with the coordinates of pixel-by-pixel movement to each next position, with the beginning designated by each conditional parallel line on the map (I chose to designate each beginning of a new line).

## Hidden text

## Hidden text

To do this, in the Engine code block, the generator following the horizontal lines of the axis ** X** (along a line for each coordinate

**or just line by line),**

*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;
```

The following functions are provided (described in the comments in the code)

1) designation of the beginning of the line by writing one in the third field,

2)automatically entering coordinates in places where the route is beveled: changing the Y coordinate,

3) automatic recording of route coordinates on straight sections.

And the cycle alternation scheme looks something like this.

To describe the scheme, let us assume that initially there is everything – a set of steps from several frequent and one rare, the remainder of short steps, and the remainder.

It is worth noting in fairness that in the Lazarus environment, in addition to the assembler, there is everything to implement even homemade loops quite simply. I wrote them my own, why so – under the spoiler. This is a completely previous, almost, generation algorithm, but in which there are already fewer uncertainties, unnecessary assembler, and everything is known in advance from preliminary calculations, and therefore it is rational to use a loop with a parameter.

## Hidden text

Here, under the spoiler, I will give a fragment of the program code so that you can immediately designate lines and variables, and this is also step-by-step, first only the organization of loops (it’s not easy to delve even into this part, for this you need to delve into the technical specifications, study the diagrams), this fragment the code fills the map to the last line, for which there is a complex formula for “cutting”, but more on that later. And now I’ll immediately say that modern programming paradigms and processor architectures do not suit me, so my loops are homemade, focused on the Zero flag (zero is the origin of reference in any mathematical system, and for all programming languages and any paradigms the actual definition of a loop with a parameter is that a loop with a parameter is a loop whose number of repetitions is known in advance).

```
//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;
```

Maybe someday I’ll make something myself, like a compiler.

Black straight down arrows indicate the beginning of the cycles to their end. Blue curved arrows mean data coming from outside. Green arrows – return to the original data before the start of the cycle in which they are returned. Red and brown arrows indicate changes in the source data after executing any cycles. In the code under the spoiler, each block of the circuit is indicated by a number in the comment. Anyone who wants to understand this will understand it. In general, we get the prospect of a good node system for organizing cycles.

Conclusion. Of course this is a personal point of view:

In pseudocode it goes something like this:

```
count:=myVariable;
if Not (count = 0) Then begin
label1:
...;
...;
...;
dec(count);//or inc (count)
if Not (count = 0) then goto label1;
end;
```

A small epithet under the spoiler.

## Hidden text

In general, the trend is that computers are becoming more and more suitable for simulating processes, and therefore programming languages will steadily approach the languages of phenomena, be it physics, society, or life, computer architecture will tend in one direction – closer to the natural. Therefore, in the future, it seems to me that languages will become more beautiful and more perfect (most likely the altruists of OOP set precisely this goal, but for example, I prefer a lower level at which everything can be described without language restrictions), and people are characterized by their actions, this is not programmed .

## After output, generation of the last line of the map.

As for generating the last line of the map, I just added some loops and a little code, under the spoiler

## 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
```

I spent a considerable part of the weekend struggling with line 68, but I still couldn’t whitewash it, no matter how hard I tried. Still, language restrictions make themselves felt.

And I’ll mention line 71, which replaces the condition checking block with this part of it ** znak*sign(trunc((1+sign(y-trunc(y/(stepsBig+1))(stepsBig+1)-steps)/2)))**, I tried to create a “footcloth” from a list of conditions, but I always stumbled on something, I couldn’t stand it and simply described the conditions with mathematical functions. In short, it checks the condition of fitting one interval into another, and the sizes of both can change from case to case, and I didn’t have enough ability to describe it in just words. But I think that my organization of loops, in principle, makes it clear what I am striving for in my code. This is not about

**and probably not at all suitable for current optimizations. As for the last mathematical expression given, in principle, any result of calculations automatically falls under setting the Zero flag to zero or one, and of course it’s a pity that the module**

*Pascal,***does not do this with something enclosed in parentheses, but it would be exactly as it seems natural to me (because zero is the starting point, and the parameter is obviously known).**

*Math*That’s all for now, I spent a lot of time with the rework, it’s a shame about line 68 from the last listing here, but nothing can be done. Generator project for ** Lazarus**tested on OS

**https://github.com/Andrei-Y/A-trace-map-generator/blob/main/generator_remake_public.zip**

*Ubuntu*This is simply a stage of correcting the organization of loops due to the removal of unnecessary assembler, which helped me, at a minimum, in debugging mathematical expressions, at least what replaced the block of condition checks. That is, the angle from ** 0** before

**is entered into the first TEdit at the top, and the two below are the axis dimensions**

*90***And**

*X***. That’s all I have for now. Probably this is still about algorithmization, but it’s unlikely that it would point to a specific programming language. But all programming languages in one way or another strive for nature, which means this probably still somehow relates to their study. For me, for example, it is natural not to endow a machine with any super-complex all-replacing operations, since everything complex consists of simple things, which in reality are always studied and improved.**

*Y*I hope that I have technically developed the main idea of the map generator. It remains to finish it a little (add “symmetrical” calculations for angles from ** 90** before

**, and that’s all), the bulk of the time was spent on general debugging and not useless “wandering” with an assembler instead of a flashlight, and quite a bit on describing my wishes and ideas about the prospects of architectures and programming languages. I used the table to visualize the debugging process outside the debugger (the more debugging tools, the better).**

*180*That’s all for now, please forgive me if I took up someone’s time in vain. I wish everyone mutual understanding. Just for fun, I added another survey, with a slightly strange structure; if anything is wrong, I apologize.