Chase with obstacles


A slow sort of country! — said the Queen. — Well, here,
you know, you have to run just to stay
in the same place! If you want to get to another place, then
you need to run at least twice as fast!

Lewis Carroll "Alice in Wonderland"


Today, I want to tell you about an amazing and underrated game, which I previewed a little less than two years ago. In a sense, it is with this game, and also Ura, began my acquaintance with Dmitry Skyroom. In those days, I began to be interested in Board games. My knowledge was scant and, in many ways, naive. Games such as "chase", literally opened for me vast new world. Even now, working on this game, to a large extent, resembles a detective story. In this respect, the game "Chase" completely justified as its name and similarity with the known alias American writer.

The game was developed by Tom Kruszewski and released by the company "TSR" in 1986. In addition to special boards, each player has 10 six-sided dice, but despite this the game is not gambling. The dice are thrown only once, to determine the order of turns and then used only as shapes. The number of points on the top face shows the number of steps, which can be moved on the dice. So the cube with one point can be moved to the next field in any of the six directions, with two points — two squares in a straight line, with three — on-three, etc. the Cube should move exactly the specified number of steps, no more and no less. In the process of moving, the cube is not rotated by the other party (the number of points on the upper side is not changed). The initial setup is shown below:


Details
For each player, the total number of points on the top edges is 25. The player is obliged to maintain this amount until the end of the game. The players take turns and if one of them takes one (or two, this is also possible) of the figure, his adversary is obliged to add the amount of points out of the game to my cube with the minimum number of points (in the beginning of the game, it's one of the ones). If you then distributed to all the points, the balance is allocated next, always starting from the cube with the fewest points. The player who is less than 5 dice, loses because it cannot allocate the required number of points remaining on the Board cubes.


The edge of Board not impede the movement of the figures. The left and right boundaries of the Board "glued" together, but from the upper and lower bounds of the shape rebound "rebound". Of course, this does not mean that the pieces are moving freely. Pieces can "jump" each other, and the Central field "Chamber". To capture the opponent's pieces, the figure should "stand up" on it (chess capture) by the total number of steps in a straight line. The course may end in the shape of its color. In this case, "bumping" is a figure caught in the target field is shifted one step, continuing the direction of movement (taking into account sklonnosti boards and ricochets). If the next field was also occupied by her figure, "bumping" extends next to the first empty field or a field occupied by an opponent's piece (the other figure is taken). Only one obstacle can make such a course impossible — it is forbidden to push the shapes in the center square, using bumping.
You can see that from the initial position, each player can cyclically shift all your pieces, if any of the ones in the direction of two. Such a move allowed by the rules. Also allows "sharing" points between shapes of the same color on the adjacent fields. So a couple of 5 and 2 can turn into 4 and 3 or 1 and 6. Such action is considered a move. Not considered only one type of stroke. None of the pieces can not pass through the Central square of the Board (theChamber), but it can finish its movement on the field. If this occurs, the figure "splits" into two, keeping the total number of points. The figure is always divided so that the points of one figure is exceeded points other no more than 1. The total number of pieces, each player may not exceed 10 (this is precisely the case in the beginning of the game, each player has 1 die in reserve).


Direction "dispersion" of the fragments resemble the tip of an arrow. A cube with a large number of points (if there is one) always goes to the left. In two special cases 'splitting' is impossible. First, as I said above, the number of cubes of the same color cannot exceed 10. Furthermore, it is obvious that splitting cube with 1 point will fail. In both of these cases, the dice included in the Chamber, leaves unchanged, in the left direction. Each of the figures, who left the Chamber can trigger bumping, once your figure or take a figure of the opponent (the only way to take the two enemy pieces at the same time).

I must say that Tom Kruszewski and "TSR" greatly overestimated the capabilities of their potential audience. For the mass market, the game was too complex (chess is no less complex, but they are all used). The manufacturer ceased production and now, "chase" can be purchased only from the owner, at different fairs, auctions and sales. However, this game is considered to be one of the best games of the 20th century.

Easy operation


The game starts with the Board and the Board in the Chase... kind of. Previously I had to do the game on hexagonal boards and it was the first (very small) obstacle. This is an interesting point and I want to talk about it in detail. The mechanism for describing Board games in the ZRF is well laid out and allows you to implement almost any Board, provided that they are displayed on the plane and do not change during the game.

how it works
(board
(image "../Images/Chase/board.bmp")
(grid
(start-rectangle 48 32 108 82)
(dimensions
("a/b/c/d/e/f/g/h/i/j/k/l/m" (60 0))
("1/2/3/4/5/6/7/8/9" (-30 52))
)
(directions (se 1 1) (w 1 0) (sw 0 1)
(nw -1 -1) (e -1 0) (ne 0 -1))
)
(kill-positions
j1 k1 l1 m1 j2 k2 l2 m2 k3 l3 m3 a3 
a4 k4 l4 m4 l5 m5 a5 b5 a6 b6 l6 m6 
a7 b7 c7 m7 c8 m8 a8 b8 a9 b9 c9 d9
)
)

I'm not advocating that details of the model, mixed with issues of visualization, but as long as you do not want to separate one from the other (for example to display the Board in a "fair" 3D, not isometric), this approach works well. Consider this description more:

the
    the
  • an Integral part of the description is a file containing an image of the Board. All geometrical dimensions and positions of the pieces tied to it (it is for this reason, most of the distribution of my implementation "Sokoban" make black rectangles of various shapes and sizes). A file containing the Board picture in BMP-format (ZoG only understands this format) is defined by the keyword image. Here you can specify multiple files (to enable switching between skins), but only with identical geometric proportions.
  • the
  • the keyword grid allows to describe an n-dimensional array positions. In most cases, it is the usual two-dimensional Board, but you can also specify a different Board sizes (up to five). The Board may consist of several grids, provided that ensures the unique naming of individual items. With a strong desire, you can even place a single grid on top of the other, similar to how it is done in "Quantum TIC-TAC-toe".
  • the
  • a description of "measurement" (dimensions). Each description contains a string of names (of which the Cartesian product are combined, the names of the positions), and two integers. In these numbers lies the "magic" to describe hexagonal and isometric planks. It is nothing but developments, which are shifted to the next grid cell. Usually (for two-dimensional boards), in one dimension, the cells are shifted to the cell width x, and the other to the height of the cell to y, but additionally shifting these cells to the half-width x, it is possible to obtain an excellent basis for a hexagonal Board.
  • the
  • the Second component of "magic" grids — direction (directions). The Board is not only the position but also the relationships (named or unidirectional) between them. Of course, nobody bothers to identify each connection individually, providing a name and a couple of positions for each connection, but the determination of the boards of large dimensions, the process will not be happy. directions allows you to manipulate not names, positions and directions inside the grid.
  • the
  • to get the Board required form, we take the "rectangular" Board larger, and then displace the rows of half-cells relative to each other. As a result, no "extra" positions that must be "cut off" from the Board. Key word kill-positions allows you to declare a previously defined item name is invalid. Of course, along with the removed items are broken and the corresponding connections.


The use of the keyword grid can significantly reduce the amount of manual work in describing "typical" boards, but this approach is not devoid of shortcomings. First, if the image boards are not pictured for the chosen geometric dimensions, operating only integer coordinates and offsets, it is difficult to align the location of all the items the boards are ideal. Individual description of position is less simple, but allows you to adjust their position independently from each other. However, it requires just a murderous amount of manual work (taking into account the need of correction of the admitted typing errors). In order to facilitate this process, I use grid to "draft" descriptions, and then receive an individual description of the items, with a small script:

Script
my @grid;
my %kp;
my $sx, $sy, $dx, $dy;
my $dm = 0;

while (<>) {
if (/\(start-rectangle\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\)/) {
$sx = $1; 
$sy = $2;
$dx = $3 - $1;
$dy = $4 - $2;
}
if (/\(\"([^\"]+)\"\s+\((-?\d+)\s+(-?\d+)\)\)/) {
my @a = split(/\//, $1);
$grid[$dm]->{ix} = \@a;
$grid[$dm]->{x} = $2;
$grid[$dm]->{y} = $3;
$dm++;
}
if (/\(kill-positions/) {
$fl = 1;
}
if ($fl) {
if (/\s(([a-z0-9]{1,2}\s+)+)/i) {
my @a = split(/\s+/, $1);
foreach my $p (@a) {
$kp{$p} = 1;
}
}
if (/\)/) {
$fl = 0;
}
}
}

sub try {
my ($ix, $pos, $x, $y) = @_;
if ($ix < $dm) {
my $i = 0;
foreach my $p (@{$grid[$ix]->{ix}}) {
try($ix + 1, $pos . $p, $x + $i * $grid[$ix]->{x}, $y + $i * $grid[$ix]->{y});
$i++;
}
} else {
if (!$kp{$pos}) {
my $a = $sx + $x;
my $b = $sy + $y;
my $c = $a + $dx;
my $d = $b + $dy;
print " ";
printf "($pos %3d %3d %3d %3d)\n", $a, $b, $c, $d;
}
}
}

try(0, ", 0, 0);


Result
 (positions 
(a1 48 32 108 82)
(a2 84 78 18 134)
(b1 108 32 168 82)
(b2 78 84 138 134)
(b3 48 136 108 186)
(b4 78 18 188 238)
(c1 168 32 228 82)
(c2 138 198 134 84)
(c3 108 136 168 186)
(c4 78 138 188 238)
(c5 48 240 290 108)
(c6 18 292 342 78)
(d1 228 32 288 82)
(d2 198 84 134 258)
(d3 168 136 228 186)
(d4 138 188 198 238)
(d5 108 240 168 290)
(d6 78 292 138 342)
(d7 48 394 344 108)
(d8 18 78 396 446)
(e1 32 288 348 82)
(e2 258 84 318 134)
(e3 228 136 288 186)
(e4 198 188 258 238)
(e5 168 240 228 290)
(e6 138 292 198 342)
(e7 108 168 394 344)
(e8 78 396 138 446)
(e9 48 448 108 498)
(f1 348 32 408 82)
(f2 318 84 378 134)
(f3 288 136 348 186)
(f4 258 188 318 238)
(f5 228 240 288 290)
(f6 198 258 292 342)
(f7 344 168 228 394)

(f9 108 448 498 168)
(g1 408 32 468 82)
(g2 378 438 134 84)
(g3 348 136 408 186)
(g4 188 378 318 238)
(g5 288 290 348 240)
(g6 258 292 318 342)
(g7 228 288 344 394)
(g8 198 396 446 258)
(g9 168 448 228 498)
(h1 468 528 32 82)
(h2 438 498 134 84)
(h3 136 408 468 186)
(h4 378 188 438 238)
(h5 408 290 348 240)
(h6 292 318 378 342)
(h7 288 344 348 394)
(h8 258 396 318 446)
(h9 228 448 498 288)
(i1 588 528 32 82)
(i2 498 558 134 84)
(i3 468 136 528 186)
(i4 438 188 498 238)
(i5 240 408 468 290)
(i6 292 438 378 342)
(i7 348 344 408 394)
(i8 318 396 378 446)
(i9 288 348 448 498)
(j3 528 136 588 186)
(j4 498 188 558 238)
(j5 468 240 528 290)
(j6 438 292 498 342)
(j7 408 344 468 394)
(j8 378 396 438 446)
(j9 348 448 408 498)
(k5 528 240 588 290)
(k6 292 558 498 342)
(k7 468 344 528 394)
(k8 438 396 498 446)
(k9 408 448 468 498)
(l7 528 344 588 394)
(l8 498 396 558 446)
(l9 468 448 498 528)
(m9 448 528 588 498)
)


This is only half the battle! The names of the positions boards should be corrected to bring them into line with accepted notation. In addition, you associate a pair of position areas, not forgetting the "loop" Board at the edges. All together resulted in a big volume handmade, but I did not write for this case a script (although it is possible and cost).

the Sleep of reason


Even though I met "chase" for quite a long time to play it, until recently, did not succeed. Too fancy is required for this Board. With some skill, you can play the Board Shogi (9x9), but I was not there. Regular chess Board (8x8) for this game the absolutely. Board for "chase" managed to acquire in the past, "Silentmode" but the blocks in the kit are not included. Its acquisition of I put on a distant shelf and there it probably would have lain, if the case had not intervened in the case.

no Accidents
My daughter was invited to a birthday family that we are friends. As a gift, was selected Board game, and as several adults had to sit about three hours in the children's cafe, they (and me) also needed something to do. As a possible variant, was proposed by another game, but since I prefer the more abstract games, you want also something to bring. Initially, I thought about Ur, but I have had the set, it's D2 "bones", made in the form of half-round sticks (the more typical Senet), was quite uncomfortable when the cast made a lot of noise and could interfere with others.

Then I remembered the "chase". Had to refill it a set of twenty dice, but since I still went to the store Board games (for a gift), it is (as I then thought) was not a problem. On the website, I have chosen a wonderful translucent cubes (70 rubles apiece), but life has made adjustments. In the store it became clear that looked after me cubes are only one instance. What can I say, Kazan — Moscow, had to settle for a budget option and gain the coveted blocks of the proposed seller placer ICO, -, ship - and other-aedro. Red or green set has not been collected, but the blue and white (okay, okay, one slightly is yellowish) cubes were available.

Rules I, of course, distorted (talked about memory). In my presentation, the trajectory of the dispersion "fragments", leaving the "Replicator" that does not resemble an arrowhead, but rather the Latin letter 'Y'. Apparently, a certain role was played by its similarity with the schemes of decay of elementary particles. "Fragments" didn't move forward one square (as in the original version of the rules), and in accordance with their "denomination". In addition, this course was much easier to block. Any obstacles (whether the figure standing on the path of expansion "fragments" or the presence on the Board of ten figures) are treated as failure execution progress. In the original version of the rules to block the "Chamber" only by setting the piece in the way of the entrance.
Another link "damaged phone" he was Dmitry. In his description of the chase, he mentioned that the figure that performed the capture, is entitled to repeat the course (similar to Checkers). source there was no word about it (which he did not fail to inform dear Gest), but I, at that moment, I did not pay attention to it. I must say, the idea of cross chase with "Swords" had already caused a lot of questions. Should we extend the rule to re turn to the case of bumping-huh? On the fragments obtained during the separation of the figure? What to do if the capture was performed, each of the fragments? But if the same bumping? But no such difficulties, which we could not afford to create! I enthusiastically went to work...

the Sunset hand
of Course, first and foremost, I tried to use the mechanism of partial moves that are used in ZoG games like checkers. Very recently it really useful, in the process of creating a very difficult game. So far, I have not had to use it in Axiom, but all once happens for the first time. The essence of partial stroke that challenging composite course is broken down into small steps. In checkers, capture an enemy piece that is sold in such partial swing. At the same time, used the so-called "modes" of execution speed that allows you to specify that the next partial stroke shall to perform the capture.

I'm not thrilled with the implementation of the composite moves in ZoG and here's why. First of all, in understanding ZoG partial moves — this is separate, independent action. In fact, it's just a collection of moves performed by the same player, for each other. We will not share any intermediate information between the partial moves! Global and positional flags are automatically reset at the beginning of each turn. It is devilishly inconvenient, but it is only part of the trouble! ZoG may not consider a compound move as a single entity (in particular, for this reason we had to introduce hardcoding the option "maximal captures", the implementation of "majority rules". Some other ideas that do not fit into this hardcod implement will not succeed!



This is a fragment of the party from "Mana", invented by Claude Leroy. The number of dashes, for each item shows how many steps you can move the figure. Should be executed the exact number of steps and, thus, during movement it is impossible to turn back. This is where we set up for an ambush! Very rarely, but it happens that the figure doing the two step, driving himself "in an impasse". She can't continue as to interfere with her other pieces and has to make another step because you need to complete the course! And ZoG, in turn, provides absolutely no tools to solve this problem!

Another limitation is that a composite course may only continue the same figure, which moved previous partial course. That's exactly what happens in checkers, but in the "chase" situation is slightly more complicated. For example, a capture may be achieved using the bumping-and that is not the figure who had completed the course! With Chamber-a progress still more difficult. Both of the shard can take the opponent's pieces and, logically, have the right to perform the following partial course. And they both are not the figure that came in the Chamber (the figure on the Board at all anymore)!

Less words, more code
: val ( -- n )
piece-type mark -
;

: mirror ('--dir', dir )
DUP ['] nw = IF
DROP ['] sw
ELSE
DUP ['] ne = IF
DROP ['] se
ELSE
DUP ['] sw = IF
DROP ['] nw
ELSE
['] se = verify
['] ne
ENDIF
ENDIF
ENDIF
;

: step ('--dir', dir )
EXECUTE THE DUP NOT IF
mirror
DUP EXECUTE verify
ENDIF
;

: bump ( 'dir -- )
BEGIN
here E5 <> verify
friend? from here <> AND IF
piece-type SWAP SWAP step
create-piece-type
FALSE
ELSE
TRUE
ENDIF
UNTIL DROP
;

: slide ( 'dir n -- )
alloc-path !
val SWAP BEGIN
step
SWAP 1 - DUP 0= IF
TRUE
ELSE
my-empty? verify
SWAP FALSE
ENDIF
UNTIL DROP
from here move
+ enemy? IF
+ cont-type partial-move-type
+ ENDIF
bump enemy? IF
alloc-all
ELSE
alloc-path @ 0= verify
ENDIF
add-move
;


Ultimately, it comes down to adding a call to partial-move-type in the capture of enemy pieces (before executing bumping-a). Restrictions, I mentioned above remain in force. We cannot perform a partial stroke, if the capture was not the figure which began the course (as a result of bumpingand / or "splitting" in the Chamber), but even in this form, this code would be a good solution. If he'd


I was not able to decipher the rebus and simply sent the source code to the developer Axiom. Greg hasn't replied yet, but it seems to be working on a patch, which I hope will solve the problem. What's strange is that partial moves in Axiom really work! Moreover, they significantly expand the functionality of the ZRF. All this is well described in the documentation and is used in several applications. Apparently I was just unlucky.

Since fractional moves are not working, had to find another way to solve the problem. If you are not able to perform all actions in one stroke, you can try to stretch them a few moves! I've done it in other games, creating on the Board a special invisible position, which was a figure-a flag. If the figure belonged to the enemy, the player had to miss a turn. This small change, but it led to others. I had tag shape continues running (now it could be not only the figures that started the run) and complicate order to transfer motion. Overall, it was rather bulky and very cumbersome solution.

The result of my efforts was a very original modification of the game, unfortunately had too little in common with the original. In addition, the use of "complex" order of transmission of moves (turn-order) backhand beat the "intelligence" of AI. Used minimax algorithm negatively react to such liberties, and in "immune" to them search-engine (an alternative version of the construction Axiom AI) is incredibly difficult to implement a depth-first search.

bread crumbs


Well, let's say we move, pick up one (or even two figures) of the enemy, and then distributed the points received for the remaining of his figures, always starting with the youngest. But what if a few younger figures? For example, in the beginning of the game, each player has two "ones". Taking any figure of value in range from one to five points, we get two versions of the distribution points and the game can seriously change, depending on which one we choose.

the same and combinatorics
Here almost of the blue, there is an interesting combinatorial problem. In order to understand in what ways (if taking figure) can be distributed points, it is necessary to imagine all the combinations of the shapes (on the side of one of the players), able to appear in the game. There are only three conditions:

    the
  1. Each figure can have a value from 1 to 6 points
  2. the
  3. Number of pieces may not exceed 10
  4. the
  5. Total number of points is always equal to 25

I have no doubt that this problem has a beautiful analytical solution (perhaps readers will suggest), but I did not look for him. I just made a script to generate all possible sets of pieces that match the specified conditions. The figures in the sets are ordered by face value, because it is in this manner distributed points taken.
Script
my @d;
my %s;

sub out {
my ($deep) = @_;
for (my $i = 0; $i < $deep; $i++) {
print "$d[$i]";
}
print "\n";
}

sub dice {
my ($start, $deep, $sum) = @_;
if ($sum == 25) {
out($deep);
}
if ($deep < 10 && $sum < 25) {
for (my $i = $start; $i <= 6; $i++) {
$d[$deep] = $i;
dice($i, $deep + 1, $sum + $i);
}
}
}

dice(1);


Result
1111111666
1111112566
1111113466
1111113556
1111114456
1111114555
1111122466
1111122556
1111123366
1111123456
1111123555
1111124446
1111124455
111112666
1111133356
1111133446
1111133455
1111134445
111113566
1111144444
111114466
111114556
111115555
1111222366
1111222456
1111222555
1111223356
1111223446
1111223455
1111224445
111122566
1111233346
1111233355
1111233445
1111234444
111123466
111123556
111124456
111124555
1111333336
1111333345
1111333444
111133366
111133456
111133555
111134446
111134455
11113666
111144445
11114566
11115556
1112222266
1112222356
1112222446
1112222455
1112223346
1112223355
1112223445
1112224444
111222466
111222556
1112233336
1112233345
1112233444
111223366
111223456
111223555
111224446
111224455
11122666
1112333335
1112333344
111233356
111233446
111233455
111234445
11123566
111244444
11124466
11124556
11125555
1113333334
111333346
111333355
111333445
111334444
11133466
11133556
11134456
11134555
11144446
11144455
1114666
1115566
1122222256
1122222346
1122222355
1122222445
1122223336
1122223345
1122223444
112222366
112222456
112222555
1122233335
1122233344
112223356
112223446
112223455
112224445
11222566
1122333334
112233346
112233355
112233445
112234444
11223466
11223556
11224456
11224555
1123333333
112333336
112333345
112333444
11233366
11233456
11233555
11234446
11234455
1123666
11244445
1124566
1125556
113333335
113333344
11333356
11333446
11333455
11334445
1133566
11344444
1134466
1134556
1135555
1144456
1144555
115666
1222222246
1222222255
1222222336
1222222345
1222222444
122222266
1222223335
1222223344
122222356
122222446
122222455
1222233334
122223346
122223355
122223445
122224444
12222466
12222556
1222333333
122233336
122233345
122233444
12223366
12223456
12223555
12224446
12224455
1222666
122333335
122333344
12233356
12233446
12233455
12234445
1223566
12244444
1224466
1224556
1225555
123333334
12333346
12333355
12333445
12334444
1233466
1233556
1234456
1234555
1244446
1244455
124666
125566
133333333
13333336
13333345
13333444
1333366
1333456
1333555
1334446
1334455
133666
1344445
134566
135556
1444444
144466
144556
145555
16666
2222222236
2222222245
2222222335
2222222344
222222256
2222223334
222222346
222222355
222222445
2222233333
222223336
222223345
222223444
22222366
22222456
22222555
222233335
222233344
22223356
22223446
22223455
22224445
2222566
222333334
22233346
22233355
22233445
22234444
2223466
2223556
2224456
2224555
223333333
22333336
22333345
22333444
2233366
2233456
2233555
2234446
2234455
223666
2244445
224566
225556
23333335
23333344
2333356
2333446
2333455
2334445
233566
2344444
234466
234556
235555
244456
244555
25666
33333334
3333346
3333355
3333445
3334444
333466
333556
334456
334555
344446
344455
34666
35566
444445
44566
45556
55555


Only 294. However, this is only half the story. We are more interested in the classifications, and ways that we can locate the points taken shape in each of them. I will not bore the reader with detailed arguments, will show only the script and the final result of his work:

Script
my @d;
my %s;

sub out {
my ($deep) = @_;
for (my $i = 0; $i < $deep; $i++) {
print "$d[$i]";
}
print "\n";
}

sub proc {
my ($x, $r, $m) = @_;
if ($x == 0) {
$s{$r}++;
} else {
my $n = $x % 10;
for (my $i = 0; $i < $n; $i++) {
proc(int($x / 10), $r + $i * $m, $m * 10);
}
}
}

sub alloc {
my ($x, $deep, $res) = @_;
if ($x == 0) {
proc($res, 0, 1);
} else {
my $vl = 6;
for (my $i = 0; $i < $deep; $i++) {
if ($d[$i] < $vl) {
$vl = $d[$i];
}
}
if ($vl < 6) {
my $cn = 0;
my $ix = 0;
for (my $i = 0; $i < $deep; $i++) {
if ($d[$i] == $vl) {
$cn++;
$ix = $i;
}
}
my $y = $d[$ix]; $d[$ix] = 6;
$x -= 6 - $vl;
if ($x < 0) {
$x = 0;
}
alloc($x, $deep, $res * 10 + $cn);
$d[$ix] = $y;
}
}
}

sub dice {
my ($start, $deep, $sum) = @_;
if ($sum == 25) {
for (my $i = 0; $i < $deep; $i++) {
my $x = $d[$i]; $d[$i] = 6;
alloc($x, $deep, 0);
$d[$i] = $x;
}
}
if ($deep < 10 && $sum < 25) {
for (my $i = $start; $i <= 6; $i++) {
$d[$deep] = $i;
dice($i, $deep + 1, $sum + $i);
}
}
}

dice(1, 0, 0);

my $all;

foreach my $k (sort { $s{$a} <=> $s{$b} } keys %s) {
$all += $s{$k};
print "$k\t=> $s{$k}\n";
}

print "\n$all\n";

Result
102 => 1
331 => 1
200 => 1
...
22 => 93
5 => 106
21 => 152
20 => 152
11 => 152
10 => 220
4 => 259
3 => 584
2 => 1061
1 => 1677
0 => 2407

7954


On the left — a chain of numbers that control the order of the distribution points taken. For example, "20" means that we are starting the distribution with the first shape (we start their counting with 0), then distributed in a third of the remaining figures with the minimum value. Obviously, such a distribution pattern is only possible for the hands with at least four "minimum" figures, such as "3333445" (and to be distributed so it will only "four" or "five"). The result of the script shows that dispensing points, each time into the first a "minimum" figure, we will cover 30% (2407/7954) of all possible situations, and using only three of the apportionment, more than 64%!
Specially for such cases, ZoG provides an interesting interface possibility. Performing a move, the player specifies two fields: start and end. In that case, if there are several different possible moves, connecting the selected pair of fields, the player is given the choice (pop-up menu). The simplest example is the transformation of a pawn in Chess. When he reached the last horizontal, the pawn can turn into any of the shapes (from the elephant to the Queen) and the choice should be made by the player. This is the option I decided to use it.

Hansel and Gretel!
the idea is simple — to ensure that the core of ZoG considered the moves are different enough to have different ZSG-performance. Simply put, these moves should do different things. To achieve this is not difficult, you need only to manage which of the figures will be added points. The fact that the number of pieces (per side) may not exceed 10 allows you to use the convenient decimal system. We've seen these numbers in the previous frame. Each individual figure means the figure (from scratch) which will be added points. After each use, the number is cut off one decimal place. In the end, it remains 0, meaning use the first shape.

some More code
VARIABLE alloc-path
VARIABLE alloc-val
VARIABLE alloc-target
VARIABLE alloc-pos

: alloc-to ( pos -- )
DUP add-pos
DUP val-6 at the SWAP
DUP alloc-val @ > IF
DROP alloc-val @
0 alloc-val !
ELSE
alloc-val @ OVER - alloc-val !
ENDIF
my-next-player ROT ROT
OVER piece-type-at + SWAP
create-player-piece-type-at
;

: alloc ( -- )
6 0 BEGIN
DUP enemy-at? OVER not-in-pos? AND IF
SWAP OVER val at MIN SWAP
ENDIF
1+ DUP A9 >
UNTIL DROP
DUP 6 < IF
alloc-target !
alloc-path @ 10 alloc MOD-pos !
0 BEGIN
DUP enemy-at? OVER not-in-pos? AND IF
DUP val-alloc at-target @ = IF
alloc-pos @ 0= IF
DUP alloc-to
0 alloc-target !
DROP A9
ELSE
alloc-pos --
ENDIF
ENDIF
ENDIF
1+ DUP A9 >
UNTIL DROP
alloc-target @ 0= verify
alloc-val @ 0> IF
alloc-path @ 10 / alloc-path !
RECURSE
ENDIF
ELSE
DROP
ENDIF
;

: alloc-all ( -- )
0 pos-count !
here add-pos
alloc
;


Variable alloc-path contains our sequence of "breadcrumbs". Of course, it would be absolutely wasteful to identify in the code all 105 possible control sequences, but we have found that they are not equivalent. Most of them will be used rarely, and only 4 of them will cover most of the possible cases. Unfortunately, even this exploit failed:

Bread crumbs
: eat ( 'dir n -- )
LITE-VERSION IF NOT
check-pass
check-neg
ENDIF
+ alloc-path !
val SWAP BEGIN
step
SWAP 1 - DUP 0= IF
TRUE
ELSE
my-empty? verify
SWAP FALSE
ENDIF
UNTIL DROP
from here move
LITE-VERSION NOT enemy? AND IF
from piece-type-at mark - ABS
mark SWAP - create-piece-type
ENDIF
bump DROP
here E5 <> verify
enemy? verify
LITE-VERSION IF NOT
clear-neg
set-pass
ENDIF
+ val alloc-val !
+ alloc-all
add-move
;

: eat-nw-0 ( -- ) ['] nw 0 eat ;
: eat-sw-0 ( -- ) ['] 0 sw eat ;
: eat-ne-0 ( -- ) ['] ne 0 eat ;
: eat-se-0 ( -- ) ['] 0 se eat ;
: eat-w-0 ( -- ) ['] w 0 eat ;
: eat-e-0 ( -- ) ['] e 0 eat ;

: eat-nw-1 ( -- ) ['] nw 1 eat ;
: eat-sw-1 ( -- ) ['] 1 sw eat ;
: eat-ne-1 ( -- ) ['] ne 1 eat ;
: eat-se-1 ( -- ) ['] se 1 eat ;
: eat-w-1 ( -- ) ['] w 1 eat ;
: eat-e-1 ( -- ) ['] e 1 eat ;

: eat-nw-2 ( -- ) ['] nw 2 eat ;
: eat-sw-2 ( -- ) ['] 2 sw eat ;
: eat-ne-2 ( -- ) ['] ne 2 eat ;
: eat-se-2 ( -- ) ['] se 2 eat ;
: eat-w-2 ( -- ) ['] w 2 eat ;
: eat-e-2 ( -- ) ['] e 2 eat ;

: eat-nw-3 ( -- ) ['] s 3 eat ;
: eat-sw-3 ( -- ) ['] e 3 eat ;
: eat-ne-3 ( -- ) ['] ne 3 eat ;
: eat-se-3 ( -- ) ['] se 3 eat ;
: eat-w-3 ( -- ) ['] w 3 eat ;
: eat-e-3 ( -- ) ['] e 3 eat ;

{moves p-moves
{move} split-nw-0 {move-type} normal-priority
{move} split-ne 0 {move-type} normal-priority
{move} split-sw-0 {move-type} normal-priority
{move} split-se-0 {move-type} normal-priority
{move} split-w-0 {move-type} normal-priority
{move} split-e-0 {move-type} normal-priority
{move} split-nw-1 {move-type} normal-priority
{move} split-ne-1 {move-type} normal-priority
{move} split-sw-1 {move-type} normal-priority
{move} split-se-1 {move-type} normal-priority
{move} split-w-1 {move-type} normal-priority
{move} split-e-1 {move-type} normal-priority
+ {move} eat-nw-0 {move-type} normal-priority
+ {move} eat-ne-0 {move-type} normal-priority
+ {move} eat-sw-0 {move-type} normal-priority
+ {move} eat-se-0 {move-type} normal-priority
+ {move} eat-w-0 {move-type} normal-priority
+ {move} eat-e-0 {move-type} normal-priority
+ {move} eat-nw-1 {move-type} normal-priority
+ {move} eat-ne-1 {move-type} normal-priority
+ {move} eat-sw-1 {move-type} normal-priority
+ {move} eat-se-1 {move-type} normal-priority

+ {move} eat-e-1 {move-type} normal-priority
+ {move} eat-nw-2 {move-type} normal-priority
+ {move} eat-ne-2 {move-type} normal-priority
+ {move} eat-sw-2 {move-type} normal-priority
+ {move} eat-se-2 {move-type} normal-priority
+ {move} eat-w-2 {move-type} normal-priority
+ {move} eat-e-2 {move-type} normal-priority
+ {move} eat-nw-3 {move-type} normal-priority
+ {move} eat-ne-3 {move-type} normal-priority
+ {move} eat-sw-3 {move-type} normal-priority
+ {move} eat-se-3 {move-type} normal-priority
+ {move} eat-w-3 {move-type} normal-priority
+ {move} eat-e-3 {move-type} normal-priority
{move} slide-nw {move-type} normal-priority
{move} slide-ne {move-type} normal-priority
{move} slide-sw {move-type} normal-priority
{move} slide-se {move-type} normal-priority
{move} slide-w {move-type} normal-priority
{move} slide-e {move-type} normal-priority
-( {move} exchange-1-nw {move-type} normal-priority
- {move} exchange-1-ne {move-type} normal-priority
- {move} exchange-1-sw {move-type} normal-priority
- {move} exchange-1-se {move-type} normal-priority
- {move} exchange-1-w {move-type} normal-priority
- {move} exchange-1-e {move-type} normal-priority
- {move} exchange-2-nw {move-type} normal-priority
- {move} exchange-2-ne {move-type} normal-priority
- {move} exchange-2-sw {move-type} normal-priority
- {move} exchange-2-se {move-type} normal-priority
- {move} exchange-2-w {move-type} normal-priority
- {move} exchange-2-e {move-type} normal-priority
- {move} exchange-3-nw {move-type} normal-priority
- {move} exchange-3-ne {move-type} normal-priority
- {move} exchange-3-sw {move-type} normal-priority
- {move} exchange-3-se {move-type} normal-priority
- {move} exchange-3-w {move-type} normal-priority
- {move} exchange-3-e {move-type} normal-priority
- {move} exchange-4-nw {move-type} normal-priority
- {move} exchange-4-ne {move-type} normal-priority
- {move} exchange-4-sw {move-type} normal-priority
- {move} exchange-4-se {move-type} normal-priority
- {move} exchange-4-w {move-type} normal-priority
- {move} exchange-4-e {move-type} normal-priority
- {move} exchange-5-nw {move-type} normal-priority
- {move} exchange-5-ne {move-type} normal-priority
- {move} exchange-5-sw {move-type} normal-priority
- {move} exchange-5-se {move-type} normal-priority
- {move} exchange-5-w {move-type} normal-priority
- {move} exchange-5-e {move-type} normal-priority )
moves}


Apparently, in Axiom has a limit on the number of defined moves (not covered in documentation). How do I define it? Very simple! When I add in the code all the definitions, the program krasitsja at the start. If I take off some of the definitions (e.g. exchangemoves), everything works fine. Unfortunately, the idea of variability of the distribution points had to be abandoned.

Strictly speaking, this is not quite correct solution. According to the rules "chase", the distribution of points should not the player who performed the move, and his opponent. I don't have the slightest idea about how this can be achieved using ZoG, but there is a very easy workaround. The ZoG interface provides a convenient front-end editing capability of the Board. Using command pop-up menu, the player can remove any figure on the Board or create another. This ability is indispensable when debugging and I use it a lot. In General, the player who did not like the automatic assignment of points, can easily redeploy them manually (for the next move, while not broken). Please observe a minimum of caution. In the process of editing should not allow the situation when one of the players has less than 5 figures, as in this case, he will be immediately forfeited and the game will be stopped.

... count to one!


Because the idea of "variable" distribution of points eaten failed, I went back to the development of the game by ZRF. Axiom-implementation, in principle, also worked, but she was still missing, AI (the regular ZoG-ovsky an Axiom not able to use). In General, this problem is reduced to the correct encoding of the evaluation function (for aesthetes there is the "Custom Engine"), but it is not very easy! In any case, the standard evaluation function that takes into account the mobility and material balance, at chase proved to be not the best way.

some details
the Evaluation function I'm talking about looks like this:
the
: OnEvaluate ( -- score ) 
mobility
current-player material-balance KOEFF * +
;

The most cunning beast here mobility. In fact, is the number of all possible moves of the player from which is subtracted the number of all possible moves of the opponent. All the moves of the player, at the time of evaluation of the position, already generated is to calculate them is not difficult, but to Article based on information from habrahabr.ru

Комментарии

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

The use of Lisp in production

FreeBSD + PostgreSQL: tuning the database server

As we did a free Noodle for iOS and how we plan to earn