Prehistory
It was in the early 90’s, the computer was not, but I had a desire to play in the race) has shown me a friend as you can on a notebook sheet of paper into the cell to play the race. And they say there is a board game with these rules. And that almost everyone played this game at university for classes.
Rules of the game
Can be read Wikipedia rules reading this and knowing nothing, I wouldn’t understand ). The main rule of the game is the rules for moving the car. Rule 2 – you can’t make a move by getting in the way, you lose.
Move 1 : The car stands still and can go to points that are located around the point to which the vector of the car’s movement is directed. At the beginning of the game the vector is zero, it is not directed anywhere, so we can go to points around the car. We make a move to the point above the car.
For each generated trace element you need to store :
Thestarting point (X and Y)
The end point (X and Y)
trace element width (in this implementation, the trace width is the same for all elements, as a saving for the future)
The length of the trace element
Angle of slope of the trace element to the oX axis
User position on the game map
The user’s position on the game map is displayed as a typewriter. For the game map to be of any size, it is necessary that when the user moves, the map moves relative to the typewriter, not the typewriter relative to the map. This means that the car will always be at the same point on the game map, and when you make a move, you need to remember the deviation of the car from its starting point and redraw the elements of the track given this deviation.
/// <summary>/// The current map shift in axes/// </summary>int _deltaX, _deltaY = 0;
The image of the car is made as a car with clearly marked back and front. To make it clear to the user the current direction of movement of the car, it is necessary to turn the car in the direction of the direction of movement.
Each completed move is stored in the _pathList – a list of the segments of the traversed path.
/// <summary>/// A list of the sections of the track covered/// </summary>List<PathElement> _pathList = new List<PathElement> ();
A segment of the traversed path should store the following data :

Point of origin (X and Y)

End point of segment (X and Y)

Displacement of the typewriter relative to the map at the time of the move
Knowing the start and end points of the segment, you can calculate the angle of the segment to the oX axis.
AC – change in the X coordinate from the beginning to the end of the segment
BC – coordinate change Y from the beginning to the end of the segment
/// <summary>/// Current path angle ////summary> public double Angle {get{if (ToY == FromY ToX > FromX) return 90;if (ToY == FromY ToX < FromX) return 90;if (ToX == FromX ToY > FromY) return 180;if (ToX == FromX ToY < FromY) return 0;if (ToY <= FromY ToX > = FromX) return Math.Atan((ToX  FromX) / (ToY  FromY)) * 180 / Math.PI;if (ToY <= FromY ToX <= FromX) return  Math.Atan((ToX  FromX) / (ToY  FromY)) * 180 / Math.PI;if (ToY > = FromY ToX <= FromX) return Math.Atan((ToY  FromY) / (ToX  FromX)) * 180 / Math.PI  90;if (ToY > = FromY ToX > = FromX) return Math.Atan((ToY  FromY) / (ToX  FromX)) * 180 / Math.PI + 90;return 0;}}
Game card field types
To perform a move on the game map are nodes – buttons (Buttons) at a distance of 20 pixels from each other, 39 rows of 39 buttons. In this way the functionality of performing a new move is achieved.
It has been said that when you make a move, the machine stays in place and the map elements change their position. This means that each time you perform a move, you must check all of the map nodes to see where that node currently falls :

field where the machine can make a safe move

a field where the machine can make a move, but it will be out of the track

field belonging to the road

field that does not belong to the road

current field with a machine
When checking, you need to find the fields into which the machine can make a move. To do this, the current speed of the Xaxis and the Yaxis is taken into account.
Formula for finding the area of a triangle knowing the coordinates of its vertices :
On the other hand, the formula for finding the area of a triangle knowing its height :
These two formulas are enough to calculate the height of the triangle (and you could also not bother with the area and calculate more easily using :
But I figured that out at the time of writing)
Formula for finding the length of a segment by its coordinates :
Formula for finding the angles of a triangle knowing the lengths of the sides of a triangle :
Traversed path segments
Each time you make a move, record a new segment of the traversed path.
Again, the segment of the traversed path must store the following data :

Point of origin (X and Y)

End point of segment (X and Y)

Displacement of the typewriter relative to the map at the time of the move

The tilt angle to the oX axis is calculated automatically
To redraw the traversed path, delete the previously drawn path elements, go through the saved path elements and draw them with the "Line" each one remembering to use the offset of that element.
Possibility to start a new game again
To implement this feature let’s make MenuItem type item in main menu of program, let’s make Click event, let’s make clearance of all game map elements, clearance of saved elements of traveled track list, road elements, clearance of variables of map shift and current speed of the car.
Display of current speed
The physics of the prototype game is such that we have two speeds : speed on the oX axis, speed on the oY axis.
By the current speed of the machine we mean the greater value between the two speeds on the axes.
Ability to change program settings through configuration file
To change variables in the game it makes sense to put the values of these variables in the configuration file :

Width of road fragment

Number of road slices to generate a new trace

Maximum angle of turn of the road to the left

Maximum right turn angle of the road

Minimum length of the road section

Maximum length of the road section

Position of the car on the game map
{"RoadWidth": "100", "RoadElementsCount": "20", "MinAngle": "60", "MaxAngle": "60", "MinRoadLength": "100", "MaxRoadLength": "200", "UserPosition": {"X": "400", "Y": "400"}}
Depending on these settings, the playing field looks different :
Landscape generation, GUI improvement
You should add as many different elements to the game as possible for a more pleasant experience (within reason of course):

Fill the playing field "Earth" – an image that fills the entire field

We check the type of each node in the game map every time we make a move. In this code method we can add the generation of "Christmas trees" in case the field of the given node does not belong to the road. To avoid too many Christmas trees, let’s limit the chance of a Christmas tree to 20%.
var random = new Random();var elkaChance = random.Next(1, 101);if (elkaChance < 20){// draw a Christmas tree}

To make the roadway look like a roadbed, you can apply a dashed marking running down the middle of the road elements, just connecting the start and end point of each road element. To make this marking more natural, we use the Polyline shape by passing a collection of points from the road elements to the shape.
var polyLinePointCollection = new PointCollection();foreach (var roadElement in _roadElements){if (polyLinePointCollection.Count == 0) polyLinePointCollection.Add(new Point(roadElement.StartPoint.X + 5 + _deltaX, roadElement.StartPoint.Y + _deltaY));polyLinePointCollection.Add(new Point(roadElement.EndPoint.X + 5 + _deltaX, roadElement.EndPoint.Y + _deltaY));}var polyLine = new Polyline(){Points = polyLinePointCollection, Stroke = Brushes.White, StrokeDashArray = new DoubleCollection() { 6, 4 }, StrokeThickness = 2};
Dangerous Turn Signs
Knowing all the information about the generated road elements, you can compare each road element with the previous element and calculate the difference of the angles of the figures. If the obtained value is > 70°, then we will show the danger sign at the point of the beginning of the road element.
70° " alt="Angle_text{curr} text{} Angle_text{prev} > 70° " src="https://habrastorage.org/getpro/habr/upload_files/f07/537/783/f07537783852171db2ae80e9c872cefc.svg"width="182"height="19"/>
There are cases where one angle is +178° and the other is 178°. The real difference between these angles is only 4°. To solve this problem, we need to add the condition that the angle will be dangerous when the angle difference is less than 290°. The formula will change to this form :