Dagaz: Episodios (Parte 1)

Sacudimos sus filtros mentales, y el resultado fue una respuesta. El método funcionó, siempre será efectivo. Todo lo que hay que hacer es deshacerse de la carga extra de prejuicios ...

Raymond Jones " Nivel de ruido "

Dagaz no apareció desde cero. Siempre me han gustado los juegos de mesa y los rompecabezas, y programo todo lo que puedo recordar, pero la idea de algún tipo de motor "universal" simplemente no podría haber pasado por mi mente. Era escéptico de esta idea. Hasta que vi a Zillions . Desafortunadamente, el producto, en ese momento, ya no se estaba desarrollando, el código fuente no estaba disponible y, de hecho, el programa solo funcionaba bajo Windows. Después de un tiempo, decidí emprender un proyecto abierto.

Como ya dije, no tenía ningún código fuente, pero toqué un poco a Zillions y capté su idea principal: la reutilización máxima del código de la aplicación, que permite usar las mismas construcciones en diferentes, que parecerían completamente diferentes entre sí casos. Se trataba del uso correcto. E hice un plan .

Damas


Esta importante pero extremadamente subestimada familia de juegos sentó las bases para el proyecto. Todos los juegos de "damas" son similares entre sí y solo difieren en detalles. En términos de diseño del juego, todos están unidos por tres ideas principales:

  • Por cheque
  • Toma de prioridad
  • Movimiento compuesto

El primer párrafo no plantea preguntas especiales, pero si el desarrollo se centra en los juegos de ajedrez, puede ser una sorpresa. No en todos los juegos, la captura se lleva a cabo en el mismo campo donde la pieza completa el movimiento. Con movimientos prioritarios, las cosas son un poco más interesantes. En las damas, esta regla le permite construir un juego combinacional complejo, atrayendo al enemigo a trampas que operan bajo el principio de "dar menos, recoger más".


Por supuesto, el mecanismo de movimientos prioritarios se implementó en Zillions (y migró de allí a Dagaz). Sin él, casi todos los juegos de damas (ciertamente incluidos en el "juego de caballeros" de juegos de mesa que son obligatorios para la implementación) simplemente no podrían funcionar correctamente. Se trata de los detalles. Veamos cómo se implementó este mecanismo:

En millones
(move-priorities jump-type normal-type) ... (define checker-shift ( $1 (verify empty?) ;        add ;   )) (define checker-jump ( $1 (verify enemy?) ;         capture ;   ( capture  -   ) $1 (verify empty?) ;           add ;   )) ... (piece (name Man) (image White "images/stapeldammen/white.bmp" Red "images/stapeldammen/red.bmp") (moves (move-type jump-type) (checker-jump nw) (checker-jump ne) (checker-jump sw) (checker-jump se) (move-type normal-type) (checker-shift nw) (checker-shift ne) ) ) 

Esta es una descripción casi completa del juego de corrector más simple. El concepto de modos de movimiento ( tipo de movimiento ) se introduce en ZRF , y la construcción de prioridades de movimiento nos permite decir que si hay movimientos de mayor prioridad (tomas), no debería considerarse menos prioridad (movimientos silenciosos). Se pueden determinar los niveles de prioridad y más de dos, en este sentido, el diseño es bastante universal, pero al trabajar en juegos en Dagaz, me encontré con algunas limitaciones de este mecanismo.


En este juego , inventado por Solomon Golomb, además de las damas, también hay piezas de ajedrez. La dificultad radica en el hecho de que tomar, aunque sigue siendo una prioridad para las damas, no lo es para las piezas de ajedrez (de lo contrario, sería demasiado fácil atraparlas y comérselas). Una priorización ingenua usando la palabra clave move-prioridades no funcionará en este juego.

De hecho, si las piezas de ajedrez no se incluyen en los movimientos prioritarios, si existe la posibilidad de tomar tanto una pieza de ajedrez como una pieza de ajedrez, no podremos jugar una pieza de ajedrez, ya que una captura de cheques es una prioridad. Si los movimientos de ajedrez se consideran igualmente prioritarios, estaremos obligados a tomar piezas de ajedrez cuando surja la oportunidad. Ambos contradicen las reglas del juego.

En Zillions, este problema prácticamente no se resuelve. Y esa fue la razón principal por la que pensé en introducir un mecanismo de extensión de JavaScript en Dagaz. La idea, en sí misma, es bastante simple: dado que algunas mecánicas de juego son bastante difíciles de expresar en ZRF, ¿por qué no introducir la fase de procesamiento posterior de los movimientos? El módulo de extensión, en este caso, escanea la lista completa de movimientos generados en su totalidad y puede tomar decisiones sobre el rechazo de ciertos movimientos. Esto es lo que parece para Shashmat :

