📜 ⬆️ ⬇️

We write puzzles on FBD. Fifteen and Simpson

Hello.

This article will show you how to write a simple program in the FBD programming language that will nevertheless do something useful. In our example, this will be a Fifteen game.

To begin, let me remind the rules of the game : the game "15", "Fifteen", "Taken" - a popular puzzle, invented in 1878 by Noah Chapman. It is a set of identical square knuckles with printed numbers enclosed in a square box. The length of the side of the box is four times the length of the side of the knuckles for a set of 15 elements; accordingly, one square box is left empty in the box. The goal of the game is to move the knuckles around the box, to arrange them by number, preferably making as few moves as possible.
')
As we can see, the rules are extremely simple. And the implementation will be simple and take about 15 minutes without a graphic part and half an hour with all the pictures. At the same time I want to draw your attention to the fact that the optimization of algorithms and logic remain outside the scope of this article, since These questions are not so simple and will require much more time.

Here's what happened in the end:


Description of the program, comments and pictures under the cut.

The basis of the program


As can be seen from the description of the game, we have a field of 16 elements. Thus, the basis of the program will be the element "Fifth cage".
We formulate the requirements for this element:

- Our “cell of the pyatinik” must accept external commands when the player says that he wants to move it to the next empty space.
- The cell should be aware of its neighbors vertically and horizontally, in order to understand whether there is a nearby empty cell with which you can switch places.
- The cell should set the initial value at the start of the game.
- To exit the cell must pass the value set in it at the moment.
- If the player gave the command to the Cage to move to the next empty field, then the Cage should transfer the command to the neighboring empty Cage that they will now change places.

Based on these requirements, we obtain a set of input and output signals.

Inputs:

Outputs:

“Swap places” in quotes, because Fifteen cages themselves will not run anywhere in our country, they will simply overwrite their meaning.

The result is this macro:


We put these macros in our task 16 pieces and link them together. As a result, we obtain:

Everything is very simple. Sixteen macros of “Fifteen cells” were assigned to the task and tied each cell with its neighbors in the vertical and horizontal. If there are no neighbors in the cell (for example, in cell_1_1 there is no neighbor above and to the left), we put "-1" on the corresponding input.

Stuffing basics


At the last step, we laid the foundation for the program. But since the element “Cell tag” while we have an empty, then nothing good our program can not do. It is time to fix this by filling in the macro “Cell Fags” with logic.



This is the finished implementation of our macro. Briefly principle of work:

1. We receive the control command as input and unpack it. We single out a separate signal with a duration of one cycle - a sign that a command has arrived, and a command parameter. In our case, the command parameter is a set of integers from 1 to 6, where the numbers 1 - 4 are the command to swap values ​​with one of the neighbors, the number 5 means the command to set the initial value in the cell, and the number 6 - that the player clicked on this cell and wants to “move” it to a possibly empty field.

2. We check all neighbors for a zero value (the number 0 denotes an empty field).

3. Next, we look at the current value of this cell and the command. If the value of the cell is zero (the cell is empty) and at the same time the command came to exchange values ​​with the neighbor (commands 1-4), then we rewrite the value of the neighboring cell in the cell.

4. If we received the command 5 (set the initial value), then we simply write the value from the “Device Value” input into memory.

5. If team 6 came to us (the player clicked on this cell with a mouse) and the value of one of the neighboring cells is zero, then we write a zero value into this cell and send the “Go” command to the neighboring cells, indicating with which neighbor we want to change .

That's all. The only subtle point in our implementation is the “Delay” algorithms. But to guess why they are needed, it is easy to simply imagine how the program is being executed:

- we received a command from the player to “move” this cell to an empty space.
- checked, really there is an empty place nearby.
- recorded in the cell "0" and transferred to the neighbor that we want to exchange values ​​with him.
- a neighbor (you need to understand that he is working on the same macro “Cell tag”, just another copy of it) sees that the team has come to change places with another cell.
- the neighbor checks that it itself is written "0" i.e. he is now an empty cell and writes down the value of the first cell, from which the command came to exchange places.

But wait a second! Indeed, at the same time as sending a command to a neighbor, we wrote down “0” in our first cell, therefore the neighbor also writes down a zero and we get two empty cells, and one value (from 1-15) disappears. And so on until the entire playing field becomes empty. To avoid this, we delay in the first cell overwriting zero. Thus, the empty neighboring cell will have time to write the correct value.

