📜 ⬆️ ⬇️

A small toy "Minesweeper" not in 30 lines

Hello.


Last time I showed how you can easily and quickly write a game "snake" in the FBD programming language, download the program to the controller, and finally make the equipment that controls and automatically regulates the production of electricity and heat for all of us, to do more Something "useful."

However, from people who are familiar with the work of the operating personnel at power plants, I received an important remark that the "snake" is absolutely not suitable for the industrial control system for objective reasons. Firstly, despite the fact that the automated process control system is controlled at the station with all the automation and it is also involved in regulation and protection, the realities of life are such that the operator is also required to monitor the process for operational intervention as needed. Therefore, the toy should be such that (unlike previously presented) does not fully occupy the operator’s attention, allow him to switch between applications and do something without affecting the game. And secondly, the toy “snake” itself is very dynamic and requires quick (and at high levels in general, instant and jewelry) pressing buttons, which can easily lead to a small error: for example, instead of controlling a toy, you can accidentally control some important technological equipment, which in this mode of operation, it was impossible to touch. Of course, nothing terrible will happen. in any case, the protection will work, but shutting down the turbine or shutting down the boiler with protection is things that lead to substantial financial losses and unnecessary work to start them back into operation.

All this suggests an obvious idea that you need to implement calm logic toys. As an option - all kinds of solitaire or the well-known Minesweeper. Due to the fact that solitaires of all kinds require images of cards that were not immediately found on the Internet (of course, it was easy to find card drawings, but I had special requirements for size and quality as well as the design of cards) it was decided to implement the Minesweeper toy.
')
In addition to this, there is another important reason for the implementation of this particular game. Namely, the desire to once again demonstrate how easy and fun to program in the FBD language.

A few introductory words


But first, a few introductory words (for those who are not too tired to read).

Probably everyone who reads this post to the end, will say (or think): “Bullshit! Yes, I will write the same in C (C ++, Delphi, JS, etc.) in 30 lines of code. ” And I agree with that. But there is one thing. Before you write something in a high-level language in 30 lines of code you need only
I could not resist
learn this high level language.
And anyone can start writing programs on FBD. Moreover, the basic programming skills in FBD are taught to children of preschool age.
I think many will remember how in childhood ...
... under the guise of educational games from cubes with letters collected the words:
For example from letters ...

Gathered a word ...


In other words, programming in the FBD language is simple and intuitive.
Small note
Here I am using the old habit of saying FBD, although according to one of the main developers of all this:
Deranged , December 20, 2013 at 12:25
This is not FBD. There is support for data types, including structures. Also, data is divided into potential (instantaneous value) and commands (buffered values). There is also built-in support for quality values. You can conduct feedbacks, they are highlighted by zebra. In this case, the default values ​​of the data type are taken as initial values ​​(prescribed in the type itself).
In general, there is a carriage of everything, I also planned to put in there control of the order of execution in the form of connections and support of conditions. Then there would be a complete Frankenstein from the FBD, SFC and conventional block diagrams.


Of course, there are also serious algorithms for this language, the struggle for controller resources, optimization of technical program execution, optimization of memory and speed in general. Smart people are puzzled over how to create ideal algorithms from the simplest blocks. But since this article is purely for informational purposes, then we omit all these subtleties and finally go directly to the programming itself.

But before that, a small question on intelligence. Who would guess - “Respect and Respect,” for the impatient - the answer in the spoiler below.
So - the game "Snake" and the game "Minesweeper" is one and the same game in terms of programming on FBD. Why? And what do they have in common, what makes such a statement?
Answer
Everything is very simple. And in the game "Snake" and in the game "Minesweeper" there is a field consisting of cells. Just the cells in this field take on different values ​​and are displayed differently. Ie ... in other words, we can take the program of the game "Snake", its graphical interface, and in half an hour-hour of work we can redo it all into the game "Minesweeper", because in fact we will have to change only one algorithm and redraw one cell of the field ..

We start to program


So, we start to program our task.
My personal opinion is that 80% of the programming in the FBD language is to clearly imagine what we want to get in the end. And so only we achieve such an understanding, then 80% of the problem is solved and there is literally a bit of work on sketching the code and combing it. Now I will try to demonstrate this principle in practice.

Suppose that the field for the “sapper” we will have by analogy with the “snake” 20x20 cells. Therefore, we need 400 memory cells, in each of which each cell of the field will be processed. To structure the program - we divide all the cells into rows. Thus, we need twenty string algorithms, each of which will consist of 20 cell algorithms. Those. We have come to understand the basis of the program.