Código simple y compacto
 var CheckInvariants = Dagaz.Model.CheckInvariants; Dagaz.Model.CheckInvariants = function(board) { var design = Dagaz.Model.design; var types = []; types.push(design.getPieceType("Bishop")); types.push(design.getPieceType("Camel")); var isPriority = false; _.each(board.moves, function(move) { if (isCapturing(board, move)) { if (_.indexOf(types, getType(board, move)) < 0) isPriority = true; } }); if (isPriority) { _.each(board.moves, function(move) { if (!isCapturing(board, move)) { move.failed = true; } }); } CheckInvariants(board); } 

En el futuro, la idea de extensiones se desarrolló y floreció con un color magnífico. Obtuve un mecanismo conveniente y poderoso para codificar muchos juegos cuya implementación en ZRF puro sería extremadamente problemática, pero ¿significa esto que la priorización en el estilo ZRF está desactualizada? Por supuesto que no! Primero, escribir una línea en ZRF es más fácil que cincuenta en JavaScript, pero lo más importante es que las prioridades "duras" al estilo ZRF funcionan de tal manera que ni siquiera se generan movimientos de baja prioridad. Esto es importante en términos de rendimiento. Generar movimientos en Dagaz es una operación muy costosa.

Otro juego con prioridades desafiantes.

Dablot es un juego algo similar a los Drafts italianos , pero más antiguo. Además de las figuras comunes, hay "príncipes" y "reyes", y las figuras más jóvenes no tienen derecho a vencer a los mayores. Pero esa no es la dificultad. Para los reyes (y en algunas variedades del juego para príncipes también), ¡la captura es opcional! Aquí surge el mismo problema que con Shashmat . Si declaramos que el rey es una prioridad, violaremos gravemente las reglas del juego, de lo contrario, no podremos vencer al rey con la posibilidad de una batalla alternativa con una figura simple. Solo el motor de extensión Dagaz resuelve este problema.

Por cierto, con "Borradores italianos" no todo es tan simple. En muchas variedades de borradores hay una regla que establece que el jugador está obligado a tomar el número máximo de piezas. Es decir, no puede simplemente interrumpir la cadena de capturas, sino que debe elegir el camino en el que tomará más piezas. Por las razones que analizaré a continuación, esta regla no se pudo implementar en Zillions de forma universal y los desarrolladores se vieron obligados a codificarla. En los borradores italianos, la "regla de la mayoría" suena aún más complicada: "debes vencer el máximo número posible de borradores del oponente, y con las mismas opciones de batalla necesitas vencer al máximo número de damas".

Los movimientos compuestos son el segundo componente importante de los juegos de damas. Tan importante que la prueba para la captura correcta en los " Borradores turcos " corro periódicamente hasta ahora. Un par de veces cuando rompí el modelo con los siguientes cambios, realmente ayudó.

Movimientos - compuesto y parcial
Veamos cómo se implementan los movimientos compuestos en ZRF
 (define checker-shift ( $1 (verify empty?) (if (in-zone? promotion) (add King) else add ) )) (define checker-jump ( $1 (verify enemy?) capture $1 (verify empty?) (if (in-zone? promotion) (add King) else (add-partial jump-type) ) )) (define king-shift ( $1 (verify empty?) add )) (define king-jump ( $1 (verify enemy?) capture $1 (verify empty?) (add-partial jump-type) )) 

Así de simple es. El comando add-partial dice que el movimiento puede continuar (con la misma pieza, esto es importante) si todavía hay movimientos con el modo especificado. En otras palabras: "la figura debe continuar tomando, mientras exista esa oportunidad". Todo parece estar bien, pero hay una advertencia. Zillions ve cada una de ellas como un movimiento "parcial" por separado. Veamos a qué puede conducir esto.


En este juego , el número de "pasos" realizados por una pieza está determinado por el icono en el que se encuentra. Ahora las blancas se mueven y Damyo está caminando (una pieza marcada con una cuenta roja). En Zillions, después de completar dos movimientos parciales, puede ir fácilmente a la esquina superior izquierda, desde la cual ya no puede hacer el último movimiento restante (no puede regresar). También está prohibido tomar la pieza del oponente en el segundo movimiento parcial. En Zillions no hay forma de prohibir la secuencia de movimientos que conducen a un callejón sin salida.

