📜 ⬆️ ⬇️

Tame ZoG (Part 5: Mana Collection)

Today, I am completing a series of articles telling about the capabilities of the ZRF language used to develop games at Zillions of Games . Since I intentionally moved from simple to complex, it is logical to assume that today's game (in terms of implementation) will be more difficult than all previous ones. It really is. The fact is that ZRF in no case can not be attributed to the universal programming languages. It is intended to describe games similar to Chess. The more “chess-like” the game is, the more obvious is its description (unless, of course, paying attention to the description of the rules of such tricky moves as castling or taking on the aisle ). The description of such a game can be quite large in scope (in this case, PreZRF , which I wrote earlier ) can help, but rather trivial in content.

Everything changes when you have to do something for Chess is not at all (or not at all) similar. Creating applications such as Game of Life or Mine Finder is a serious challenge when using pure ZRF, without any extensions . Today I will try to show with what difficulties this development can be connected.

The source of inspiration, this time, was the collection of mana from the Battle vs Chess I mentioned earlier. A player is given one or several chess pieces with which he must put forks , removing randomly placed “crystals” from the field:


')
Let's try to figure out what difficulties we will encounter when trying to implement a similar mini-game using ZRF. The first difficulty is to count the number of pieces under combat, which is necessary to determine the presence of a "fork". As I said, with all sorts of calculations in the ZRF tight, but, in particular cases, the problem is completely solvable:

Definition of forks
(define common-check mark (if (on-board? $1) $1 (while (and empty? (on-board? $1)) $1) (if enemy? (if (flag? is-first) (set-flag is-second true) else (set-flag is-first true) ) ) ) back ) (define common-capture mark (if (on-board? $1) $1 (while (and empty? (on-board? $1)) $1) (if enemy? capture) ) back ) (define queen-slide ($1 (while empty? (set-flag is-first false) (set-flag is-second false) (common-check n) (common-check s) (common-check w) (common-check e) (common-check nw) (common-check ne) (common-check sw) (common-check se) (if (flag? is-second) (common-capture n) (common-capture s) (common-capture w) (common-capture e) (common-capture nw) (common-capture ne) (common-capture sw) (common-capture se) ) add $1 ) ) ) 


Since we don’t have to know the exact number of pieces “under attack” to “count”, two boolean flags are enough. If, after a series of common-check checks in various directions, the is-second flag is set , it is necessary to remove all the figures that are “under attack”.

An important (and not obvious for understanding) point is that all these checks and captures must be performed before the formation of the add command, which completes a possible move (since they are part of this move). In addition, unlike conventional chess, we cannot go to a busy field (enemy figure).

Already in this place, the "cunning" of ZRF is fully manifested. After playing a bit, you can notice the following bug:



It can be noted that the figure located behind the starting position, in the direction opposite to the course, is not considered to be “under attack”. This is due to the fact that, at the time of calculating the course, the initial field is not considered empty. Having understood the cause of the error, it is easy to fix it:

Corrected definition of forks
 (define common-check mark (if (on-board? $1) $1 (while (and (or (position-flag? is-start) empty?) (on-board? $1)) $1) (if enemy? (if (flag? is-first) (set-flag is-second true) else (set-flag is-first true) ) ) ) back ) (define common-capture mark (if (on-board? $1) $1 (while (and (or (position-flag? is-start) empty?) (on-board? $1)) $1) (if enemy? capture) ) back ) (define queen-slide ( (set-position-flag is-start true) $1 (while empty? (set-flag is-first false) (set-flag is-second false) (common-check n) (common-check s) (common-check w) (common-check e) (common-check nw) (common-check ne) (common-check sw) (common-check se) (if (flag? is-second) (common-capture n) (common-capture s) (common-capture w) (common-capture e) (common-capture nw) (common-capture ne) (common-capture sw) (common-capture se) ) add $1 ) ) ) 


