All this is Queen Mab's rogue. She in the stables mane braids And knocks her hair with a pimple ...
William Shakespeare
It was a long release , but a lot was done. A session-manager appeared , allowing you to roll back erroneously made moves. Somewhere where sound design was added. And also, I came up with a cool way to push several alternative options for the initial placement into one game. And most importantly - I finally got to the games with incomplete information. I will explain what is at stake. In board games we are used to, such as Chess or Checkers , players, at any time of the game, have complete information about the location of the pieces (their own and the opponent), the rules of their movement, the goals of the game, etc. Such games are quite well studied and belong to the category of " games with complete information ." Now imagine that some of this information may be hidden from the player. ')
The Fog of War is a great illustration of the theme. According to the rules of " Chess in the Dark ", players can see not all the opponent's pieces, but only those that are placed on the fields, which can be reached with one move of any of their pieces. I made two additions to this rule:
Of course, the player always sees his pieces, but by the way they are displayed - in a normal or translucent form, he can judge whether the opponent sees them.
Solely for decorative purposes, I have placed “clouds” on areas that are currently invisible.
Having mastered the general principle , I got a little carried away and made a huge number of games with the "fog of war." In addition to Chess itself , I have “dark” options for Syantsy , Changi , Shatrange , Sittuyin and many other games. There are even " Guns in the dark "! All these games have one thing in common:
Computer cheats!
I did not even try to make changes to the algorithms of the bots for these games, because I relied on the fact that unequal conditions at least partially compensate for their extremely weak game compared to humans. As I wrote earlier, the development of high-quality AI for board games is a very difficult task. Of course, the rules have exceptions. Even with a very weak game of the bot, it will be difficult for a person to play an unfamiliar game , literally stuffed with traps. What can we say about her "dark" version
However, in general, this is not a very correct approach. I want to see a bot who knows how to manage exactly the data that is available to his opponent - a man. Why is it important? Everything is very simple - by the way a bot plays, sometimes it is very easy to guess whether it has access to hidden information (spies) or not. And, of course, it is much more interesting for a person to play with the bot that does not spy (it is even more interesting to play with another person, but this is a separate story).
And here it is worth picking up a game that is a little different from Chess (since I am not ready to engage in developing an "honest" Chess-playing bot "in the dark"). There are a lot of such games and it’s impossible to say that they are simpler than Chess or Checkers. They are just different and require an individual approach.
for example
There is one children's game, with the development of the bot for which I have not yet managed. It is called “Jungle” or Dou Shou Qi . The goal of the game is to penetrate enemy territory. Each of the players has a "lair" - the central field on the first line. If any of the enemy figures enters the lair, he won (it is impossible to occupy the lair with your own figures).
The figures are ordered by seniority. The elephant beats all the figures, followed by: Leo, Tiger, Leopard, Dog, Wolf, Cat and Rat. A rat can only beat an elephant and another rat, in addition, it is the only figure that can move in the water (there are two reservoirs in the middle of the board). A tiger and a lion can jump over water, but only if the rat does not block the path through the water. With the exception of jumps, all the figures move in the same way - one adjacent field vertically or horizontally. The lair is surrounded by traps. A trapped figure is vulnerable to any opponent piece.
As you can see, the rules are pretty simple. What prevents to develop a bot for this game? First of all - low speed figures. If there are threats, I can appreciate the benefits of exchanges, but most of the game pieces simply run after each other over fairly long distances. I cannot afford to watch the game for a large number of moves ahead (due to restrictions on the duration of the stroke calculation), as a result of which exchanges fall behind the viewing horizon and all the moves become equal for me.
To begin with, I decided to stop at BanQi - Chinese "Blind Chess". This is a very original game with hidden information, akin to "Jungle". For me it is important that the developments, in connection with the creation of a bot for this game, can be used in other games, such as Dou Shou Qi , Luzhan Qi , Stratego, or even (possibly) Tafl .
I'll tell you about the rules. The game takes place on the half board for "Chinese chess" ( Xiang Qi ), while the original layout of the board does not play any role. The figures are placed inside the cells (as in traditional), and not at the intersections of the lines (as in Chinese chess). At the beginning of the game, all the figures are carefully mixed and placed on the board face down (since the traditional figures of Xiangqi are sort of barrels, and their number coincides with the number of fields on the half of the board, this does not cause difficulties).
Next, the players alternate their moves. When making a move, the player can turn over any of the closed figures, or move the previously opened figure of his own color. The colors of the players are determined by the very first move. If the black piece is opened first, the player who opened it will play black. All the figures in the game go the same way (except for the “Guns” in the Taiwanese version, which I will talk about later) - one adjacent cell vertically or horizontally. The possibility of taking is determined by the order of precedence of the figures:
The senior figures beat the younger or equal to them, with one exception: the soldier beats the general (a kind of " Rock-Paper-Scissors "). It remains to say a few words about the Taiwan BanQi:
Unlike the Chinese version, in Taiwan BanQi, a general can not beat a soldier.
The gun moves according to the rules of XiangQi , that is, to any number of fields orthogonal in a quiet way (like a chariot) or hits any opponent's piece, with a jump through the "carriage", when performing an attacking turn.
There is also a Hong Kong version, but it is practically no different from the Chinese, except that the order of the precedence of the figures has been changed. I decided to focus on the Taiwanese version of the rules, as the most interesting, in tactical terms.
What should I look for when developing a bot?
Firstly, the game looks very simple, but it is not. Even if you do not consider the nuances associated with Taiwanese cannons, the cost of the figures is counterintuitive. Although the Counselor can beat fewer pieces than the General, he is the main character in the game. First, the player has two advisers. In addition, each adviser is outnumbered by only one enemy general, while the generals can attack as many as five soldiers! For the same reason, the cost of a soldier, in the game, is higher than the cost of a general. In the end, he can beat the strongest piece! The second important consideration is suggested by one of the “Canterbury” puzzles of Henry Dewdeni.
This is more a joke than a complete puzzle. All figures can walk on one adjacent field vertically or horizontally. White goes first, while White and Black always make two moves (in different pieces)! Under these conditions, the left jester will never be able to catch the left donkey, and the right jester will never be able to catch the right donkey (you can check it yourself). Of course, the right jester can catch the left donkey without any difficulty. It's all about parity!
This problem prompted me to some thoughts. First, the bot's task, in games such as BanQi or DouShouQi, is, above all, finding the shortest path. From each of the active figures (your own or your opponent) you need to build a chain of moves to all possible goals (including your own figures, to calculate possible exchanges). After this, the chains need to be evaluated and the following options are possible here.
The attacking figure hits the attacked - a profitable (bonus) chain estimated by the value of the attacked figure (minus the attacking figure, if the latter is under protection), taking into account the length of the chain.
The attacking figure beats the attacked - not advantageous (penalty) chain, estimated by the value of the attacking figure.
The figures beat each other (for example, they are equal) - everything depends on the parity, odd chains are beneficial, and even ones should be considered as penalties (if there were no other figures on the field, the parity would completely determine the result of the game).
Of course, everything is not so simple. At a minimum, one should keep in mind the specific progress of the cannons in the Taiwan BanQi (As for the "Jungle", there are even more special cases), but this is where to begin. Having a full set of priced chains, you can evaluate the moves. The cost of a move must consist of the value of chains (both bonus and penalty), the length of which it reduces.
First of all, it is important to understand that effectively using minimax algorithms is unlikely to succeed here. Moves that reveal previously hidden pieces, too radically change the assessment of the position. Without information about hidden figures, it is almost impossible to view a position for many moves ahead. But every cloud has a silver lining, but we can use much more complex (computationally) heuristics to evaluate the moves themselves!
I already have a bot evaluating the moves according to their heuristics (needed for one amusing game ). This is a very simple algorithm. All moves are sorted in descending order of heuristics (moves with a negative value of heuristics are generally discarded), after which they are viewed in order. If the next move leads to the position from which there is no response of the enemy, leading to immediate victory, the bot considers it the best. Using this algorithm, you can not bother with the assessment of the position, but you will have to sweat on heuristics .
First of all, we build chains
var getChains = function(design, board) { var player = board.getValue(board.player); if (player === null) return []; if (_.isUndefined(board.chains)) { board.chains = []; var pieces = getGoals(design, board); var targets = getTargets(design, board, pieces); _.each(pieces.positions, function(pos) { var goals = pieces; var f = true; var piece = board.getPiece(pos); if (piece === null) return; if (!chinese && (piece.type == 12)) { goals = targets; f = false; } var group = [ pos ]; var level = []; level[pos] = 0; for (var i = 0; i < group.length; i++) { if (_.indexOf(goals.positions, group[i]) >= 0) { // ... } if ((i > 0) && (board.getPiece(group[i]) !== null)) continue; _.each(design.allDirections(), function(dir) { p = design.navigate(board.player, group[i], dir); while (p !== null) { if (_.indexOf(group, p) >= 0) break; group.push(p); level[p] = level[ group[i] ] + 1; if (f || (board.getPiece(p) !== null)) break; p = design.navigate(board.player, p, dir); } }); } }); } return board.chains; }
Of course, I cache all the intermediate data in the game state, so as not to count them several times. In addition, one trick is used here, which is very useful in calculating connected areas. I iterate through the group array, enclosing additional elements inside the loop, as needed. All difficulties are connected with guns. For them, the targets of the chains are not the figures themselves, but the fields from which the latter can be attacked.
Chains are valued exactly as I said
var getChainPrice = function(design, board, attacker, attacking, len) { var player = board.getValue(board.player); if ((player === null) || (attacker == null) || (attacking === null)) return0; if (attacker.player == attacking.player) return0; var isAttacking = isAttacker(design, attacker.type, attacking.type); var isAttacked = isAttacker(design, attacking.type, attacker.type); if (!chinese && (attacker.type == 12)) { isAttacking = true; isAttacked = (attacking.type == attacker.type) && (len == 1); } var price = 0; var f = (len % 2 == 0); if (attacker.player != player) f = !f; if (isAttacking) { if (isAttacked) { price = f ? (len - design.price[attacker.type]) : (design.price[attacking.type] - len); } else { price = design.price[attacking.type] - len; if (f) price = (price / 2) | 0; } } else { if (isAttacked) { price = len - design.price[attacker.type]; } } return price; }
... depending on the length and parity of the chain, as well as the cost of attacking and attacking figures. But this is only half the battle! It is necessary to evaluate each of the possible moves using the constructed chains. I am introducing another intermediate structure - wishes to aggregate the available data. Assessment of the course consists of assessments of wishes, which it satisfies:
As for the getWish function itself , magic begins here (and this is the place where I most likely plowed and more than once). First of all, I share the assessment of moves based on open information and the introduction of new pieces into the game. This is not entirely correct, but for the time being I just don’t know how to reconcile such diverse assessments. If, based on open information, no wishes have been formed, the bot tries to discover new figures (there are also some tricks here).
If an enemy cannon is open, surrounded by closed figures, it makes sense to open one of the figures next to it, since it is likely that it will be able to attack the cannon, and the cannon cannot beat it anyway.
If the figure is different from the cannon, you can try to open the figure located through the "carriage" from it, because there is a chance that it will be a gun.
In the presence of an attacking chain from the side of the enemy, you can open one of the figures, next to the chain, to intercept the attack.
If you cannot protect the figure, you can open the figure next to it, trying to reduce the situation to exchange.
Of course, the probability of opening a particular figure is useful to evaluate
var getShadow = function(design, board) { var player = board.getValue(board.player); if (player === null) return []; if (_.isUndefined(board.shadow)) { board.shadow = []; _.each(design.allPositions(), function(pos) { var piece = board.getPiece(pos); if ((piece !== null) && (piece.type < 7)) { var value = piece.type + 7; if (piece.player != player) { value = -value; } board.shadow.push(value); } }); } return board.shadow; } var isFriend = function(design, x) { return x > 0; } var isPiece = function(design, x, y) { return x == y; } var isAttacker = function(design, x, enemy) { if (x < 0) returnfalse; if ((x == 13) && (enemy == 7)) returntrue; if (!chinese && (x == 7) && (enemy == 13)) returnfalse; if (!chinese && (x == 12)) returnfalse; return x <= enemy; } var isDefender = function(design, x, enemy, friend) { if (!isAttacker(design, x, enemy)) returnfalse; return design.price[friend] <= design.price[enemy]; } var estimate = function(design, board, p, y, z) { var shadow = getShadow(design, board); if (shadow.length == 0) return0; var r = 0; _.each(shadow, function(x) { if (p(design, x, y, z)) r++; }); return (100 * r) / shadow.length; }
The player can estimate probabilities by keeping records of the figures who have left the game. In principle, the bot can do the same, but there is a simpler way - to view the still not open figures in a crowd and, based on the collected information, evaluate the probability of finding the desired one. At the same time, the success of the chosen move is not guaranteed, but if the probability of a favorable outcome is low, the move will not be chosen at all.
In principle, the approach justified itself, but there is still a lot of work to be done.
So far, not very good defensive moves. Some figures bravely go to meet a stronger enemy, instead of fleeing from it (although it’s usually useless to run away in their case). Also, there are difficulties in coordinating the actions of various figures (this may be useful in order to "drive out" the remnants of the enemy figures). The approach itself looks very promising, but heurists still have to think about it.
Heuristics based on "chains" of moves can be useful not only in BanQi, but also in many other games, with the prevalence of "slow moving" figures (if not as a decisive criterion, then for preliminary assessment of the quality of moves in more complex algorithms, least). This approach is especially in demand in those games for which the use of minimax algorithms is difficult or even impossible (such as Yonin Shogi , for example).
Of course, I'm going to continue working on games with incomplete information. The picture shows the Philippine " Game of Generals ", not yet finished. This is the simplest game from the large family, which includes games like LuzhanQi and Stratego . And of course, I still expect to make a working bot for the " Jungle "!
And for those who are still reading me, I can offer another funny puzzle game with hidden information:
I played it in my childhood, on a programmable calculator, called Fox Hunting. On the field, 8 foxes are randomly hidden, which must be found using the “spear method”. When you select an empty area, the total number of foxes in all eight directions is displayed. It is impossible to lose, but you can compete for the minimum number of clicks. And if you play in headphones, turn down the sound. Maybe I overdid it with sound effects.