Algorithm "String"


Let us consider the string algorithm in more detail. Or rather, what kind of data we need to have at the input and what - at the output of this algorithm. I propose to start from the end - i.e. from the output.

Algorithm outputs

- First, we need an output data line that determines the state of each cell to draw it in the human-machine interface (or, more simply, in the operator station).
- Secondly, we need a vector that shows which cells contain bombs and which cells are safe.
- Thirdly, we need a sign that the player “stepped” on the field with a bomb and the game is lost, let's call this exit “Big_Bada_Boom”.
- Fourthly, for convenience, the “sapper” has a feature that, when it hits a cell that does not have a neighborhood with a bomb, all empty cells bordering it automatically open. Those. we will need an output that sends the reverse “open cell” command by the program itself.
- Fifthly, for the algorithm for setting bombs, we will need a way out showing how many bombs are in a given line. Of course, this value can be obtained directly from the vector described in clause 2, simply add up all the non-zero bits. But for convenience, we will bring this function inside the macro.
- Sixth, to keep statistics, we need to know how many more closed cells are left in a row.
That's all we need for the full functionality of the game "sapper".

Only 6 outputs with data.

Now let's think about what we need to have at the entrance in order to be able to form the outputs we need.

Algorithm inputs

- It is logical to assume that the main entrance of the algorithm is the team from the player "Open cell". After all, this is the main point of the game.
- For clarity of programming, we will use another entry - “Put a flag on the cell”, which marks that there is a bomb and does not accidentally click on this cell and explode.
- To form the status of each cell (and we all remember that if there is no bomb in the cell, then it shows how many bombs are in the cells bordering it) you need to start a vector from the line above and the line below. Let's make the algorithm universal and add three inputs: an input for a vector with bombs in the line above the current one, an input for a vector with bombs in the current line and an input for a vector with bombs in the line below the current one.
- To start the new game and rewrite the values ​​of the cells, add the logical input “New game”.
- As mentioned earlier, if the cell is empty and there are no cells with bombs, then the cells next to it should automatically open. Add for this input "Open program."
- Well, of course, what kind of game is it without a mined field. So we need the entry "Set bombs".

Total got 8 entries.

That's all that we need from the main algorithm of the program.
We quickly type a macro with our inputs and outputs, and we get the following algorithm:
or if you reveal it:
Now let's fill our algorithm with meaning. As I already said, the basis for filling our main algorithm is the “Memory cell” macro, of which there will be 20 pieces.

Algorithm "memory cell"


Let's try to think over what we may need from each cell of the “sapper” field of play, and what we need to do for this to input the macro. Let's start again from the end, i.e. from the exits. And when we formulate all the data that we need, it becomes clear that you need to have at the entrance to get them.

Algorithm outputs

- To begin with, the cell must have an output showing the status of this cell. Have you ever wondered how many states each field cell in a sapper game can take?
States of the field cells
Answer 12. That's exactly the way - 12 states corresponding to 12 different cell mappings.
I coded them like this:
-1 - the cell is closed.
0-8 - the cell is open and shows the number of bombs in the neighboring cells.
9 - the cell is open and there is a bomb in it.
10 - the cell is closed and the flag is set on it.

- Separately, for calculations and other processing, we will render the logical outputs "Cell closed" and "Bomb installed in the cell".
- As already mentioned - if an empty cell is open, then it should automatically open the cells adjacent to it. accordingly, we still need the logical sign “Open neighbors”.
- Well, in the end, if a player made a mistake and “stepped” on the field with a bomb, then you need to form a logical sign that the game is lost. let's call it Bada_Boom.
Total only 5 outputs.

Algorithm inputs

Let's try to decide what we need in order to form the required outputs.
- First of all, the algorithm must receive a command from the player "Open field".
- For simplicity, with a separate team, we also start the “Put a Flag” command here.
- As I said earlier - every cell in the field must know how many bombs are laid in the neighboring cells. we will get for this a separate entrance "Neighbors".
- In order to randomly set the bombs first you need the logical sign “Here is the bomb” by which the cell will be mined.
- Well, of course, to start a new game there must be a reset sign, which resets all information in the cells.

We fill this macro with the following algorithm:

Now let's program the “Memory cell” algorithm itself. It is extremely simple:

A brief explanation of the principle of the algorithm
- A vector, formed from the state of neighbors, that is unpacked with this algorithm and then the number of units (bombs) in the vector is calculated on the algorithm “BitDeshifr1”. The question immediately arises: why is there 9 bits, when the cell has only 8 neighbors maximum. I answer: for universality. The fact is that as will be seen later, I wrote another macro to find the neighbors of each cell. And in order for the macro to be universal for all cases (a cell can have not only 8 neighbors when it is in the center of the field, but only 3 when it stands in the corner, or 5 when it stands on the edge of the field) use nine bits.
- The trigger "RStrig1" serves to set and reset the "flag" on the cell. The first command retracts the trigger, and the second command resets. As can be seen from the diagram, the cocked trigger blocks the passage of the “Open” command on the I2 algoblock since the value from the trigger output is input to the “AND” algorithm input with inversion. Those. while the trigger is cocked, the second input of the “AND” algorithm receives “False” and the output of the “AND” algorithm is also equal to “False” regardless of the value at the first input. It turns out that the “Open” command cannot reset the “RS2” trigger and we are insured against the accidental opening of a cell that we don’t want to open.
- The trigger "RS2" is used to form the status of the cell: it is closed or open. As can be seen from the diagram, the trigger is cocked by the “Reset” command (the beginning of a new game) and is reset only when the “Open” command arrives.
- The trigger "RS1" is used to indicate the presence of a bomb in this cell. It is cocked upon receipt from the program of the “Here's a Bomb” team, which sets bombs in a cell and is reset with the start of a new game.
- Algorithm "I3" is one of the most important in this scheme. On it, the value of the “RS1” trigger (the bomb is installed here) and the arrival of the “open” command in this cell are added together. If both conditions are fulfilled, then “True” is formed at the output, meaning that the player stepped on the field with a bomb. This value is fed to the “Bada_Boom” output, which are then collected from all the cells at the “Big_Bada_Boom” output and mean loss.
- Three “Choice” algorithms, cascaded, form the cell status. The “Vybor1” algorithm is used to form what to output to the output when a cell opens: the numbers “0-8” corresponding to the number of bombs in the cells in the neighborhood, or the number “9” corresponding to the mined cell. If the field is still closed (Trigger “RS2” is set), then the “Select2” algorithm is set to “-1”, which, as we agreed earlier, corresponds to the status of the closed cell. If the field is closed (Trigger “RS2” is cocked) and at the same time (algorithm “I4”) the trigger “RS Trig1” is raised, then on the Algorithm 3 the value is forcibly replaced with “10” corresponding to the set flag.
- The last comparison algorithm is used to automatically open neighboring cells. If at the exit "Vybor3" we got zero, then the player opened the cage, next to which there are no bombs. Then the team is sent to open neighboring cells.

This primitive algorithm is the basis of the whole game. there are only a few tidying up the program and finishing the functionality.

We now proceed to the macro "String", consisting of 20 "memory cells."

The macro itself and the description. The drawing is big.

The macro itself is extremely simple. 20 memory cell algorithms associated with their inputs and outputs of the common macro. For cell status, this link goes directly, for the rest, either through BitShifter algorithms for packing logical values ​​into a vector, or through adders to count the number of necessary elements in a row, or through the OR algorithm to form an output command for opening neighboring cells or ending games ("Big_Bada_Boom").
Separate attention is deserved only by the blocks “Open”, “Vector Transformation” and “Open Neighbors”.
Consider each such unit separately.
Let's start with the block: Open

As it is easy to see, here they simply add up the "OR" commands from the player and from the program when it opens the neighbors of an empty cell.

Consider the block: Vector conversion

The idea is very simple. Three vectors come to us and we need to calculate how many mines surround each cell in a particular position. For this, I intended to simply shift the vector to the right by (position - 2) and count the number of units in the first three bits of each vector after the shift. But then I came across a funny thing. What to do if the position of the cell first? then, by analogy with the others, I must shift the vector to a negative value (that is, not to the right, but to the left). Of course, all the left-shifted bits will be zero, but the general principle of counting will be preserved. However, the subtleties of the implementation of the algorithm did not allow this, so we had to put a check on the position of the cell and for the first position of the cell to carry out a separate processing. As a matter of fact, this processing consists only in processing the second bit in the current line and the third bits in all lines. Remember, I wrote that for the standard macro I needed to form 9 bits of neighbors, and not 8, as the logic requires. This is due to this situation.

