Back to Home

Snake on the PLC. Our answer to Siemens

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 Siemens s7-300 family controller was shown. And I thought: everything ...

Snake on the PLC. Our answer to Siemens

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 shade.

In this article I will show how to implement the algorithm of the game "Snake" in the Russian automated control system "Quint 7" developed in NIIITeplopribor in half an hour . And for greater interest, the game will be fully implemented in the technological programming language FBD , which receives undeservedly little attention.

So, let's begin:

Our task is to write the game "Snake" in half an hour.
To implement the idea we need:
  • Computer with QUINT 7 software installed
  • P-400 controller
  • Patch cord for connecting the controller to the computer
  • License for operational tools (for the game) and design tools (to write the game itself) QUINT 7

A little explanation
Although QUINT 7 has the full virtualization of the entire process control system, and an arbitrarily complex project can be implemented on one computer, but we will do everything in an adult way.


The game algorithm itself can be divided into several large blocks:
  • Generator (for forming a clock frequency)
  • Management (in order to be able to play)
  • The head of the snake (the element that we will control)
  • The tail of a snake (without it, the game loses its meaning, because the goal of the game is to collect the longest 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

First, create the basis of the snake. Because Since the snake is constantly creeping around the field, the basis of the entire program will be a generator that gives out pulses with a period corresponding to the speed of the snake. First, let this speed be equal to 1 cell per second. It is very simple to organize such a generator. All that is needed is a logical AND algorithm, a pulse counter, a comparison algorithm, and a selection algorithm. Total for 1 minute we make the simplest generator.

The principle of action is as follows. At the output of the “ I1 ” algoblock, each cycle changes the logical value 1 <-> 0.
Further on the complex 1 is counting the number of units, which is compared with the value set by us (in this example, 50 because the cycle time is 10 ms, then the counter counts 50 starts in 1 second).
If the counter readings are equal to our predetermined value, we rewind the value wound on the counter (algorithm " Calcul1 ") and issue a clock pulse with a duration of 10 ms (1 controller cycle) from the "=" output of the " Compare1 " algorithm . The last selection algorithm is used to reset the counter by a reset signal.
For attentive readers
An attentive reader must have noticed that when you first turn on the counter, it will count 50 pulses not in 1 second, but in 980 ms. because we are counting on the leading edge, and not on the trailing edge. But at the same time, he will give out all other clock pulses exactly in 1 second. Of course, it is very simple to fix this situation, but I intentionally left such a version of the generator implementation specifically for this issue.

After the generator is done, collapse it into a compact macro and leave it to wait in the wings.


Control

Everything is extremely simple here. We already have a generator. The snake creeps continuously across the field, changing only the direction of its movement. The field itself is a two-dimensional array, each cell of which is specified by the coordinates X and Y. When moving to the right, the X coordinate increases by one at each step, and the Y coordinate does not change. When moving to the left, the X coordinate decreases with a constant Y coordinate. For up and down movement, in the same way, only the Y coordinate will change, and the X coordinate will be constant.
Therefore, we set two reverse counters (one for each coordinate), which, depending on the command, will either add or subtract one from the current coordinate of the snake’s head simultaneously with the arrival of a new clock pulse. As a result, we obtain the following scheme.

A little explanation about circuit redundancy
Опять таки внимательные читатели могут задать вопрос — почему данная схема реализована не оптимальным способом, а с задействованием дополнительных ненужных алгоритмов. Все очень просто. Эти, пока бесполезные, алгоритмы включены в схему заранее и понадобятся нам на этапе «причесывания» программы. Чтобы лишний раз не возвращаться и не править исходную конструкцию, лучше ввести их заранее. Для чего они нужны расскажу в спойлере ниже

Operating principle
Алгоритм "РучСелектор1" служит для ввода команд змейки. Он имеет 4 выхода которые соответствуют четырем направлениям движения (вправо-влево-вверх-вниз). Далее стоит еще один алгоритм селектирования "РучСелектор2". Т.к. команды управления могут поступать в любой момент времени, используем его для синхронизации команд управления с тактовыми импульсами. т.е. будем с первого селектора передавать значение на второй только по приходу тактового импульса. На самом деле очевидно, что данный алгоритм избыточен, т.к. мы проводим синхронизацию с тактовыми импульсами непосредственно на счетчиках (это видно из схемы), однако данный алгоритм был оставлен мной в программе исключительно в целях отладки. Далее стоят 4 алгоритма "И" для синхронизации счетчиков с тактовыми импульсами, о чем я написал выше. По соответствующему сигналу с алгоритмов "И" выбирается увеличение или уменьшение нужной нам координаты на единицу. Т.к. алгоритм селектирования работает по схеме «1 из n», следовательно в каждый момент времени логическая единица будет только на одном из выходов селектора. А это значит, что мы можем просто сложить приращение соответствующей координаты и полученное значение отправить на реверсивный счетчик данной координаты, реализованный как и в случае с генератором на алгоритме суммирования с обратной связью.

This completes the control circuit. Until we collapse it into a macro, because we’ll fix it a little more at the last stage.

Snake head and tail

As mentioned earlier, the head of a snake is an array cell with specific coordinates X and U. But so far we do not have this array. In addition, looking a little into the future (namely, the creation of the tail of a snake), it becomes clear that this cell in the array must be with memory. The simplest logic 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 will prefer the “RS-trigger”. To add beauty to the game (for example, coloring the “snake” and “food” in different colors, highlighting the head of the snake, etc.), the logical memory cell is not enough, because we will have to store not 2, but at least 4 states in it. 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 this will complicate the program a little,
Let's make a 20 by 20 field for our snake. we need 400 triggers. The problem is solved simply. make one cell with one trigger and collapse it into a macro. We 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
  1. Input (a logical sign whether the "head" of the snake is in this memory cell)
  2. Clock (clock pulses for synchronization with the main oscillator)
  3. Length (snake tail length)
  4. Reset (a logical sign of pressing the reset button to start a new game)
  5. Food (a logical sign, is there "food" for the snake in this cell)

About food and the head of a snake
«Еду» для змейки нужно отделять от «головы» т.к. они по разному проходят проверку на попадание головы змейки в данную клетку. Попадание головы змейки на клетку с «едой» это хорошо, в то время как попадание змейки на клетку с хвостом змейки приводит к поражению и концу игры.

Outputs:
  1. Output (logical sign, fill this cell on the field or not)
  2. GameOver (a logical sign of the end of the game if the snake stepped on its tail)
  3. Yum_yam (a logical sign that the snake has stepped on a cell with food and you need to generate new food for the snake)
  4. Another_test (a logical sign that a cell with food was thrown randomly into a field that was already occupied by a snake and that the coordinates of a new random cell had to be regenerated)

After the inputs and outputs of the cell are defined and in brow it is clear what we want from it, we describe the logic of the cell.

The principle of the cell algorithm
На вход поступает сигнал, что змея наступила на эту ячейку. Их него выделяется импульс длиной 10 мсек. и по алгоритму "И" складывается с тактовым импульсом. Если оба условия выполнены, т.е. в одном цикле пришел тактовый импульс и сигнал о том, что змея появилась в этой ячейке, то триггер "RS1" взводится. Это главный триггер этой ячейки памяти. В принципе для обработки головы змеи достаточно сбрасывать триггер следующим тактовым сигналом. Все остальное, это обработка хвоста, и разных ситуаций.

1. обработка ситуации «змея наступила себе на хвост». тут все просто за исключением маленькой тонкости. Мы складываем по И входной сигнал и состояние триггера. Если оба сигнала равны единице, то пришел голова наступила на клетку, занятую хвостом. Это нарушение правил и мы выдаем сигнал «GameOver» по которому игра заканчивается. Тонкость заключается в том, что сигнал с триггера нужно брать не текущий, а в предыдущем цикле. Для этого здесь использована обратная связь (отображается пунктирной линией).

2. обработка «еды». тут тоже все просто. Получаем сигнал о том, что в этой клетке должна находится еда. Складываем его по И с главным триггером. Если оба сигнала единицы, значит клетка еды выпала на поле, уже занятое змеей. В этом случае выдаем на выход сигнал «Еще_попытка», который дает команду генерировать новые случайные координаты.

3. обработка ситуации «змей съела еду». Если условия 2 у нас не выполнились, т.е. клетка с едой попала на свободное поле, то взводится триггер еды "RS2". Далее мы просто ждем сигнала о том, что змея наступила на эту клетку. Как только такой сигнал получен — сбрасываем триггер еды (еда съедена) и выдаем сигнал «Ням-ням» по которому увеличиваем длину змеи на единицу и одновременно посылаем команду генерировать новые координаты для еды.

4. «обработка хвоста». Идея заключается в том, чтобы хранить состояние ячейки до тех пор, пока весь хвост змеи не уползет с нее. Для этого ставим счетчик (алгоритм "Слож1") и считаем сколько тактовых импульсов у нас пришло за то время, как главный триггер был взведен. Сравниваем это значение с длиной змеи и как только они становятся равны — сбрасываем триггер. Для простоты и отладки здесь сравнивается не значение счетчика с длиной. А разница между ними с нулем. Один из моментов, на который стоит обратить внимание, это то, что из после вычитания из длины показаний счетчика мы к получившейся разнице добавляем единицу. Это сделано для того, чтобы компенсировать подсчет первого такта, который происходит одновременно с попаданием змеи на клетку.


"Food"


The biggest problem here is getting a random number. Because we don’t have the command “random”. There are tons of ways to generate a pseudo-random number on the Internet. In this example, one of them is implemented. A certain large changing number is taken - the controller's UpTime in seconds. And it is divided into a constantly changing divider (implemented on an integrator). The remainder of the quotient is taken and it is believed that this is a random number. In fact, of course, it is not accidental at all, and at first glance you can see that the probability of small numbers falling out is greater than large ones precisely due to the “walking” of the divisor in a certain area, but if we take not integers from the remainder, but say hundredths or thousandths from the remainder. Bring them to a range of 1-20, we get a number whose value will already be very similar to a random value.

We connect all the parts and comb the program

So, all parts of the program are ready. We connect them together by bonds. In a first approximation, our game is already working. The finishing touches remained.

1. Check for going out of the field.
It all depends on the implementation of the game. We recall that in the second stage of “Management” we had a counter in which the coordinates of the snake’s head were X and Y. Just compare these coordinates with 0 and 21 (because we have a field of size 20 by 20 and coordinates 1 -20). If any of the conditions is met, then we set the sign “End of the game” (which should be added by OR with our sign “GameOver”). It can be done as in the Pac-man game so that when the snake crawls out of the field, it would appear on the other side. To do this, it is enough to reset the corresponding coordinate.

2. Check for the correctness of the command.
Our snake cannot instantly turn 180 degrees. Therefore, we add a small check, which will not allow giving commands of the opposite direction one after another.

The principle of action is as follows. If a team arrives, then we check to see if it is the opposite of an existing team. If not, then write a new command to memory and submit to the output. If it is, then writing to the memory does not work and the old command remains at the output. If desired, this attribute can be taken out to a separate macro output and an audio signal can be generated from it.

3. The “Start” and “Reset” teams.
Made for convenience. The "Start" team cockes a trigger that sends a signal to our generator. The Reset command resets this trigger, stopping the generator. At the same time, a reset resets all memory cells and data in all counters.
The final program. the drawing is very large


We make the interface and play

A couple of minutes are left from the time allotted to us for the game, which is just enough to fasten the graphical interface to the game.
We draw a 20x20 field from the squares. Each box corresponds to its own memory cell. We set the properties so that at 0 the corresponding memory cell, the box is gray, and at 1 it is green (or whatever colors you like).

By searching for pictures on the Internet, we find a beautiful frame for the field and some kind of snake pattern that is suitable for the meaning.
Next to the field we place the control buttons and attach the control of the mouse and keyboard with the WASD keys.
Nearby we place the "Start" and "Reset" buttons. And a big inscription "GameOver", which will appear when losing.

We compile and load the program into the controller. We launch the operational control station and play.
This is the mnemonic scheme for the operator


To summarize

In this article, we looked a bit at the principles of programming in the FBD language for controllers PTK QUINT 7 . Without much effort, we implemented the game "Snake". Because Since the FBD language is mainly focused on technologists, anyone familiar with elementary logic (for example, the logic of the operation of AND, OR, triggers and counters) can understand the principles of work and start writing their programs. In this case, you do not need to have vast experience in programming or circuitry.

This algorithm can be implemented on any system that supports the FBD programming language simply by replacing the algorithms shown in the figure with similar ones in functionality, because The algorithms used here are basic and are in all systems.

When writing a program in the FBD language, it is important to follow the order of execution of the algorithms. As I wrote above, the above program is not optimal and is equipped with additional algorithms that are needed for only one purpose - to control the execution order and synchronization of the development of all algorithms with a clock signal.

Of course, through simple manipulations, you can improve the program, for example, increasing the speed of the snake with time. Or enter the parameter “hunger”, according to which a snake that does not receive during a certain period of time does not receive new food, is accelerated. In addition, attentive readers could pay attention to the function of remembering the length of the snake when moving it to a new cell (and here a small error was specially laid in to attract attention) and think that this is wrong because usually the snake lengthens from the head, and not from the tail. You can do anything. It is only a matter of desire and time.

Thanks to everyone who read to the end. I hope it was interesting.

Read Next