📜 ⬆️ ⬇️

Dagaz: Rehearsal

image - Not true! This tale says something quite different.
“But if you already know what is being said in this fairy tale, why should I read it?”
- Then, I want to hear it!

Ted Chan " Your Life Story "


This is a big event when something suddenly starts to work. Unintelligible pages of code in Java and JavaScript, even less understandable XML, pictures drawn in Paint are all this together!
Now, this can be run and "touch." Tests could be run before, they helped get to this day. But how can you compare tests with a running program? Workable release! For many, this milestone marks the end of the journey.
')
I hope that for me this is just the beginning ...

As the first task for the test of strength, I chose "Sliding Puzzles" - puzzles with moving parts. It all started with the game " 15 " and Sam Loyd . This outstanding entrepreneur offered a large cash prize for solving a problem that has no solution. And the world has gone mad. When the degree of madness died down, other people offered to glue the pieces of the spots together to get even more interesting (but already solved) puzzles.


Papa's puzzle is perhaps the earliest known puzzle of this kind (under this name it was sold in America in 1926). The large square must be moved to the lower right corner. It's simple. There are (almost) no dead ends and forks, it is difficult to go astray.

To deliver a square to the lower left corner is a little more difficult. If you cut one of the rectangular pieces into two small squares and put them in a box over the other, you can get a much more complicated puzzle.


In childhood I was given this. I did not decide it. In France, this puzzle is known as “L'Ane rouge” (Ginger ass). A large square is required to be placed at the bottom, exactly in the middle (sometimes a hole was made through which it could be pushed out of the box). As far as I know, the minimum number of moves required to resolve the Red Ass is unknown. Martin Gardner, in his book Mathematical Leisure , describes a solution consisting of 81 moves.

Of course, "Red Donkey" is not the most difficult puzzle. There are a lot of games of this type (and some of them are very complex). Known puzzles with non-convex parts (like " Mother's Puzzle " or " Ascot Puzzle "). Some kits were developed in honor of historical events (such as the election of George Washington as president of America or the flights of famous pilots). This is a whole world, rich and diverse.

Unlike the game "15" there is no simple way to determine whether such a puzzle has a solution. Only bust! It is not surprising that programmers to them breathe so unevenly . This fad also did not pass me. I was doing "Red ass" when I mastered Delphi. Later, I developed a mobile application for Android. Now I do it again, though this time the puzzles themselves are not the goal — they are just a way to make sure that the approach I have chosen works. Private use of a more versatile product.

From the point of view of Dagaz , puzzles are single player games. There is a board and figures, there are rules for moving and the conditions for completing the game. No interaction of two (or more players). For me, this is convenient, because it allows you to run the project in the absence of some modules required for the implementation of more complex games. This is the zero iteration. A kind of prerelease project.

What does the implementation of Sliding puzzles on ZRF look like?
; for (2,1)-pieces (define shift2x1 ((verify (empty? w)) from w to cascade ee from w to (count-move) add) ((verify (empty? e)) from e to cascade ww from e to (count-move) add) (w (verify (piece? $1))(verify (empty? w)) e from w to cascade from w to (count-move) add) (e (verify (piece? $1))(verify (empty? e)) w from e to cascade from e to (count-move) add) ((verify (empty? n))(verify (empty? ne)) e (verify (piece? $1)) w from n to cascade se from n to (count-move) add) ((verify (empty? n))(verify (empty? nw)) w (verify (piece? $1)) e from n to cascade sw from n to (count-move) add) ((verify (empty? s))(verify (empty? se)) e (verify (piece? $1)) w from s to cascade ne from s to (count-move) add) ((verify (empty? s))(verify (empty? sw)) w (verify (piece? $1)) e from s to cascade nw from s to (count-move) add) (w (verify (empty? w)) (verify empty?) e from ww to cascade eee from ww to (count-move) add) (w (verify (piece? $1)) w (verify (empty? w)) (verify empty?) ee from ww to cascade e from ww to (count-move) add) (e (verify (empty? e)) (verify empty?) w from ee to cascade www from ee to (count-move) add) (e (verify (piece? $1)) e (verify (empty? e)) (verify empty?) ww from ee to cascade w from ee to (count-move) add) ) 

And this is only one of the macros controlling the coordinated movement of tile figures! Specifically, this provides the movement of the plate size of 2x1, rotated horizontally (for the vertical plate uses a different hardcode). Of course, not this terrible horror is my goal! In Dagaz, everything is much easier!

 (define step ( $1 add )) 

That's all! Of course, in real life, things are never that simple. In order for the puzzle to work as it should, the tile figures that make up one plate must move synchronously. In addition, they should not "run into" the tiles of other dice located on the board. All this is incredibly difficult to describe on ZRF. There is absolutely no point in doing this.

