Hello.
Recently, they sent me a link to
an article where an example of the implementation of a simple and at the same time cult game "Snake" in the controller of the Siemens s7-300 family was shown. And I thought: everyone knows about monsters like Siemens, ABB, etc. But modern domestic developments remain in the shadows.
In this article I will show how in half an hour to implement the algorithm of the game "Snake" on the Russian
industrial control system
"KVINT 7" , developed in
NIITeplopribor . And for more interest, the game will be fully implemented in the language of technological programming
FBD , which is paid unfairly little attention.
')
So, let's begin:
Our task is to write the game "Snake" in half an hour.
To implement the ideas we need:
- Computer with installed QUINT 7 software
- R-400 controller
- Patchkord for communication of the controller with the computer
- License for operational tools (for the game) and design tools (to write the game itself) KVINT 7
A little explanationAlthough KVINT 7 has a complete virtualization of the entire process control system, an arbitrarily complex project can be implemented on one computer, but we will do everything for an adult.
The algorithm of the game itself can be divided into several large blocks:
- Generator (for clock generation)
- Management (so that you can play)
- Snake head (the element that we will manage)
- Snake tail (without it, the game loses its meaning, because the goal of the game is to collect as long as possible the tail)
- Food (those cells that the snake will "eat" and increase the length of the tail)
- Additional checks (allow you to make the game more convenient and more logical)
Generator
To begin with, let's create a snake base. Since a snake in our country continuously crawls across the field, then the basis of the entire program will be a generator that gives out pulses with a period corresponding to the speed of the snake. Let for a start this speed will be equal to 1 cell per second. This generator is very easy to organize. It will only be necessary an algorithm of logical AND, a pulse counter, an algorithm of comparison, and an algorithm of choice. Total for 1 minute doing the simplest generator.