¡En Dagaz, es diferente! Una secuencia de movimientos parciales siempre se ensambla en un movimiento compuesto completo. ¡Un movimiento que no se puede completar simplemente no va a suceder! Este es un enfoque más intensivo en recursos y, como resultado, generar una lista de movimientos en Dagaz es una operación muy costosa. Pero sus ventajas son significativas. Por ejemplo, el bot recibe el movimiento compuesto completo en su totalidad y no debe mirar hacia adelante, realizando los movimientos parciales restantes.

Aún más importante, este enfoque brinda la oportunidad de considerar la lista completa de movimientos condicionalmente aceptables, realizar verificaciones más complejas y prohibir algunos movimientos dependiendo de la presencia de otros. Por ejemplo, la "regla de la mayoría", que mencioné anteriormente, en Dagaz se implementa de manera bastante simple . Por otra parte, para los "Damas italianas" también. Los desarrolladores de Zillions "resolvieron" el problema que conocían para los juegos de damas al codificar la opción de " capturas máximas ", pero hay una gran cantidad de juegos con otras comprobaciones complejas, que, en ese momento, ¡no tenían idea!

En el proceso de trabajar en nuevos juegos, también se ha desarrollado el concepto de un movimiento compuesto. Fanorona y Pasang sugirieron una mecánica de juego interesante, en la que el jugador que realiza el movimiento debe seleccionar un grupo de piezas retiradas del tablero:


Además, Fanorona es uno de esos juegos raros en los que un jugador tiene derecho a interrumpir la cadena de capturas. La primera captura en ella es obligatoria, la posterior, dentro del mismo movimiento, a discreción del jugador. En Dagaz, esta opción ( pass-parcial ) se implementa moviendo la figura en su lugar. Missclick puede estar aquí, y esto, al parecer, no es muy conveniente, pero con la introducción del administrador de sesión , ahora se pueden revertir movimientos erróneos.

Otro desarrollo del tema fueron los movimientos de "disparos". Primero los hice en Hanga Roa y Ko Shogi , pero como resultó más tarde, ¡lo hice mal! La implementación errónea no funcionó bajo el control de los bots (y dado que todavía no tengo bots para ambos juegos, no es sorprendente que no haya notado nada). Mucho más tarde, cuando hice Amazonas , logré localizar el problema y solucionarlo. Esta idea alcanzó su punto máximo en un juego inventado por uno de nuestros compatriotas en 1957.


Hay otro problema relacionado con la implementación de movimientos compuestos en Dagaz. El hecho es que la lista de movimientos permitidos, a partir de un estado de juego específico, se forma de inmediato, todo. En Zillions, con sus movimientos parciales, esto no es muy crítico, pero en Dagaz, si la pieza tiene la oportunidad de "circular en su lugar", la fase de generación del movimiento nunca se completará (es obvio que es imposible solucionar este problema con extensiones, porque es fácil tratar con ellas). no alcanza) Este es uno de los juegos para los que esto es importante:


Aquí las piezas no se retiran del tablero y la misma pieza se puede saltar muchas veces seguidas. La solución obvia es prohibir visitar el mismo campo dos veces por turno, pero tuve que entrar completamente en el núcleo para implementar tal verificación. Esto fue un poco como la implementación de la opción de " captura diferida ", pero como hice borradores rusos mucho antes, había muchos menos problemas con ella.

Desafortunadamente, hay juegos en los que incluso estos controles no se guardan
Las reglas de los Stapeldammen (esta es una especie de " Pilares " establece explícitamente que la misma pieza puede ser golpeada varias veces por turno. La pieza que realiza el movimiento regresa a la misma posición varias veces y continúa la batalla, mientras está en el enemigo hay figuras en las columnas ... Los movimientos compuestos de Dagaz no pueden hacer frente a este problema. La lógica de la batalla de Pole Drafts es demasiado complicada para el núcleo, y no llegará a las extensiones, porque la generación del bucle está en bucle. Por supuesto, hay una salida:


No hay movimientos parciales en Dagaz, pero podemos emularlos saltando el próximo movimiento del oponente (se usa el mismo enfoque en las calcomanías ). Y solo esta lógica se implementa fácilmente por la extensión . Simplemente prohibimos todos los movimientos bajo ciertas condiciones, y la opción pasar-girar = forzado genera automáticamente un movimiento vacío. Aquí hay otro juego con emulación similar.


La división artificial de movimientos compuestos en parciales no es muy buena para los robots de IA, pero a veces, simplemente no hay otra salida.

En general, el concepto de compuesto mueve vidas y se desarrolla. Más recientemente, tuve que hacer otra nueva opción ( completa-parcial ) para un antiguo juego egipcio.


Además de completar automáticamente el movimiento de figuras a lo largo de las flechas, también tiene otras soluciones técnicas interesantes. Pero sobre esto en otro momento.

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


All Articles