In JavaScript, this is done much easier!
 var distinctMode = false; var isEqual = function(a, b) { if ((a == 0) || (b == 0)) return false; return a == b; } var isEmpty = function(board, pos, value) { var piece = board.getPiece(pos); if (piece === null) return true; return isEqual(piece.getValue(0), value); } var isSubset = function(x, y) { for (var i = 0; i < x.actions.length; i++) { var action = x.actions[i]; if (_.chain(y.actions) .filter(function(a) { return a[0][0] == action[0][0] && a[1][0] == action[1][0]; }) .size() .value() == 0) return false; } return true; } Dagaz.Model.getPieceTypes = function(piece, board) { var tag = piece.getValue(0); return _.chain(board.pieces) .compact() .filter(function(piece) { return piece.getValue(0) == tag; }) .map(function(piece) { return piece.type; }) .uniq() .value(); } var CheckInvariants = Dagaz.Model.CheckInvariants; Dagaz.Model.CheckInvariants = function(board) { _.chain(board.moves) .filter(function(move) { if (move.actions.length != 1) return false; var action = move.actions[0]; if (!action[0]) return false; if (!action[1]) return false; if (action[0][0] == action[1][0]) return false; if (board.getPiece(action[0][0]) === null) return false; return true; }) .each(function(move) { var design = board.game.design; var action = move.actions[0]; var from = action[0][0]; var delta = action[1][0] - from; var piece = board.getPiece(from); var value = piece.getValue(0); if (isEmpty(board, action[1][0], value)) { _.chain(_.range(design.positions.length)) .filter(function(pos) { return pos != from; }) .filter(function(pos) { if (board.getPiece(pos) === null) return false; return isEqual(board.getPiece(pos).getValue(0), value); }) .each(function(pos) { var target = pos + delta; if ((Dagaz.find(design.positions[pos], delta) < 0) || (target < 0) || (target >= design.positions.length) || !isEmpty(board, target, value)) { move.failed = true; } else { move.movePiece(pos, target, null, 1); } }); } else { move.failed = true; } }); if (distinctMode) { var moves = []; _.chain(board.moves) .filter(function(move) { return (_.isUndefined(move.failed)); }) .each(function(move) { if (_.chain(moves) .filter(function(m) { return isSubset(m, move) && isSubset(move, m); }) .size() .value() == 0) { moves.push(move); } }); board.moves = moves; } CheckInvariants(board); } 

This module does everything we need. Yes, it is difficult, but it is written only once, after which it is reused in all puzzles of this type. Connecting a JavaScript module to the Dagaz core written in JavaScript is much easier than loading DDL files written in C ++ into the Zillions of Games Windows application. Why not use it?

  (option "smart-moves" from) (option "sliding-puzzle" true) 

The magic module is connected by the second option. The first includes a more friendly movement of shapes. What else is required to describe a new puzzle? A bit of template code to describe the board and player:

  (players You) (turn-order You) (board (grid (start-rectangle 0 0 100 100) (dimensions ("a/b/c/d/e" (100 0)) ; files ("3/2/1" (0 100)) ; ranks ) (directions (n 0 -1) (s 0 1) (e 1 0) (w -1 0)) ) ) 

If the size of the board is not 5x3, you will need to slightly edit the grid lines. Next, we describe the figures themselves (tiles):

 (define T (piece (name $1$2) (help " ") (image You "images/$1.bmp") (attribute value $2) (moves (step n) (step s) (step w) (step e) ) ) ) (T R0000 1) (T R0010C 2) (T R0001P 2) (T R0100C 3) (T R1000C 3) (T R0010P 4) (T R0001C 4) (T R0000P 5) (T R0100P 6) (T R1000P 6) (T R0000C 7) (T R0000 8) (T R0000 9) 

A macro called “T” simply saves us from a lot of monotonous writing. The ones and zeros in the shape name encode the picture used to display the tile on the board. The number added to them is the group identifier. Tiles in which it matches are moved together (this, as well as checking the possibility of such a move, is provided by our magic script). It remains to describe the initial arrangement of the figures:

  (board-setup (You (R00001 b3) (R0010C2 c3) (R0001P2 c2) (R0100C3 d3) (R1000C3 e3) (R0010P4 a2) (R0001C4 a1) (R0000P5 b2) (R0100P6 d2) (R1000P6 e2) (R0000C7 b1) (R00008 c1) (R00009 d1) ) ) 

and the goal of the game:

  (win-condition (You) (and (absolute-config R0010C2 (a2 b2 c2 d2 e2)) (absolute-config R0100C3 (a2 b2 c2 d2 e2)) (absolute-config R1000C3 (a2 b2 c2 d2 e2)) (absolute-config R0001C4 (a2 b2 c2 d2 e2)) (absolute-config R0000C7 (a2 b2 c2 d2 e2)) )) 

Putting it all together, we get an almost honest ZRF file . Launch it in Zillions of Games fail for two reasons.

  1. The traditional Zillions of Games knows nothing about the " sliding-puzzle " option
  2. He also does not support numeric attributes (but would be worth it)

Now, Z2J comes into play - it is such a special utility for turning ZRF files into something more understandable for Dagaz. Written in Java using the Xalan XSLT processor:

 /usr/bin/java -classpath "./z2j.jar:./log4j-1.2.jar:./xalan-2.7.1.jar:./serializer-2.7.1.jar" com.gluk.z2j.app.App ./twins.zrf 

After its launch, in the same directory where the ZRF file was located, two more files appear (you can not pay attention to the XML files, they are for debugging). The JavaScript file contains everything needed to set up the model, and the html file is created just for convenience. It can be run.


In principle, this entire part, with writing a ZRF file and translating it into JavaScript code, can be omitted by stuffing all the necessary settings with your hands, but you will have to fill in quite a lot and at the same time make a mistake. I am thinking of creating a small library that supports the creation of the most common board configurations and move patterns. If I do this, the ZRF file can not be written, limited to the development of JavaScript code.

What is the result


The approach I have chosen has proven to work. The code is laid out under a free license, anyone can use it. Intermediate creation of a ZRF file may seem redundant to someone, but for me, with my experience in developing applications for Zillions of Games, this is convenient. However, I am working to make this step optional. There are still a couple of important (and very complex) modules for launching board games and, of course, bots. I am also working on it. I'm not going to stop. There are a lot of board games. Enough for a long time.

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


All Articles