The most observant readers have already noticed that there is clearly an unnecessary operation performed here. I get bits describing the neighbors, pack them into a vector, and then again unpack and summarize. Those. unpacking operations are clearly redundant. But I left this option for debugging and clarity.

Consider the block: Open Neighbors

It's all very simple here. If a cell is empty, then it must open not one, but as many as 8 neighboring cells. To unify and simplify life, let it open up not 8 neighboring cells, but 9 (yes and even itself). In the FBD language, sometimes even the simplest assumption, which does not affect the program’s work or resources, greatly simplifies life). Thus, the macro operation algorithm is simple. We check the position of the cell. If the cell is in the second and more positions, we create a vector from the first consecutive bits and shift them (remember, by analogy with vector processing, just not to the right, but to the left, because we don’t bring any part of the vector to the beginning, but on the contrary, the initial vector is shifted to the area we need) by (position -2). If we have the first cell, then we form the initial vector of two bits and shift it to (position -1) = 0 ie We are not moving anywhere.
Next, we just have to add up all the prepared vectors for “OR” and give them to the main exit.


Separately I wanted to mention another macro. Before the start of the game, we need to randomly place mines in the field. But what to do if there is no random number generator and the Random command is not available. Of course, there are many pseudo-random sequence generation algorithms on the Internet. But they are quite complex and their implementation in itself deserves a separate article. Therefore, we had to go the usual way and make the macro “Random generator”.

A brief description of the principle of the macro
So, we don’t have a random number generator. Well. let's do it yourself!
The idea is primitive and has already been applied by me in the previous program, just here it received some development.
On the integrator, a value that varies very quickly with a saw with a adjustable range is formed (the “Upper threshold” and “Lower threshold” inputs correspond to the range). Further, using the “Seconds” algorithm, we calculate the controller operation time since the start, which also increases continuously. We divide the controller operation time by our rapidly changing number, while taking the remainder of the division. Next, we extract from the remainder the digits we need (for greater randomness, I took 4-5 decimal places for the X coordinate, and 6-7 decimal places for the Y coordinate). Discharges are very simple. Multiply the remainder of division by 1000000 and divide with the remainder by 100. As a result, the remainder yields the value [0..100). At the same time, it is necessary to understand that this value can come as close as you like to 100, but will never be equal to it. The random value required by us lies in the range [1..20], therefore we divide our remainder by 5 (algorithm “DelOst3”), take the integer part from the division and convert it into an integer using the type converter. As already mentioned, the dividend will never be equal to 100, which means that our quotient lies in the range of natural numbers [0..19]. We solve the problem simply by adding one to the result. So our random number is ready. Due to the fact that it is unknown at what point the player clicks on the “New game” button and what value will be on the integrator at this moment, as well as the fact that we take values ​​in 4-7 decimal places, it is safe to say that we got a good random number generator.

We combine all the parts together and add beauty


So, all parts of our program are ready. We connect them together and do the strapping:
Picture of the main program. Big.

