Dagaz: fuera de la niebla

imagen Todo esto es la reina de la reina Mab.
Ella teje en los establos de una melena
Y el cabello derriba un enredo ...

William Shakespeare

Fue un lanzamiento largo, pero se hizo mucho. Ha aparecido un administrador de sesión , que le permite retroceder movimientos realizados por error. En algunos lugares, se agregó el diseño de sonido. Y, sin embargo, se me ocurrió una forma genial de impulsar varias opciones alternativas para la colocación inicial en un juego. Y lo más importante: finalmente llegué a los juegos con información incompleta.

Explicaré lo que está en juego. En los juegos de mesa habituales, como Ajedrez o Damas , los jugadores, en cualquier momento del juego, tienen información completa sobre la ubicación de las piezas (la suya y la del oponente), las reglas para moverlas, los objetivos del juego, etc. Tales juegos están bastante bien estudiados y entran en la categoría de " juegos con información completa ". Ahora, imagine que parte de esta información puede estar oculta para el jugador.


La niebla de la guerra es una gran ilustración del tema. De acuerdo con las reglas del " Ajedrez a ciegas ", los jugadores no pueden ver todas las piezas del enemigo, sino solo aquellas que se colocan en los campos, a las que se puede acceder con un movimiento de cualquiera de sus piezas. Hice dos adiciones a esta regla:

  1. Por supuesto, el jugador siempre ve sus piezas, pero por la forma en que se muestran, en forma normal o translúcida, puede juzgar si el oponente las ve.
  2. Solo con fines decorativos, coloqué "nubes" en áreas que actualmente son invisibles.

Habiendo dominado el principio general, me dejé llevar e hice una gran cantidad de juegos con la "niebla de guerra". Además del ajedrez en sí , tengo opciones "oscuras" para Xiang , Changi , Shatrange , Sittuyin y muchos otros juegos. ¡Incluso hay un " Blind Guns "! Todos estos juegos tienen una cosa en común:

¡La computadora está haciendo trampa!
Ni siquiera intenté hacer cambios en los algoritmos de los bots para estos juegos, porque aposté a que las condiciones desiguales compensan al menos parcialmente su juego extremadamente débil en comparación con los humanos. Como escribí anteriormente, el desarrollo de IA de alta calidad para juegos de mesa es una tarea muy difícil. Por supuesto, las reglas tienen excepciones. Incluso con un juego muy débil del bot, será difícil para una persona jugar un juego desconocido, literalmente repleto de trampas. ¿Qué podemos decir sobre su versión "oscura"?

Sin embargo, en general, este no es un enfoque muy correcto. Quiero ver un bot que pueda hacer exactamente con los datos que tiene su adversario: man. ¿Por qué es esto importante? Todo es muy simple: por la forma en que juega el bot, a veces es muy fácil adivinar si tiene acceso a información oculta (píos) o no. Y, por supuesto, es mucho más interesante que una persona juegue con ese bot que no se asoma (jugar con otra persona es aún más interesante, pero esta es una historia diferente).

Y aquí vale la pena elegir un juego que sea ligeramente diferente del Ajedrez (ya que no estoy listo para desarrollar un bot "honesto" jugando ajedrez "a ciegas"). Hay muchos juegos de este tipo y no se puede decir que sean más simples que el Ajedrez o las Damas. Son simplemente diferentes y requieren un enfoque individual.

Por ejemplo
Hay un juego para niños con el que aún no he logrado desarrollar un bot. Se llama la "Jungla" o Dou Shou Qi . El objetivo del juego es penetrar el territorio enemigo. Cada uno de los jugadores tiene una "guarida", un campo central en la primera línea. Si alguna de las figuras del enemigo entra en la guarida, él ganó (no puedes ocupar la guarida con tus propias figuras).


Las cifras están ordenadas por antigüedad. El elefante supera a todas las figuras, seguido de: león, tigre, leopardo, perro, lobo, gato y rata. Una rata solo puede vencer a un elefante y otra rata, además, esta es la única figura que puede moverse en el agua (en el centro del tablero hay dos depósitos). Un tigre y un león pueden saltar sobre el agua, pero solo si la rata no bloquea el agua. Con la excepción de los saltos, todas las figuras se mueven de la misma manera: a un campo adyacente vertical u horizontalmente. La guarida está rodeada de trampas. La figura en la trampa es vulnerable a cualquier figura enemiga.