The principle of operation is as follows. At the output of algoblock "
I1 ", each cycle changes the logical value 1 <-> 0.
Next, on the slab “
Sludge1 ”, the number of units is calculated, which is compared with the value specified by us (in this example, 50, since the cycle time is 10 ms, then in 1 second the counter counts 50 inclusions).
In
case of equal readings of the counter and our given value, we wind back the value (algorithm "
Vych1 " wound on the counter) and issue a clock pulse of 10 ms duration (1 controller cycle) from the output "=" of the "
Compar1 " algorithm. The last selection algorithm serves to reset the counter by the reset signal.
For attentive readersThe attentive reader probably noticed that when first turned on, the counter counts 50 pulses not in 1 second, but in 980 ms. because we are counting on the leading edge, and not on the back. But at the same time it will issue all other clock pulses in exactly 1 second. Of course, it is very easy to fix this situation, however, I intentionally left this version of the generator implementation for this particular issue.
After the generator is made, we roll it up into a compact macro and leave it to wait in the wings.

Control
It's all very simple. We already have a generator. Snake continuously crawling across the field, changing only the direction of its movement. The field itself is a two-dimensional array, each cell of which is defined by X and Y coordinates. When moving to the right, the X coordinate increases by one with each step, and the Y coordinate does not change. When moving to the left, the X coordinate decreases with a constant Y coordinate. For moving up and down similarly, only the Y coordinate will change, and the X coordinate will be constant.
Therefore, we put two reversible counters (one for each coordinate), which, depending on the command, simultaneously with the arrival of a new clock pulse, will either add or subtract a unit from the current coordinate of the snake head. As a result, we obtain the following scheme.

A little explanation about the redundancy schemeAgain, attentive readers may ask the question - why this scheme is not implemented in an optimal way, but using additional unnecessary algorithms. Everything is very simple. These, so far useless, algorithms are included in the scheme in advance and we will need it at the stage of “combing” the program. In order not to return and correct the original construction once again, it is better to introduce them in advance. What are they for I will tell in the spoiler below
Operating principleAlgorithm " Ruchselektor1 " serves to enter snake commands. It has 4 exits which correspond to four directions of movement (right-left-up-down). Next is another selection algorithm " Ruchselektor2 ". Since control commands can be received at any time, we use it to synchronize control commands with clock pulses. those. from the first selector we will transmit the value to the second only after the arrival of the clock pulse. In fact, it is obvious that this algorithm is redundant, since we synchronize with clock pulses directly on the counters (this can be seen from the diagram), but this algorithm was left by me in the program solely for debugging purposes. Next, there are 4 algorithms " And " for synchronizing the counters with clock pulses, as I wrote above. The corresponding signal from the algorithms " And " is selected to increase or decrease the coordinates we need by one. Since the selection algorithm works according to the scheme “1 of n”, therefore at each moment of time the logical unit will be only on one of the outputs of the selector. And this means that we can simply add up the increment of the corresponding coordinate and send the resulting value to the reversible counter of this coordinate, implemented as in the case of the generator on the summation algorithm with feedback.
This completes the control scheme. For now, let's not collapse it into a macro. we will correct it a little more at the last stage.
Snake head and tail
As mentioned earlier, the snake head is an array cell with specific X and Y coordinates. But for now, we don’t have this array. In addition, looking a little into the future (namely, the creation of the snake tail), it becomes clear that this cell of the array should be with memory. The simplest logical element with memory is a trigger. There are many varieties of them, each of which is used for its own purposes. In this example, we give preference to the "RS-trigger". To add beauty to the game (for example, to color the snake and food in different colors, highlight the snake's head, etc.) with a logical memory cell is not enough, because we will have to store at least 4 states in it no longer. Those. you will have to make a memory cell either from several triggers, or in another format — for example, store an integer in the cell. But it will somewhat complicate the program, and one of the goals of this whole action is to meet within half an hour.
Let's make for our snake a 20 to 20 field. we will need 400 triggers. The problem is solved simply. make one cell with one trigger and collapse it into a macro. Make twenty cell macros and collapse them into a new, large macro. We put twenty large macros and get 400 memory cells.
The cell macro itself will have 5 inputs and 4 outputs.

Inputs:
- Input (a logical sign of whether the snake's “head” is in this memory cell)
- Tact (clock pulses to synchronize with the main generator)
- Length (length of snake tail)
- Reset (a logical sign of pressing the reset button to start a new game)
- Food (a logical indication if the “food” is for a snake in this cell)
About food and snake head"Food" for the snake must be separated from the "head" because they are tested for hitting the head of a snake in a given cell in different ways. Hitting a snake's head on a cage with “food” is good, while getting a snake on a cage with a snake's tail leads to a defeat and the end of the game.
Outputs:
- Output (a logical sign, paint this cell on the field or not)
- GameOver (a logical sign of the end of the game if the snake has stepped on its tail)
- Yum_nyam (a logical sign that a snake has stepped on a cell with food and you need to generate a new food for a snake)
- Another attempt (a logical sign that a randomly thrown cell with food got into the field, already occupied by a snake, and you need to regenerate the coordinates of a new random cell)
After the inputs and outputs of the cell are defined and in the brow it is clear that we want from it, we describe the logic of the cell operation.

The principle of the cell algorithmA signal arrives at the input that a snake has stepped on this cell. Their pulse is emitted with a length of 10 ms. and the algorithm " And " is added to the clock pulse. If both conditions are met, i.e. in one cycle, a clock pulse came and a signal that a snake appeared in this cell, then the trigger " RS1 " is cocked. This is the main trigger of this memory cell. In principle, to process a snake's head, it is enough to reset the trigger with the next clock signal. All the rest is processing of the tail, and various situations.
1. handling of the situation “the snake came on its tail”. everything is simple with the exception of a small subtlety. We add in both the input signal and the state of the trigger. If both signals are equal to one, then the head came stepped on the cell occupied by the tail. This is a violation of the rules and we issue a “GameOver” signal at which the game ends. The subtlety lies in the fact that the signal from the trigger must be taken not in the current, but in the previous cycle. For this purpose, feedback is used here (displayed as a dotted line).
2. processing "food". here, too, everything is simple. We receive a signal that there must be food in this cell. We add it on And with the main trigger. If both signals are units, then the food cell fell on a field already occupied by a snake. In this case, we give out the “More_express” signal, which gives the command to generate new random coordinates.
3. handling the situation of "the snake ate food." If conditions 2 are not fulfilled, i.e. the cell with the food hit the free field, then the " RS2 " food trigger is cocked. Then we just wait for the signal that the snake has stepped on this cell. As soon as such a signal is received, we reset the food trigger (the food is eaten) and issue a “Yum-yum” signal by which we increase the length of the snake by one and at the same time send the command to generate new coordinates for the food.
4. "tail treatment". The idea is to keep the state of the cell until the entire tail of the snake crawls away. To do this, we put the counter (algorithm " Slug1 ") and count how many clock pulses we had during the time the main trigger was cocked. We compare this value with the length of the snake and as soon as they become equal - reset the trigger. For simplicity and debugging, the counter value is not compared with the length. And the difference between them is with zero. One of the points worth paying attention to is that from after subtracting from the length of the meter readings we add one to the resulting difference. This is done in order to compensate for the calculation of the first clock cycle, which occurs simultaneously with the snake hitting the cage.
"Food"

Here the biggest problem is getting a random number. Since we don’t have the “random” command. On the Internet there are a lot of ways to generate a pseudo-random number. In this example, one of them is implemented. Some big changing number is taken - controller's UpTime in seconds. And is divided into a constantly changing divider (implemented on the integrator). The remainder of the quotient is taken and it is considered that this is a random number. In fact, of course, it is not random at all, and at first glance it can be noted that the probability of small numbers falling out is greater than large ones due to the divider’s “walking” in a certain area, but if we take not the whole of the remainder, but say hundredths or thousandths from the remainder. Bring them to the range of 1-20, we get a number, the value of which will already be very similar to a random variable.
We connect all the parts and comb the program
So, all parts of the program are ready. We connect them with each other connections. In the first approximation, our game is already running. There are final touches left.
1. Check to go beyond the field.
It all depends on the implementation of the game. We recall that at the second “Management” stage we had a counter in which the coordinates of the snake head were in X and Y. Just compare these coordinates with 0 and with 21 (since we have a field of 20 x 20 and coordinates 1 -20). If any of the conditions is fulfilled, then set the “End of Game” sign (which should be added along OR with our “GameOver” sign). You can do it like in the game
“Pac-man” so that when a snake crawls out of the field, it would appear on the other side. To do this, simply reset the corresponding coordinate.
2. Check for correct command.
Our snake can not instantly turn 180 degrees. Therefore, we will add a small check, which will not allow to give commands of the opposite direction one after another.

The principle of operation is as follows. If a team comes, then we check if it is the opposite of the existing team. If not, then write a new command in memory and feed it to the output. If it is, then the write to memory does not pass and the old command remains at the output. If you wish, you can take this sign to a separate output of the macro and form an audio signal using it.
3. Commands "Start" and "Reset".
Made for convenience. The "Start" command cocks the trigger, which sends a signal to our generator. The "Reset" command resets this trigger, stopping the generator. At the same time reset resets all memory cells and data in all counters.
The final program. drawing is very big Making the interface and playing
From the time allotted to us for the game there are a couple of minutes, which are just enough to fasten the graphical interface to the game.
Draw a field of 20x20 squares. Each square corresponds to its own memory cell. Set the properties so that when the value of the corresponding memory cell is 0, the square is gray, and when 1 it is green (or any colors you like).

Searching for pictures on the Internet, we find a beautiful frame for the field and some kind of snake pattern that matches the meaning.
Next to the field we place the control buttons and bind the mouse and keyboard controls with the WASD keys.
Nearby place the buttons "Start" and "Reset". And the big inscription "GameOver", which will appear when you lose.
Compile and load the program into the controller. We launch the operational control station and play.
This is the mnemonic scheme the operator will have Summing up
In this article, we
briefly reviewed the principles of programming in the FBD language for
PTC controllers
KVINT 7 . Without making much effort, we implemented the game "Snake". Since FBD language is mostly oriented to technologists, then anyone who is familiar with elementary logic (for example, the logic of the AND, OR, algorithms, and counter algorithms) can begin to understand the principles of operation and begin to write their programs. You do not need to have extensive experience in programming or circuitry.
This algorithm can be implemented on any system that supports the FBD programming language simply by replacing the algorithms presented in the figure with similar in functionality, since The algorithms used here are basic and exist in all systems.
When writing a program in FBD, it is important to follow the order of execution of the algorithms. As I already wrote above, the given program is not optimal and is equipped with additional algorithms that are needed with only one purpose - to control the execution order and synchronization of all algorithms with a clock signal.
Of course, by simple manipulations, you can improve the program, for example, by increasing the speed of the snake movement over time. Or enter the parameter “hunger”, according to which a snake that does not receive during a certain period of time does not receive a new food, is accelerated. In addition, attentive readers could pay attention to the function of memorizing the length of a snake when moving it into a new cell (and here a small error was specially laid down to attract attention) and think that this is wrong. usually the snake is lengthened from the head, not from the tail. You can do anything. This is just a matter of desire and time.
Thanks to all who read to the end. I hope it was interesting.