A little explanation
As can be seen from the figure, the most important current algorithm is “Ruchselektor1”, which outputs a logical unit for one cycle and launches a new game. The launch of the new game is that the signal is sent to all the “String” macros to the “New game” input and the trigger is set to “SR2” in the “Memory cell” macro and the other triggers responsible for the presence of bombs and flags are set. .
At the same time, the same output of the “RuchSelektor1” algorithm resets the “RS1” trigger (“Game_over” - this trigger is cocked if the player opens the cell in which the bomb was hidden, and the RS3 (Victory) trigger, which is cocked when the player wins, i.e. detecting all the bombs hidden in the field.At the same time, the same signal is sent to the input of the Memory1 algorithm for recording the number of installed bombs in the current game into the memory (the player can change the number of bombs in the field at any time, but these settings will only get with the beginning of a new game s).
Then, with a cycle delay (“Delay1” algorithm) (which is needed to reset all of the triggers in the memory cells), the “RS2” (“Put the Bomb”) trigger is activated, which includes the procedure for installing bombs. Those. transferring our random coordinates generated at the “Generator_Random” macros to the “Column” and “String” algorithms, sending, by the given coordinates, a command to reset the “RS1” trigger in the “Memory Cell”.
Controlling the number of bombs installed is simple. The total number of bombs installed is calculated, and as soon as it is compared with the number specified on the Memory1 algorithm, the SR2 trigger is reset, thereby stopping the installation of new bombs.

Algorithms 36-40 serve to verify the correctness of a given number of bombs by a player. Here, for debugging, I limited the minimum and maximum number of bombs to 0 and 400 pieces, respectively. When specifying a number outside this range, it is automatically equated to the nearest boundary.

Check for the fulfillment of the conditions of victory is extremely simple. All closed cells are counted. As soon as their number becomes equal to the specified number of bombs and the trigger “RS1” (“Game_over”) is not cocked, the trigger “RS3” (“Victory”) is activated.

It remains for us to consider only 2 algorithms:
The algorithm "OR2". It logically OR the outputs of all three triggers are collected. And according to the output of the “OR2” algorithm (when it is equal to one), a lock is placed on the player’s actions. Those. (.. , , ) . , .
«1» , . . 400. 400, , .

.

We fasten the graphic interface


This is done in just a couple of minutes. 12 pictures for the cell are drawn (I used paint for this) and their display is set depending on the cell status. Further, this cell is copied 400 times (there is no need to be intimidated here, since 400 cells are just nine operations Ctrl + c - Ctrl + v) and a minefield is obtained. We find on the Internet the first image that matches the meaning of the “sapper” logo. We fasten the display of the timer and the "New Game" button.
And now the most difficult thing remains - to choose pictures for victory and defeat in the game. Then I took the first images that appeared in the search engine for “atomic explosion” and “Victory”. And that's all - the game is ready.
Starting position:

We play:

Losing

: , , , .

And we win!

Summing up


Anyone who was able to master this post entirely noticed that we wrote a program, albeit simple, but still not at the “Hello, World!” Level, and at the same time we didn’t need absolutely any reference books, helpers, seating on the programmer’s forums, smoking manuals, etc. We implemented all that was needed in a couple of hours (and personally, most of the time I spent on a beautiful arrangement of algoblocks for pictures), while using even the schoolchildren’s algorithms for addition, subtraction, multiplication and logic algorithms “AND”, “OR”. Plus involved a few simple RS-triggers. The most complex algorithm used in the program is “Integrator”. And you and I used it simply as an adder with feedback and checking boundaries. Those.in fact, it is possible to replace the integrator with the “Addition” algorithm and two “Comparison” algorithms.
In other words, programming in FBD is simple and fun, and at the initial level no knowledge and skills are required at all except for understanding elementary logic. The main thing is a clear idea of ​​the final result. If it is available, the program itself is its logical conclusion. If we imagine “What we want to get in the end” is not yet possible, then it is probably worth more clearly formulating the task. At once I will make a reservation that all of the above does not apply to all projects implemented on FBD, but to simple little puzzles, like the one shown in our example. Sincefor huge projects with tens of thousands of signals to connect all this in my head probably will not work for anyone. But even in this case, the huge project is divided into small subtasks, where our approach is already becoming quite applicable (of course, taking into account the general requirements of the project).
Since the FBD language is universal and we have used the simplest algorithms in the program, which are basic for any implementations, then this program can be reproduced with minimal effort on any controller, both domestic and foreign (for example, Siemens or ABB controllers).

From what remains unrealized

Three functions of the classic sapper game remained unrealized.
The first is the ability to set the size of the field in the settings. This is done easily. As a matter of fact, the “Memory cell” algorithms are simply copied as many times as there are cells in the field. The problem is the same: lack of dynamic memory, i.e. impossibility to add or remove any data on the fly. And from this it follows a simple conclusion - it is very simple to make changing field sizes - you first need to program the task for the maximum field size, and then just put a logical sign on the unused cells, which by this sign will not be displayed in the graphical interface and will not be processed during the game.
The second is the substitution of the cell during the first click, if it was originally a bomb. Those.in our program, in contrast to the “sapper” in Windows, there is a chance to lose at the first click. This is also made easy; it is enough to check the incoming signal on the Game_over trigger and the number of closed cells. If we have the number of closed cells 399 and the signal came for the arming of the “Game_over” trigger, then we need to block this signal, reset the clicked cell and launch the algorithm for randomly installing another mine, while blocking the possibility of placing the mine in the already opened cell
Well, as you can see from the description - the statistics collection function is not implemented. Those.the best result is not remembered, the name of the person who set the record. The ratio of victories and defeats, etc. But this is all so trivial that it is literally done with a couple of mouse clicks, so I leave this function for implementation to everyone.

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

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


All Articles