Como puede ver, las reglas son bastante simples. ¿Qué impide el desarrollo de un bot para este juego? En primer lugar, las cifras de baja velocidad. Si hay amenazas, puedo apreciar los beneficios de los intercambios, pero durante la mayor parte del juego, las piezas simplemente se ejecutan una tras otra en distancias bastante largas. No puedo permitirme ver el juego para una gran cantidad de movimientos hacia adelante (debido a restricciones en la duración del cálculo del movimiento), como resultado de lo cual los cambios caen fuera del horizonte de visualización y todos los movimientos se vuelven equivalentes para mí.

Para empezar, decidí pensar en BanQi : ajedrez chino ciego. Este es un juego muy original con información oculta, similar a la "Jungla". Es importante para mí que los desarrollos, en relación con la creación de un bot para este juego, se puedan usar en otros juegos, como Dou Shou Qi , Luzhan Qi , Stratego o incluso (posiblemente) Tafl .


Te contaré sobre las reglas. El juego se ejecuta en la mitad del tablero para el "Ajedrez chino" ( Xiang Qi ), mientras que el diseño original del tablero no juega ningún papel. Las piezas se colocan dentro de las celdas (como en las tradicionales), y no en las intersecciones de las líneas (como en el ajedrez chino). Al comienzo del juego, todas las piezas se mezclan a fondo y se colocan boca abajo en el tablero (dado que las piezas tradicionales de Syants son una especie de barriles, y su número coincide con el número de campos en la mitad del tablero, no hay dificultad).

A continuación, los jugadores alternan sus movimientos. Al realizar un movimiento, el jugador puede voltear cualquiera de las piezas cerradas, o mover una pieza abierta previamente de su color. Los colores de los jugadores están determinados por el primer movimiento. Si se abre la primera pieza negra, el jugador que la abrió jugará en negro. Todas las figuras en el juego van de la misma manera (con la excepción del "Cañón" en la versión taiwanesa, que discutiré más adelante), en una celda adyacente vertical u horizontalmente. La posibilidad de tomar está determinada por el orden de antigüedad de las cifras:

General> Asesor> Elefante> Vagón> Caballo> Cañón> Soldado

Las figuras más viejas golpean más jóvenes o iguales a ellos, con una excepción: un soldado golpea al general (una especie de " Piedra-Papel-Tijera "). Queda por decir algunas palabras sobre TaiQuan BanQi:

  1. A diferencia de la versión china, en Taiwán BanQi, un general no puede vencer a un soldado.
  2. El cañón se mueve de acuerdo con las reglas XiangQi , es decir, a cualquier número de campos ortogonales a baja velocidad (como un carro) o golpea a cualquier figura enemiga, con un salto a través del "carro", al realizar un movimiento de ataque.

También hay una versión de Hong Kong, pero prácticamente no es diferente de la china, excepto que el orden de antigüedad de las cifras ha cambiado. Decidí centrarme en la versión taiwanesa de las reglas, como la más interesante tácticamente.

¿Qué debo buscar al desarrollar un bot?
En primer lugar, el juego parece muy simple, pero no lo es. Incluso si no considera los matices asociados con las armas taiwanesas, el costo de las cifras es contradictorio. Aunque el "Asesor" puede vencer a menos figuras que el "General", él es el protagonista principal del juego. En primer lugar, el jugador tiene dos asesores. Además, solo un general enemigo es superior en fuerza a cada asesor, ¡mientras que el general puede ser atacado por hasta cinco soldados! Por la misma razón, el costo de un soldado en un juego es más alto que el costo de un general. ¡Al final, puede vencer a la figura más fuerte! La segunda consideración importante sugiere uno de los rompecabezas "Canterbury" de Henry Dudeney.


Esta es más una tarea de broma que un rompecabezas completo. Todas las figuras pueden ir a un campo adyacente vertical u horizontalmente. ¡El blanco se mueve primero, mientras que el blanco y el negro siempre hacen dos movimientos (en piezas diferentes)! En estas condiciones, el bufón izquierdo nunca puede atrapar al burro izquierdo, y el bufón derecho nunca puede atrapar al correcto (puede verificarlo usted mismo). Por supuesto, el bufón derecho puede atrapar al burro izquierdo sin ninguna dificultad. ¡Se trata de paridad!

