Tamed ZoG (Part 5: the Gathering mana)

Today I complete the cycle articles telling about the capabilities of the ZRF language used for game development in Zillions of Games. Because I was deliberately moving from simple to complex, it is logical to assume that today's game (in terms of sales) will be more difficult for all previous. It is really so. The fact that ZRF in no case be attributed to universal programming languages. It is designed to describe games similar to Chess. The more "somatotopy" the game, the more obvious its description (unless, of course, do not pay attention to the description of the rules such tricky moves as rokirovka or take on the aisle). Description this game can be quite lengthy (in this case, can help PreZRFthat I wrote previously), but rather trivial in content.

Everything changes when you have to do something in Chess is not (or not entirely) similar. The creation of such applications as Game of Life or Mine Finder is a serious challenge, in the case of using pure ZRF, without any extensions. Today I will try to show what difficulties may be related to such development.

A source of inspiration, this time, I served as a mini-game of "Collect the mana" from already mentioned by me earlier Battle vs Chess. The player is given one or more chess pieces, that he must put wilkicleaning the fields we arrange random "crystals":



Try to understand what difficulties we will face trying to implement a similar minigame using ZRF. The first difficulty is to count the number of figures under the battle needed to determine whether a "fork". As I said, with all sorts of calculations in ZRF tight, but, in special cases, solvable problem:

the Definition of "fork"
(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 flat-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 that are "under attack", "counting", only two Boolean flags. If, after a series of audits of the common-check in different directions, the flag is second is cocked, you must remove all the shapes that are "under attack".

Important (and not obvious to understand) the point is that all these checks and sampling needs to be performed before the formation of the team add, final possible (as they are part of this course). In addition, unlike in standard chess, on occupied (enemy) field walking we can't.

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


You can see that the figure behind the starting position, in the opposite stroke direction, is not regarded as being "under attack". This is because, at the time of calculation speed, the initial field is not considered empty. Realizing the cause of the error, easy to fix:

Fixed detection of "fork"
(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 flat-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 just mark the starting field position flag and then see it as empty.

The generalization formulated the rules on the moves of the other figures is a matter of technique. Eto the solution works great, if you play the Queen, but when playing, for example, two rooks it is not complete.



The program ignores the fact that one-pieces are moving, can "open up" the plug. This is exactly the case when it is quite clear what to do to fix it, but not (yet) quite clear how. Obviously, you want to shuffle through all your figures and check the plugs from each of them. To automate the search of all the pieces that are on the Board, it is useful to create a "direction" that links all the fields in the chain:

link all fields
(define Next-Definitions
(dummy an 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) (e5 d5) (f5 e5) (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) (h8 g8) (h8 an offboard)
)
)

(game
...
(board (Board-Definitions) (Next-Definitions))
)


In addition to next, we also define a dummy field an offboard used to complete the bust. This is a fairly common idiom ZRF. In addition, besides the possibility of concatenating all the fields, links can be used to create a variety of boards with a "modified layout". With its help, you can, for example, "glue" the edges of the Board, turning it into a cylinder, a torus or a Mobius strip.

You can now use the generated direction. Unfortunately, the attempt to "sort out" his figures directly in the framework of the course one of them is facing technical difficulties. Before the bust, need to remember the current position (in order to, subsequently, to form a final move command add). Usually, it uses a pair of commands mark/back, but we already use them in common-check and common-capture, and stack of saved positions with mark not supported.

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

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

As I have said in the previous article, the question mark at the beginning of a player's name means that he is directly not involved in the game and to perform its moves at random. But how it will go ?Clean? The moves that player we need solely for the side effect (at the time of travel will be checking on the Board plugs and removing fallen under their figures). Obviously, you should not ?Clean-om moving figures, so will have to put (drop) on their 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? an 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? an 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 1 off)
)
(?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, but the General sense, I think, clear. We allowed the player ?Clean to put on the field his figure (immediately after Black), checking the process, the presence of plugs. Because this figure has nothing to do with gameplay, it is desirable to make it invisible. The resource is completely transparent one can take, for example, from this funny games.

?Clean copes with his work on the detection of forks, but what about adding them figure? Whether this figure is at least three times invisible, so it does not interfere with gameplay, it is desirable to remove it from the Board. We will do this in the course of ?White:

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


To ?Clean could have used the figure repeatedly, enable the appropriate option:

the
(option "recycle captures" true)

In principle, it works, but if Black during a move will not put the plug ?Clean spend your figure and ?White will not be able to clear, because they will not be able to make a move (because all 8 shapes already on the Board). Obviously we should give the opportunity to complete the course ?Clean only on the condition that he was able to find at least one fork:

Fixed search forks
(define clean-queen
( (verify empty?)
(set-flag is-succeed false)
(set-position-flag is-cleaner true)
a1
(while (not-position? an 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? an offboard) 
(if (and (flag? is-succeed) (position-flag? is-cleaner))
add
)
next
)
)
)


In order for the game not suddenly ended, if you cannot turn one of the players, we can use the following option:

the
(option "pass turn" 2)

Ufff... Now all works (though not as nice as the original, but we decide which set of figures we play). Of course, the whole structure is strongly reminiscent of the structure of supporting each other's crutches, but such is the price to pay to do in a ZRF anything not trivial.
As a dessert, include the following option Chess:



All the pieces move as in usual Chess. I made only one change, but it radically changed the entire course of the game. Because I'm subjective, I find it difficult to appreciate its merits, but I can say that I personally to win her at the computer a lot harder than ordinary Chess. The game is very dynamic and rarely lasts longer than 10 turns. Put the plugs to win!
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

When the basin is small, or it's time to choose VPS server

Performance comparison of hierarchical models, Django and PostgreSQL

From Tomsk to Silicon Valley and Back