A few words about optimization
As it is easy to see, the macro “Cell of the tag” itself is quite simple. However, it can still be simplified by throwing out part of the blocks. The idea is simple. Now we check the team and neighboring cells before performing the permutations. However, as mentioned above, with any permutation, two instances of the same macro are processed. And in the first instance we check before sending the command, and in the second copy we carry out the same check when receiving the command. Thus, one of the checks can be safely discarded. This is the first thing that immediately catches the eye. The second point is an extra delay on the value. After all, the value is stored in the "Memory" block, where it is overwritten only by a command. Therefore, it is enough to delay only the command of rewriting the value, and the value itself to submit as is.

Victory or defeat


Actually, the basis of our program is ready and is already working perfectly. You can play. All further actions are needed only for the implementation of support functions.

Let's start with automatic verification of the player’s victory or loss.

In the game "tag" there is a funny moment, which consists in the fact that half of all the possible initial layouts of tiles cannot be assembled if the tiles with the numbers "14" and "15" stand in the wrong order.

Based on this, we form two conditions:
- condition 1: the value recorded in each cell corresponds to its ordinal number. Those. the player won.
- condition 2: the values ​​recorded in the cells correspond to their ordinal number with the exception of cells "14" and "15", which are interchanged. Those. a little bit unlucky with the layout.

We put 18 comparison algorithms, and collect two sets of sixteen equalities by AND. By OR form a sign that we have come to one of the ends of the hand.

18 algorithms because we check the compliance of 16 cells for the first case and additionally the permutation of the cells "14" and "15" for the second case. By the way, since we have an array of data predetermined, then fifteen checks are enough for the entire field, since The last cell is checked automatically.



Initial set up


So, the program is ready with us. You can play and when you win, a message is displayed. Everything is good except that at the beginning of the game you need to place the tiles in the field at random. And it turned out that in this example it is the most difficult task. But if you do not run into performance, the optimality of algorithms, etc. then you can solve this problem "in the forehead" with little blood relatively quickly.

First, we make a generator of random integers 0 - 15. Let's go in the old proven way. This is a valid option. the resulting sequence of random numbers depends on the point in time when the "New Game" button was pressed. And since this moment in time happens and never repeats, then in the end we get a fairly simple and good random number generator.



At the output of the Real-to-Integer (TWC) algorithm, we obtain a random integer value of 0 - 15.

Now the task is to form a series of non-repeating values, and write each value to its own cell.

As I said, it is difficult to make a more or less good solution specifically for the FBD language. Or maybe I just do not see the obvious for the rest a beautiful and simple solution. Of course, there is always an option to make an insert on ST, but the goal is to make the task entirely in FBD.

The solution of the problem "in the forehead" is as follows:

- put the control element.
- when giving the command, we hammer in the initial array "-1" (it was possible with any numbers outside the range [0..15]).
- reset the counter.
- then we generate a random number from 0 to 15 and check if this is in our array. If there is, we continue to generate random numbers.
- if there is no such number, increment the counter by one and write our value into the corresponding memory cell.
- we repeat these actions 15 more times.
- we check if we have reached the sixteenth step, then we form a pulse, by which we send a command to write these values ​​to all the cells.

The main disadvantage of this algorithm is a repeated search of previously thrown out values. In theory, the generation time of such a random sequence can be infinite. In reality, with dozens of program runs, it was not more than half a second. In any case, at the time of generation (since it is not instantaneous) we put a lock on the player’s actions.

Here's what happened:



We attach graphics


Now that our task is completely ready, it's time to add a UI.

To do this, choose on the Internet any favorite pictures with tags. At the same time, we select pictures for a successful collection of the layout and unsuccessful ones. I decided to put Homer Simpson there.

We put 16 pictures and attach animations to them so that they show the number corresponding to the number in the cell. And in a cell with zero tile must be invisible. We put on each tile the formation of the team "6". Below we have the “New Game” button by which we will run the algorithm for generating a random initial placement. At the same time, do not forget to put a lock on all tiles during generation. On top of all this business we put Homer. Everything. The graphics are ready, you can play.

Painted version. You may notice that initially all the tiles we have with the unit.


Starting position


A bad deal.


And win!


What is left unfulfilled


In fact, not so much:

- You can attach a move counter. This is done elementary. In the “Cell tag” macro there is a team “6” - a player's team. It is enough to take from it a logical signal with a duration of one cycle. Bring this signal out. Collect all sixteen signals by OR, and start on the addition algorithm covered by the feedback. Those. Only three additional algorithms and a couple of minutes of work.

- statistics. You can make a counter of won and unsuccessful games. Two addition algorithms and a pair of binding algorithms are added. Also a couple of minutes of work.