Este problema me dio algunas ideas. En primer lugar, la tarea del bot, en juegos como BanQi o DouShouQi, es, en primer lugar, encontrar el camino más corto. De cada una de las piezas activas (la propia o la del oponente), es necesario construir cadenas de movimientos para todos los objetivos posibles (incluidas sus propias piezas, para calcular posibles intercambios). Después de eso, las cadenas deben evaluarse y las siguientes opciones son posibles aquí.

  1. La figura atacante vence al atacado: una cadena rentable (bonificada) estimada por el costo de la figura atacada (menos el costo del atacante, si este último está protegido), teniendo en cuenta la longitud de la cadena.
  2. La figura atacante vence al atacado, no una cadena rentable (penalización), estimada por el valor de la figura atacante.
  3. Las piezas se vencen entre sí (por ejemplo, son iguales): aquí todo depende de la paridad, las cadenas impares son ventajosas y las pares deben considerarse como penalizaciones (si no hubiera otras figuras en el campo, la paridad determinaría completamente el resultado del juego).

Por supuesto, no todo es tan simple. Como mínimo, debe recordar el curso específico de los cañones en BanQi de Taiwán (En cuanto a la "Jungla", hay casos aún más especiales), pero aquí es donde puede comenzar. Con un conjunto completo de cadenas evaluadas, puede evaluar movimientos. El costo del movimiento debe consistir en el costo de las cadenas (tanto de bonificación como gratuitas), cuya longitud se reduce.

En primer lugar, es importante comprender que es poco probable que pueda utilizar efectivamente algoritmos minimax aquí. Los movimientos que revelan piezas previamente ocultas también cambian radicalmente la estimación de posición. Al no tener información sobre piezas ocultas, es casi imposible ver una posición con muchos movimientos por delante. Pero cada nube tiene un lado positivo, ¡pero podemos usar heurísticas mucho más complejas (en términos de cálculo) para evaluar los movimientos en sí!

Ya tengo un bot que evalúa los movimientos por su heurística (necesario para un juego divertido). Este es un algoritmo muy simple. Todos los movimientos se ordenan en orden descendente por heurístico (los movimientos con un valor heurístico negativo generalmente se descartan), después de lo cual se escanean en orden. Si el siguiente movimiento lleva a una posición desde la cual no hay respuesta enemiga que conduzca a una victoria inmediata, el bot lo considera el mejor. Con este algoritmo, no puede molestarse con la estimación de la posición, pero debe sudar por la heurística .

Primero que nada, construimos cadenas
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; } 

Por supuesto, guardo en caché todos los datos intermedios en el estado del juego, para no leerlos varias veces. Además, aquí se usa un truco, que es muy útil para calcular áreas conectadas. I itero sobre la matriz del grupo , colocando elementos adicionales dentro del ciclo, según sea necesario. Todas las dificultades están asociadas con las armas. Para ellos, los objetivos de las cadenas no son las figuras mismas, sino los campos desde los cuales se puede atacar a estas últimas.

Las cadenas se evalúan exactamente como dije
 var getChainPrice = function(design, board, attacker, attacking, len) { var player = board.getValue(board.player); if ((player === null) || (attacker == null) || (attacking === null)) return 0; if (attacker.player == attacking.player) return 0; 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; } 

... dependiendo de la longitud y paridad de la cadena, así como teniendo en cuenta los costos de las figuras atacantes y atacadas. ¡Pero esto es solo la mitad de la batalla! Es necesario evaluar cada uno de los movimientos posibles usando las cadenas construidas. Presento una estructura intermedia más: desea agregar los datos disponibles. La evaluación del curso consiste en evaluaciones de los deseos, que satisface:

Algo como esto
 var addWish = function(board, comment, price, src, dst) { if (_.isUndefined(board.wish[src])) { board.wish[src] = []; } if (_.isUndefined(dst)) dst = src; if (_.isUndefined(board.wish[src][dst])) { board.wish[src][dst] = price; } else { board.wish[src][dst] += price; } } var getWish = function(design, board) { if (_.isUndefined(board.wish)) { ... } return board.wish; } Dagaz.AI.heuristic = function(ai, design, board, move) { var wish = getWish(design, board); if (move.isSimpleMove() && !_.isUndefined(wish[ move.actions[0][0][0] ]) && !_.isUndefined(wish[ move.actions[0][0][0] ][ move.actions[0][1][0] ])) { return wish[ move.actions[0][0][0] ][ move.actions[0][1][0] ]; } return 0; } 