We simply mark the initial field with the position flag and treat it further as empty.

The generalization of the rules formulated on the moves of the other figures is a matter of technique. This solution works perfectly, in the case of playing with one queen, but when playing, for example, with two rooks, it is not complete.



The program does not take into account that one shape, moving, can "open" the plug with another shape. This is exactly the case when it is quite clear what to do to correct the error, but (for now) it is not quite clear how. Obviously, you need to go through all your pieces and check forks from each of them. To automate the search for all the pieces on the board, it is useful to create a “direction” connecting all the fields in the chain:

Link all fields
 (define Next-Definitions (dummy offboard) (links next (a1 b1) (b1 c1) (c1 d1) (d1 e1) (e1 f1) (f1 g1) (g1 h1) (h1 a2) (a2 b2) (b2 c2) (c2 d2) (d2 e2) (e2 f2) (f2 g2) (g2 h2) (h2 a3) (a3 b3) (b3 c3) (c3 d3) (d3 e3) (e3 f3) (f3 g3) (g3 h3) (h3 a4) (a4 b4) (b4 c4) (c4 d4) (d4 e4) (e4 f4) (f4 g4) (g4 h4) (h4 a5) (a5 b5) (b5 c5) (c5 d5) (d5 e5) (e5 f5) (f5 g5) (g5 h5) (h5 a6) (a6 b6) (b6 c6) (c6 d6) (d6 e6) (e6 f6) (f6 g6) (g6 h6) (h6 a7) (a7 b7) (b7 c7) (c7 d7) (d7 e7) (e7 f7) (f7 g7) (g7 h7) (h7 a8) (a8 b8) (b8 c8) (c8 d8) (d8 e8) (e8 f8) (f8 g8) (g8 h8) (h8 offboard) ) ) (game ... (board (Board-Definitions) (Next-Definitions)) ) 


In addition to the next direction, we also define a fictitious offboard field used to complete the iteration . This is a fairly common idiom ZRF. In addition, in addition to the ability to link all fields, the links command can be used to create a variety of boards with a "modified topology." With its help, you can, for example, “glue” the edges of the board, turning it into a cylinder, torus or Mobius strip.

Now you can use the created direction. Unfortunately, the attempt to "bust" his pieces directly within the framework of the course of one of them faces technical difficulties. Before starting the search, you need to remember the current position (in order to subsequently create the final move command add ). Usually, a couple of mark / back commands are used for this, but we already use them in common-check and common-capture , and the stack of saved positions is not supported by the mark command.

In order to solve this problem, create a fictitious player ? Clean :

 (game ... (players Black ?White ?Clean) (turn-order ?White ?White ?White ?White ?White ?White ?White ?White Black ?Clean) ) 

As I mentioned in the previous article , the question mark at the beginning of the player's name means that he does not directly participate in the game and will execute his moves randomly. But how exactly will it go ? Clean ? We need the moves of this player solely for the sake of a side effect (during his turn, he will check forks on the board and remove the pieces that fall under them). Obviously, you should not ? Clean- move figures, then have to put ( drop ) them on the board:

Search all forks
 (define common-check mark (if (on-board? $1) $1 (while (and (or (position-flag? is-start) (or (piece? Cleaner) empty?)) (on-board? $1)) $1) (if (piece? Stone) (if (flag? is-first) (set-flag is-second true) else (set-flag is-first true) ) ) ) back ) (define common-capture mark (if (on-board? $1) $1 (while (and (or (position-flag? is-start) (or (piece? Cleaner) empty?)) (on-board? $1)) $1) (if (piece? Stone) capture) ) back ) (define clean-queen ( (verify empty?) (set-position-flag is-cleaner true) a1 (while (not-position? offboard) (if (piece? Queen) (set-flag is-first false) (set-flag is-second false) (common-check n)(common-check nw) (common-check s)(common-check ne) (common-check w)(common-check sw) (common-check e)(common-check se) (if (flag? is-second) (common-capture n)(common-capture nw) (common-capture s)(common-capture ne) (common-capture w)(common-capture sw) (common-capture e)(common-capture se) ) ) next ) a1 (while (not-position? offboard) (if (position-flag? is-cleaner) add ) next ) ) ) (define slide ( (set-position-flag is-start true) $1 (while empty? add $1 ) ) ) (game ... (players Black ?White ?Clean) (turn-order ?White ?White ?White ?White ?White ?White ?White ?White Black ?Clean) (board (Board-Definitions) (Next-Definitions)) (board-setup (?Clean (Cleaner off 1) ) (?White (Stone off 8) ) (Black (Queen e4) ) ) (piece (name Stone) (image ?White "images\Chess\SHaag\wpawn.bmp") (help " ") (drops (add-to-empty) ) ) (piece (name Cleaner) (image ?Clean "images\DarkChess\Invisible.bmp") (drops (clean-queen) ) ) (piece (name Queen) (image Black "images\Chess\SHaag\bqueen.bmp") (help "Queen: can slide any number of squares in any direction") (moves (slide n)(slide ne) (slide e)(slide nw) (slide s)(slide se) (slide w)(slide sw) ) ) ) 


There are quite a few changes here, but the general meaning, I think, is clear. We allowed the player ? Clean to put his figure on the field (immediately after the Black move), checking, during the process, the presence of forks. Since this figure is not related to the gameplay, it is desirable to make it invisible. The resource of a completely transparent figure can be taken, for example, from this funny game .

? Clean does an excellent job with fork detection, but what about the figure he adds? If this figure is even three times invisible, so that it does not interfere with the gameplay, it is advisable to remove it from the board. Do this on the course ? White :

Scavenging
 (define add-to-empty ( (verify empty?) (set-position-flag is-cleaner true) a1 (while (not-position? offboard) (if (piece? Cleaner) capture ) next ) a1 (while (not-position? offboard) (if (position-flag? is-cleaner) add ) next ) ) ) 


In order for Clean to use his shape many times, we will enable the corresponding option:

 (option "recycle captures" true) 

In principle, it all works, but if Black fails to put the plug during the move ,? Clean will spend his piece, and ? White will not be able to clear it, since he will not be able to make a move (since all his 8 pieces are already on the board). Obviously, we must give the opportunity to complete the move ? Clean only on the condition that he managed to find at least one plug:

Fixed fork search
 (define clean-queen ( (verify empty?) (set-flag is-succeed false) (set-position-flag is-cleaner true) a1 (while (not-position? offboard) (if (piece? Queen) (set-flag is-first false) (set-flag is-second false) (common-check n)(common-check nw) (common-check s)(common-check ne) (common-check w)(common-check sw) (common-check e)(common-check se) (if (flag? is-second) (set-flag is-succeed true) (common-capture n)(common-capture nw) (common-capture s)(common-capture ne) (common-capture w)(common-capture sw) (common-capture e)(common-capture se) ) ) next ) a1 (while (not-position? offboard) (if (and (flag? is-succeed) (position-flag? is-cleaner)) add ) next ) ) ) 


In order for the game not to end abruptly, if one of the players cannot move, the following option will help us:

 (option "pass turn" 2) 

Ufff ... Now everything works (though not as beautiful as in the original, but then we decide what set of pieces we will play). Of course, this whole structure strongly resembles the construction of crutches supporting each other, but such is the price for doing something non-trivial in ZRF.

As a dessert, I suggest enjoying the following version of Chess:



All the pieces go the same way as in ordinary Chess. I made only one change, but it radically changed the whole course of the game. Since I am subjective, it is difficult for me to evaluate its merits, but I can say that, for me personally, it is much more difficult to win at the computer than in ordinary Chess. The game is very dynamic and rarely lasts longer than 10 moves. Put the plugs to win!

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


All Articles