- selection of layouts. As already mentioned, half of the layouts did not initially converge due to the inability to swap tiles "14" and "15". You can check the “bad deal” at the start and immediately change these two tiles in some places.

It is not difficult to do all this, so I will leave this opportunity to everyone.

findings


This time, in half an hour, a simple little toy “Fifteen” was implemented in the FBD language. In this case, all the algorithms used are simple and clear. All the logic of signal processing is obvious. Understanding this program will not be difficult for anyone who is familiar with the basics of logic and the basics of IEC 61131 programming languages.

To check, I’ve gotten about the same thing in C. Here’s what happened:
C program text
Since I do not speak C / C ++ programming, most likely the program is terrible. Moreover, I immediately see several "doubtful" places. However, it works and works without errors.

The main idea is that the same program written in C and FBD has a different complexity of human understanding "from the side". And if it is not difficult to deal with the language of functional blocks, then you will have to tinker with the implementation in C.

Although the FBD program consists of 153 blocks (and we remember that there are still 16 macros, each of which consists of 32 blocks), but it took much less time to write it than to write 50 lines of C code.

#include <iostream> #include "cstdlib" #include <locale.h> #include <stdio.h> #include <conio.h> #include <math.h> #include <memory.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <cmath> using namespace std; int main(void) { int MyPyatn[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; int i = 0, x = 0, y = 0, MyRand = 0, MyBuffer = 0, MyButton = 0; bool MyGameover = false, MyChange = false; setlocale(LC_ALL, "ru-RU"); srand(time(NULL)); cout << " 'n'         : "; kbhit(); if(getch() != 110) return 0; std::system("cls"); cout << "  : "; for(i=0;i<16;i++) { MyRand = rand() %(16-i); MyBuffer = MyPyatn[15-i]; MyPyatn[15-i] = MyPyatn[MyRand]; MyPyatn[MyRand] = MyBuffer; } for (i=0;i<16;i++) { if ((i %4) == 0) { cout << "\n"; cout << "\n"; } if ((MyPyatn[i]<10) || (MyPyatn[i] == 16)) cout << " "; else cout << " "; if (MyPyatn[i] == 16) { cout << "_"; x = i %4 + 1; y = int (i/4) + 1; } else cout << MyPyatn[i]; cout << " "; } cout << "\n"; cout << "\n"; cout << "  ---.  - '0' \n"; cout << " _   "; do { MyChange = false; MyGameover = true; for(i=0;i<16;i++) { if (MyPyatn[i] != (i+1)) MyGameover = false; } while(!kbhit ()); MyButton = getch(); if ((MyButton == 48) || (MyButton == 72) || (MyButton == 75) || (MyButton == 77) || (MyButton == 80)) { if (MyButton == 48) return 0; if ((MyButton == 72) && (y > 1)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[(y-2)*4 + x - 1]; MyPyatn[(y-2)*4 + x - 1] = MyBuffer; y = y - 1; MyChange = true; } if ((MyButton == 80) && (y < 4)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[y*4 + x - 1]; MyPyatn[y*4 + x - 1] = MyBuffer; y = y + 1; MyChange = true; } if ((MyButton == 75) && (x > 1)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[(y-1)*4 + x - 2]; MyPyatn[(y-1)*4 + x - 2] = MyBuffer; x = x - 1; MyChange = true; } if ((MyButton == 77) && (x < 4)) { MyBuffer = MyPyatn[(y-1)*4 + x - 1]; MyPyatn[(y-1)*4 + x - 1] = MyPyatn[(y-1)*4 + x]; MyPyatn[(y-1)*4 + x] = MyBuffer; x = x + 1; MyChange = true; } } if (MyChange) { std::system("cls"); cout << "  : "; for (i=0;i<16;i++) { if ((i %4) == 0) { cout << "\n"; cout << "\n"; } if ((MyPyatn[i]<10) || (MyPyatn[i] == 16)) cout << " "; else cout << " "; if (MyPyatn[i] == 16) cout << "_"; else cout << MyPyatn[i]; cout << " "; } cout << "\n"; cout << "\n"; cout << "  ---.  - '0' \n"; cout << " _   "; } } while(!MyGameover); std::system("cls"); cout << "!!!"; getch(); return 0; } 


In other words, the FBD language is a simple and understandable language intended for people far from programming. Programming simple puzzles on it is easy. And to understand the principles of the program written by another person also usually does not constitute a big problem.

That's all. I hope it was interesting.

Source: https://habr.com/ru/post/304416/


All Articles