En cuanto a la función getWish en sí , la magia comienza aquí (y este es el lugar donde probablemente aré más de una vez). En primer lugar, comparto la evaluación de movimientos basada en información abierta y la introducción de nuevas piezas en el juego. Esto no es del todo correcto, pero hasta ahora no sé cómo conciliar opiniones tan diversas. Si en base a información abierta no se formaron deseos, el bot intenta abrir nuevas figuras (también hay algunos trucos aquí).

  1. Si un cañón enemigo está abierto, rodeado de figuras cerradas, tiene sentido abrir una de las figuras al lado, ya que es probable que pueda atacar el arma, y ​​el arma no podrá vencerlo, en cualquier caso.
  2. Si una figura que no sea un cañón está abierta, puede intentar abrir una figura ubicada a través del "carro", ya que existe la posibilidad de que resulte ser un cañón.
  3. Si hay una cadena de ataque desde el lado del enemigo, se puede abrir una de las piezas, al lado de la cadena, para interceptar el ataque.
  4. Si no puede proteger la figura, puede abrir la figura al lado, tratando de reducir la situación a un intercambio.

Por supuesto, es útil evaluar la probabilidad de abrir una figura en particular.
 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) return false; if ((x == 13) && (enemy == 7)) return true; if (!chinese && (x == 7) && (enemy == 13)) return false; if (!chinese && (x == 12)) return false; return x <= enemy; } var isDefender = function(design, x, enemy, friend) { if (!isAttacker(design, x, enemy)) return false; return design.price[friend] <= design.price[enemy]; } var estimate = function(design, board, p, y, z) { var shadow = getShadow(design, board); if (shadow.length == 0) return 0; var r = 0; _.each(shadow, function(x) { if (p(design, x, y, z)) r++; }); return (100 * r) / shadow.length; } 

El jugador puede evaluar las probabilidades haciendo un seguimiento de las cifras que han abandonado el juego. En principio, el bot puede hacer lo mismo, pero hay una manera más fácil: mirar las cifras aún no abiertas a granel y evaluar la probabilidad de abrir la deseada en función de la información recopilada. Además, el éxito del movimiento seleccionado no está garantizado, pero si la probabilidad de un resultado favorable es baja, el movimiento no se seleccionará en absoluto.

En principio, el enfoque valió la pena, pero aún queda trabajo por hacer.
Si bien los movimientos defensivos no son muy buenos. Algunas figuras se enfrentan valientemente al enemigo más fuerte, en lugar de huir de él (aunque huir en su caso, como regla, ya es inútil). Además, existen dificultades para coordinar las acciones de varias figuras (esto puede ser útil para "conducir" los restos de figuras enemigas). El enfoque en sí parece muy prometedor, pero la heurística aún debe considerarse.

La heurística basada en "cadenas" de movimientos puede ser útil no solo en BanQi, sino también en muchos otros juegos, con un predominio de piezas de "movimiento lento" (si no es un criterio definitorio, entonces para una evaluación preliminar de la calidad de los movimientos en algoritmos más complejos, al menos menos) Este enfoque es especialmente solicitado en aquellos juegos para los cuales el uso de algoritmos minimax es difícil o incluso imposible (como Yonin Shogi , por ejemplo).


Por supuesto, continuaré trabajando en juegos con información incompleta. La imagen muestra el " Juego de los generales " de Filipinas, que aún no está listo. Este es el juego más fácil de una familia numerosa, incluidos juegos como LuzhanQi y Stratego . Y, por supuesto, ¡todavía espero hacer un bot de trabajo para " Jungle "!

Y para aquellos que todavía me están leyendo, puedo ofrecer otro divertido juego de rompecabezas con información oculta:


Lo jugué en mi infancia, en una calculadora programable llamada Fox Hunt. Ocho zorros se ocultan al azar en el campo, que se debe encontrar utilizando el "método de empuje". Cuando selecciona un área vacía, se muestra el número total de zorros en las ocho direcciones. Es imposible perder, pero puede competir por la cantidad mínima de clics. Y si juegas con auriculares, baja el sonido. Quizás lo exageré con efectos de sonido.

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


All Articles