Programming "for the soul"

Polno, pigeon, sin no more!
Get your pennies.
I did it not for money.
I did it for the soul.

Leonid Filatov "the Tale about Fedot-Archer, swashbuckling young man"

Just for Fun.

Linus Torvalds


It is no secret that people get pleasure in different ways. One likes to watch TV, others collect quadcopters. I want to share my recipe. It is unlikely he will be useful to anyone, but maybe someone interested. I like to write programs (and I think that it is not uncommon, even among professional programmers), but I don't really like it when the process turns into a dull routine.

To be interesting, programming should be a kind of "charging for the mind". A good example of this (useful) entertainment is known to many resource, dedicated to the improvement of programming skills in SQL queries. But not SQL-eat one alive programmer! Recently, I found a great way to improve your skills Fortom. Axiom allows you to zaprogrammirovat on Forte plenty!

My recipe for Fun-and, with the help of Axiom, is simple:

    the
  1. Select any the game, with the rules tricky, the number of not yet implemented ZoG community
  2. the
  3. are Trying to implement using Axiom
  4. the
  5. Get pleasure, in the process of solving the arising tasks
  6. the
  7. In case the app is fun to play, Fun developed is automatically doubled!


With the implementation of the first paragraph of this plan to me, it usually helps the Internet. This time, the object of their inhuman experiments, I chose Splut!. Here's his description IG Game Center. Without going into a retelling of the rules, I will try to explain what drew me to this game:

the
    the
  • play more than two players (that is, to a certain extent, a challenge for AI algorithms)
  • the
  • player's turn involves sequential movement of several (1 to 3) pieces
  • the
  • Moves that lead to winning, not linear (you can't just eat a piece, you want to perform a sequence of moves with the same objective)
  • the
  • Rules of this game is very thought out and very original

Remark
that's what the author writes about the rights to your game:

The SPLUT game idea and design are copyrighted. You cannot use any of the ideas or contents of this publication for commercial purposes without written authorization of the designer Tommy De Coninck.

Since I am not going to use the idea or design of the game for commercial purposes, this item is all right.

Magic exposing


Begin to develop a Fun and. Let's start with the simple moves of the Troll. Usual course the figures do not represent any difficulties. Its implementation is obvious and well-suited for explaining concepts of Axiom:

Quiet running
: one-step ( 'dir -- )
EXECUTE verify \ Step forward
empty? verify \ Empty?
from \ From the source point
here \ Here
move \ Walk
add-move \ Move completed
;


Just want to advise you to pay attention to the comments (in parentheses). They will help not to get lost in what is happening on the stack (this is really important in Forte). Also pay attention to the gaps. Gap, not put in the wrong place can make you spend a lot of time.

According to the code, I think everything is clear. We perform the transition in the direction (taken from the stack), the command EXECUTE, and then check the Boolean result of the transition (if not TRUE, completing the calculation speed). Then, we perform the verification that the target cell is empty, then moved the figure. The command move performing the move, stack two values — the start point of the stroke (from) and the position in which we find ourselves, after moving (here). The command add-move completes a turn.
A slightly more complicated move, with the movement of stone:

Drag stone
: drag ( 'dir 'opposite -- )
EXECUTE verify \ Step back
is-stone? verify \ That stone?
piece-type \ twist twirl
SWAP here SWAP \ want to Confuse
DROP DUP EXECUTE EXECUTE verify \ twice step forward
empty? verify \ Empty?
from \ From the source point
here \ Here
move \ move a shape
capture-at \ removing the Stone from the position previously stored
from create-piece-type-at \ And create it where we started the course
add-move \ that Is all!
;

: drag-to-north ( -- ) ['] north ['] south drag ;
: drag-to-south ( -- ) ['] south ['] north drag ;
: drag-to-east ( -- ) ['] east ['] west drag ;
: drag-to-west ( -- ) ['] west ['] east drag ;


Here we put on the stack from two directions — the direction of travel and opposite to it. The code looks more complicated because of manipulations with the stack, but you get used to it. It is very important that all "adverse" actions to seize or creating shapes must be executed after move the basic shape. It is also important to remember that the order in which rests on the stack after each command. A detailed description of these commands, you can always look in the manual for Axiom.

On one point, however, should stay apart. Check that the figure in the current cell is a Stone, is performed by a predicate is-stone?. Of course, this is not a built in function Axiom, and our. Here is its implementation:

Stone?
DEFER SSTONE
DEFER NSTONE
DEFER WSTONE
DEFER ESTONE

: is-stone? ( -- ? )
piece-type SSTONE =
piece-type NSTONE = OR
piece-type WSTONE = OR
piece-type ESTONE = OR
;

...
{pieces
{piece} lock {moves} pass-moves
{piece} sstone {drops} stone-drops
{piece} nstone {drops} stone-drops
{piece} wstone {drops} stone-drops
{piece} estone {drops} stone-drops
{piece} wizard {moves} wizard-moves
{piece} dwarf {moves} dwarf-moves
{piece} troll {moves} troll-moves
pieces}

'sstone SSTONE IS
'nstone NSTONE IS
'wstone IS WSTONE
'estone ESTONE IS


Remember last article, I lamented the fact that you cannot use object names (in this case shapes) until you define them? DEFER allows you to cope with this problem. Only bad thing is that this critical pattern not described in the documentation for Axiom.

But why do we have four types of stone? Can't you do one? Alas, the rules Splut! made in such a way that we can't do without "individuality" of stones. I will show later why this is necessary.

Now the Troll can move and (optionally) to carry a Stone, but it looks like we forgot something. The fact that the only way natural loss figures in Splut! is that Trolls will throw stones at them! We will add the missing functionality, gathering the code together:

Moves the Trolls
DEFER CONTINUE-TYPE

: one-step ( 'dir -- )
check continue? IF
EXECUTE verify
empty? verify
from
here
move
add-move
ELSE
DROP
ENDIF
;

: step-to-north ( -- ) ['] north one-step ;
: step-to-south ( -- ) ['] south one-step ;
: step-to-east ( -- ) ['] east one-step ;
: step-to-west ( -- ) ['] west one step ;

: drag ( 'dir 'opposite -- )
check continue? IF
EXECUTE verify
is-stone? verify
piece-type
SWAP here SWAP
DROP DUP EXECUTE EXECUTE verify
empty? verify
from
here
move
capture-at
DUP lock-stone
from create-piece-type-at
add-move
ELSE
DROP DROP
ENDIF
;

: drag-to-north ( -- ) ['] north ['] south drag ;
: drag-to-south ( -- ) ['] south ['] north drag ;
: drag-to-east ( -- ) ['] east ['] west drag ;
: drag-to-west ( -- ) ['] west ['] east drag ;

take-stone ( 'dir -- )
check continue? IF
EXECUTE verify
is-stone? verify
CONTINUE-TYPE partial-move-type
from
here
move
add-move
ELSE
DROP
ENDIF
;

: take-to-north ( -- ) ['] north take-stone ;
: take-to-south ( -- ) ['] south take-stone ;
: take-to-east ( -- ) ['] east take-stone ;
: take-to-west ( -- ) ['] west take-stone ;

: drop-stone ( 'opposite 'dir -- )
check edge? check wizard? OR 
on-board? AND IF
check troll? piece-is-not-present? AND IF
player-piece-type
drop

drop-team
ELSE
DROP
ENDIF
lock-continue
current-piece-type lock-stone
add-move
ENDIF
ENDIF
;

: drop-to-north ( -- ) ['] north ['] south drop-stone ;
: drop-to-south ( -- ) ['] south ['] north drop-stone ;
: drop-to-east ( -- ) ['] east ['] west drop-stone ;
: drop-to-west ( -- ) ['] west ['] east drop-stone ;

{troll moves-moves
{move} step-to-north {move-type} normal-type
{move} step-to-south {move-type} normal-type
{move} step-to-east {move-type} normal-type
{move} step-to-west {move-type} normal-type
{move} drag-to-north {move-type} normal-type
{move} drag-to-south {move-type} normal-type
{move} drag-to-east {move-type} normal-type
{move} drag-to-west {move-type} normal-type
{move} take-to-north {move-type} normal-type
{move} take-to-south {move-type} normal-type
{move} take-to-east {move-type} normal-type
{move} take-to-west {move-type} normal-type
moves}

{moves stone-drops
{move} drop-to-north {move-type} continue-type
{move} drop-to-south {move-type} continue-type
{move} drop-to-east {move-type} continue-type
{move} drop-to-west {move-type} continue-type
moves}

'continue-CONTINUE type IS-TYPE


I will not describe auxiliary functions. Their implementation can be viewed here. Will focus only on the throws. The Troll can take the stone course take-stone (implementation of this function is trivial), then the partial-move-type includes the continuation of the operation, with the given type (continue-type). Under this type of was the only move type — cast (drop) of Stone on the Board.

You can quit not somehow, and in strictly certain places! According to the rules, the stone flies from the Troll in a straight line (vertical or horizontal), flying over the head of the Dwarves, to the obstacle (the Magician, the edge of the Board or any other Troll). Magician pricebuy immediately, in other cases, falls on the Board. If this place turned out to be a Dwarf — he was just unlucky. It is difficult to implement the rule and will be easier to put it into practice, starting from the other end. We seek a field bordering the obstacle and move them in the opposite direction, the empty cells or cells occupied by the Dwarves. If the road will meet his Troll, so at that place from which we started, you can throw a stone.

In addition, the code implemented accompanying regulations. For example the fact that the murder of the Magician, is removed from the field his entire team, and also the fact that after throwing the stone, the turn immediately passes to another player. I won't elaborate on that here.

A puzzle of a different kind is the special move of Gnome. Dwarf, on their own, can move any number of pieces (including Stones) in front of him in a row. To store all these figures, we clearly need a stack. For everything else you can use variables:

Gnome
VARIABLE forward
VARIABLE backward
VARIABLE step count
VARIABLE here pos

: push-step ( 'opposite 'dir -- )
check continue? IF
0 step-count ! forward ! backward !
forward @ EXECUTE verify not-empty? verify
step-count ++
player-piece-type
here here-pos !
BEGIN
forward @ EXECUTE IF
empty? IF
TRUE
ELSE
step-count ++
player-piece-type
FALSE
ENDIF
ELSE
BEGIN
step-count @ 0 > IF
step-count --
DROP DROP
FALSE
ELSE
TRUE
ENDIF
UNTIL
TRUE
ENDIF
UNTIL
step-count @ 0> verify
from here-pos @ move
BEGIN
step-count @ 0 > IF
step-count --
DUP is-stone-type? IF
DUP lock-stone
ENDIF
create-player-piece-type
backward @ EXECUTE DROP
FALSE
ELSE
TRUE
ENDIF 
UNTIL
add-move
ELSE
DROP DROP
ENDIF
;


Yes, to understand this more complicated than in the previous code, but the essence is the action being performed is simple. We are moving in the same direction, folding the shapes in the stack to empty cells, and then return, recreating them on the Board pushed one step (since a single cell cannot be more than one shape to delete the shapes you don't care — ZoG you delete them). Try to understand how this code is good "gymnastics for the mind".
Of course, the Magi weren't Magicians, if not delivered us the most trouble. Mages can levitate stones. Any, but... under certain conditions. For example, you cannot levitate a rock that moved (in any way) in the previous course. Here the question immediately arises: what is considered previous? Unfortunately, the rules do not transcribe this moment. In my code I implemented the clearing of signs of displacement of the stones (it is here that need a personality, each stone its own flag) immediately before passing the turn to the first player. Of course, this gives him a serious advantage (it can move any Stones, and the following players are the only ones that haven't moved it), but other possible implementations of this rule also is not perfect.

Levitated Stones
: fly-stone ( 'dir -- )
check continue? IF
DUP EXECUTE empty? AND IF
a5 to
BEGIN
is-stone? not-locked? AND IF
here here-pos !
DUP piece-type SWAP
EXECUTE SWAP
can-fly? AND IF
from to
DROP DUP EXECUTE
from
here
move
here-pos @ to
DUP piece-type capture SWAP
EXECUTE DROP
DUP lock-stone
DUP begin-fly
create-piece-type
add-move
ENDIF
here-pos @ to
ENDIF
Next DUP NOT
UNTIL
ENDIF
DROP
ELSE
DROP
ENDIF
;


It is easy to make a mistake, arguing that we have implemented all the necessary. Implemented but not all features! Whether the Magician to move to the square occupied by the Stone if beyond the Stone were placed an empty cage? The rules of the game say Yes, but the code says otherwise. In fact, the Magician can still "push" the Stones in front of him. It's just kind of levitation!

Push Stones in front of him
: push stone ( 'dir -- )
check continue? IF
DUP EXECUTE is-stone? not-locked? AND AND IF
piece-type can-fly-lock? IF
here SWAP
piece-type SWAP
EXECUTE empty? AND IF
SWAP from SWAP move
DUP lock-stone
DUP begin-fly
create-piece-type
add-move
ELSE
DROP DROP DROP
ENDIF
ELSE
DROP
ENDIF
ENDIF
ELSE
DROP
ENDIF
;


This code is simpler because it does not have to look for Rocks all over the field. If we want to stand on a square occupied by a Stone, the only Stone that you can levitate — he is.

A and B were sitting on the tube


As I said above, the implementation of AI, for games involving more than two players, poses some difficulties. The problems begin when determining to end the game. For example, I developed a newly implementation of the game Yonin Shogi (Japanese chess variant for 4 people), it would be tempting to determine the condition of lesion as follows:

the
(loss-condition (South North West East) (checkmated King))

This entry means that the game should be carried out before the King any of the players. Alas, this approach won't work! I already wrote that the team checkmated, carries too much "magic". In particular, it specifies that the King must always go from under the Shah (and never to stand under the Shah). Overall, it works... until the game is played by two players. The video illustrates the problem:



As you can see, checkmated is working fine for only one of the 4 players. For other players, protection of the Shah is not considered mandatory! Of course, next turn, king of the perhaps eat, but this fact only exacerbates the situation. Anyway, normal Mata in a game like this put will fail.

In Splut! the situation is even worse. The game should be carried out until, until there will be only one team. But ZoG does not allow you to change the list sequence of moves during the game! This means that every eliminated team should be doing when her turn comes (of course it will pass, because no other opportunity to make a move there). In addition, Splut! each team makes a few moves in a row (1-2 early in the game and 3 in the middle of the party). In General, for me was not a surprise the fact that the standard Axiom AI lost this game.
It certainly works, the program makes the moves (rather silly in my opinion), but, after the elimination of one of the players the problem starts. When calculating each pass of the course, the program starts to "think" longer and longer, not lying down in any of the specified time frames. The assignment of its evaluation function (OnEvaluate) does not fix the situation. In General, I considered this a sufficient reason to try to implement your own algorithm AI, the benefit of the Axiom that option available (I'll tell you what happened is not very good, but try it was worth.)

For the background I have the following, known to many, the algorithm from the book Evgeny Kornilov "Programming of Chess and other logical games":

Alpha-Beta cut off with shock absorption bounce
int AlphaBeta(int color, int Depth, int alpha, int beta)
{
if (Depth == 0) return Evaluate(color);
int score = -INFINITY;
PMove move = GenerateAllMoves(color);

while (move)
{
MakeMove(move);
int tmp = -AlphaBeta(color==WHITE?BLACK:WHITE, Depth - 1, -beta, -alpha);
UnMakeMove(move);
if (tmp > score) score = tmp;
if (score > alpha) alpha = score;
if (alpha >= beta) return alpha;
move = move- > next;
}
return score;
}


As can be seen, in its original form, this algorithm is not suitable for us. We have more than two players, and with alternating moves much more difficult. But this algorithm is a good starting point for developing your own version.

After thinking a bit, you can see that in the worst case, three players, opposing that for which we hope the course will combine their efforts. In other words, for us it is a single hostile to the player (if they do not unite — so much the worse for them). Another important point is the calculation of the evaluation function. When calculating speed, the evaluation function must always be calculated "from the point of view of" one and the same player (the one that is calculated). For hostile players, the rating should be taken with the opposite sign (better than us — so they are worse). Taking into account these considerations, we can rewrite the algorithm as follows:

Generic Alpha-Beta cut off
VARIABLE Depth

MaxDepth [] CurrMove[]
MaxDepth [] CurrTurn[]
MaxDepth [] CurrScore[]

: Score ( alpha beta turn -- score )
Depth -- Depth @
0< IF
EvalCount ++
SWAP DROP SWAP DROP
Eval
SWAP turn-offset-to-player
current-player <> IF
NEGATE
ENDIF
ELSE
The DUP turn-offset-to-player FALSE 0 $GenerateMoves 
Depth @ CurrTurn[] !
$FirstMove Depth @ CurrMove[] !
-10000 Depth @ CurrScore[] !
BEGIN
$CloneBoard
Depth @ CurrMove[] @
.moveCFA EXECUTE
2DUP
Depth @ CurrTurn[] @ next-turn-offset
RECURSE
$DeallocateBoard
$Yield
Depth DUP @ CurrScore[] @ > IF
Depth @ CurrScore[] !
ELSE
DROP
ENDIF
Depth @ CurrTurn[] @ turn-offset-to-player
current-player <> IF
SWAP NEGATE NEGATE
ENDIF
OVER Depth @ CurrScore[] @ < IF
SWAP DROP
Depth @ CurrScore[] @
SWAP
ENDIF
2DUP > = IF
OVER Depth @ CurrScore[] !
TRUE
ELSE
Depth @ CurrTurn[] @ turn-offset-to-player
current-player <> IF
SWAP NEGATE NEGATE
ENDIF
Depth @ CurrMove[] @
$NextMove
Depth DUP @ CurrMove[] !
NOT
ENDIF
UNTIL
$DeallocateMoves
DROP DROP
Depth @ CurrScore[] @
Depth @ CurrTurn[] @ turn-offset-to-player
current-player <> IF
NEGATE
ENDIF
ENDIF
Depth ++
;


There are a lot "magic" of the Fort and the Axioms associated with the generation of moves and positions, but, at a certain voltage, the source algorithm is quite visible. For unloading of the stack had to simulate multiple stacks, with the variables used in recursive calls. In fact the stack during the calculation, are just two values of alpha and beta. In the recursive calls (RECURSE), they are always transmitted in the same order, but if the calculation is hostile to the player, we change their sign, and then change these values in some places. Also we change the sign of the estimates obtained when estimating the position of a hostile player.
This function is called from the familiar, in the last article, the implementation of Custom Engine:

Custom Engine
MaxDepth 3 CONSTANT

VARIABLE BestScore
VARIABLE Nodes

: Custom-Engine ( -- )
-10000 BestScore !
0 Nodes !
$FirstMove
BEGIN
$CloneBoard
DUP $MoveString 
CurrentMove!
DUP .moveCFA EXECUTE
MaxDepth Depth !
0 EvalCount !
BestScore @ 10000 turn-offset next-turn-offset Score
0 5 $RAND-WITHIN +
BestScore @ OVER <
IF
DUP BestScore !
Score!
0 Depth!
DUP $MoveString BestMove!
ELSE
DROP
ENDIF
$DeallocateBoard
Nodes ++
Nodes @ Nodes!
$Yield
$NextMove
DUP NOT
UNTIL
DROP
;


You may notice that in this code we add to the appraisal value of a random number from 1 to 5. This is done to ensure that the program did not go always the same in cases when the assessment of the moves slightly different.

As usual, the main difficulty is to build the evaluation function. I'm not going to load the article with listing of the current version (you can always view the code on GitHub), I can only say that it, currently, takes into account the following points:

the
    the
  • the Number of enemy Mages (our main goal is to decrease this value)
  • the
  • the Number of friendly Mages (if this value changes from 1 to 0, the game will end)
  • the
  • the Number of enemy Dwarves (always nice to bind the enemy hand)
  • the
  • the Number of friendly Dwarves (not that we are without him was not, but its shape still)
  • the Penalty for being a friendly Mage on the same line with stones (it's really dangerous) the

  • Bonuses for finding enemy Magicians on the same lines with Stones (for the same reason)
  • the
  • Total number of steps from the Trolls to Stones (try to reduce their and increase for others)

It's certainly not ideal. Weight values should be chosen more optimally, and the fact, for example, finding a Mage on the same line with the Stone, by itself, says nothing. Line of the throw can be blocked, like the Troll, the stone must still be reached in order to throw it. Anyway, we wrote the code and can see how it works:



As expected, the AI does not possess the intelligence (and works terribly slow), but at least trying to "appear intelligent". At least, it is already possible to play.

Counted — to tears


Of course, to assess the quality of the AI, you can play with him many times and build a "peer rating", but it is not our method. Complete with Axiom AutoPlay comes a wonderful tool that can automate this process. Unfortunately, it is not able to work with games designed for more than 2 players, but it's not a problem. She will create a configuration with two players (stones let's leave 4 pieces):

Duel
LOAD Splut.4th ( Load the base game Splut )

{players
{player} South {search-engine} Custom-Engine
{neutral} West
{player} North {search-engine} Custom-Engine
{neutral} East
{player} ?Cleaner {random}
players}

{turn-order
{turn} South
{turn} North
{turn} North
{repeat}
{turn} ?Cleaner {of-type} clear-type
{turn} South
{turn} South
{turn} South
{turn} North
{turn} North
{turn} North
turn-order}

{board-setup
{setup} South sstone e1
{setup} South wizard d2
{setup} South dwarf e2
{setup} South troll f2
{setup} South lock f1

{setup} West wstone a5

{setup} North nstone e9
{setup} North wizard f8
{setup} North dwarf e8
{setup} North troll d8
{setup} North lock h1

{setup} East estone i5
board-setup}


Also, we need a configuration in which the players make random moves:

Random
LOAD Splut.4th ( Load the base game Splut )

{players
{player} South {random}
{neutral} West
{player} North {random}
{neutral} East
{player} ?Cleaner {random}
players}

{turn-order
{turn} South
{turn} North
{turn} North
{repeat}
{turn} ?Cleaner {of-type} clear-type
{turn} South
{turn} South
{turn} South
{turn} North
{turn} North
{turn} North
turn-order}

{board-setup
{setup} South sstone e1
{setup} South wizard d2
{setup} South dwarf e2
{setup} South troll f2
{setup} South lock f1

{setup} West wstone a5

{setup} North nstone e9
{setup} North wizard f8

{setup} North troll d8
{setup} North lock h1

{setup} East estone i5
board-setup}


The results were surprisingly good (although for the calculation of the 100 parties took a whole night):

the
Final results:
Player 1 "Random" wins = 13.
2 Player "Duel", wins = 87.
Draws = 0
100 game(s) played

Why the program works so long? Let's see how many times, when calculating the stroke, is called the evaluation function (data, calculate 5 moves deep):



Yes, 8000 calls to the evaluation function is certainly a lot, but why are there three rows? I'll try to explain. Here we consider the number of calls to Eval:

statistics Collection
$gameLog ON

VARIABLE EvalCount

: Score ( alpha beta turn -- score )
Depth -- Depth @
0< IF
EvalCount ++
...
ELSE
...
;

: Custom-Engine ( -- )
...
BEGIN
...
0 EvalCount !
BestScore @ 10000 turn-offset next-turn-offset Score
0 5 $RAND-WITHIN +
EvalCount @ . CR
...
UNTIL
DROP
CR
;


At the output, we get the following sequence:

Result
992
655
147

3749
22
1
22
22
22
22
1
1

336
132
50

382
42
213
35
392
21
62
40
49

1465
189
1
1
1
1
1
1
1
52
91

122
75
50

1509
2074
637
492
249
800
415
877
963

5608
90
4
4
4
4
4
4
4
4

Each group of numbers (separated by a blank line) contains the results display all the possible moves the player from the current position. In the chart above, the first row shows the minimum value in the group, the second average, the third — maximum. The difference between the maximum and minimum value is determined by the effectiveness of alpha-beta cut-off. The average value determines the performance that we can expect, given the depth of the bust.

You may notice that the numbers in the groups, mostly anything, but sometimes in violation of the monotone decreasing "spikes". Try to count the number of them:



Too much! In some groups more than 16 violations of a monotonous descending order. If it were possible to view the moves in a more optimal sequence, probably managed to improve the performance (and it is possible to achieve greater depth of search). Unfortunately, the following two points bother me to do it:

    the
  1. I Have no heuristics, allowing a preliminary assessment of the "quality" of moves in the game Splut!
  2. the
  3. Even if such heuristics were, preliminary assessment and sorting the list of moves in Axiom is associated with certain technical difficulties (and overhead)

Another method of increasing the depth of Robin could serve as "advanced" too much "forced" moves. Also, it would be nice to cut duplicate positions (this would greatly help Zobrist hashing).

Let's see how the number of viewed items, if you increase the depth too much:



Since the average waiting time of the completion of the moves of all opponents (at depth 5 moves) is about 1 minute, it is obvious that this is the maximum depth of the bust, you can count on, with the current implementation of the algorithm (any increase will make the game the person with the program it is not comfortable).

But let's think about what 5 moves in the game Splut? It's not enough to even calculate the possible moves of all the players! Even in Duel mode. It's like count the game of Chess in just 1 move ahead! It is difficult to expect much intelligence from such a program.

Of course Splut! much fewer pieces than Chess, but the moves more challenging! To win, the program should be able to make long-term plans many moves ahead. I don't know how to achieve this using Axiom, but probably as you can.

PS
I want to thank the developer of Axiom. Greg Schmidt — a real enthusiast computer design Board games. It supports code Axiom for almost 10 years, constantly improving it and adding something new. From the moment I put the Axiom-implementation of the game Ur in ZoG, he leads me lively correspondence, helping and explaining the intricacies of the Axiom. Just yesterday, with his help, I was able to detect and fix a very nasty bug in the implementation of the Ur. I am very grateful for the support!

P. P. S.
When making the articles used a fragment of the works of famous Russian artist, a comics artist Daniel Victor.

Article based on information from habrahabr.ru

Комментарии

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

Wikia Search — first impressions

Emulator data from GNSS receiver NMEA

mSearch: search + filter for MODX Revolution