Sobre lo inmutable: historia del noveno lugar de la Copa AI rusa 2019

Mi nombre es Andrey Rybalka, participo en la Copa AI de Rusia bajo el sobrenombre de lama y les diré nuevamente cómo no ganar un macbook. Afortunadamente, soy una persona con experiencia en esto, con estas manos no he ganado hasta 7 piezas.


Por lo tanto, la tarea de este año fue un juego de plataformas / juego de disparos en 2D, para el que tenía que escribir un bot. El juego se veía así:



El bot se veía así:



Si está interesado en cómo la imagen # 2 jugó en la imagen # 1, debajo de cat.


Si no participó y no leyó otros artículos, le recomiendo que primero vea cómo se ve todo en dinámica en el sitio o en el tubo:



Sistema de torneo


Para empezar, se dan más de 2 semanas para la programación. Entonces comienza la primera ronda. Dura 2 días y los 300 mejores continúan. Después de la ronda, las reglas del juego cambian (ahora controlamos dos personajes a la vez) y se da otra semana, después de lo cual pasa la segunda ronda. Luego, las reglas se complican nuevamente (ahora jugamos en mapas mucho más complejos), se da otra semana y, finalmente, jugamos la final.


Pero este no es el final. Después de la final, hay otra semana, al final de la cual el sandbox simplemente se detiene, y también se otorgan los 6 mejores, excluyendo a los ganadores de la final. La diferencia fundamental entre el final del sandbox y el final del campeonato es que en el sandbox, los juegos se crean en un formato aleatorio, y no solo en el formato de la ronda actual.


Historial de participación


¿Dónde sin eso? Puedes saltarte a los que no estén interesados. La parte técnica será más baja.


Beta Test Week y Round One


Parece que comencé a programar el día después del inicio de la Prueba Beta abierta. Pero personalmente, fue algo desmotivador para mí que los organizadores esta vez, a diferencia del pasado, decidieran alejarse nuevamente de la práctica de publicar un pseudocódigo de un simulador. Seguramente entre los participantes estaban aquellos a quienes les gusta escribir un simulador por ingeniería inversa, pero yo no soy uno de ellos, y me aburrí con esto. Como este no era mi primer campeonato, sabía que tarde o temprano me involucraría, pero por la razón descrita anteriormente, me involucré demasiado tarde. Como resultado, lo que hicieron algunos participantes en los primeros días, pude obligarme a terminar en solo una semana y media. Como resultado, escribí la primera línea de código del bot 5 días antes de la primera ronda. En resumen, al comienzo de la primera ronda, no estaba listo, y la ronda pasó sin mi participación. Decidí llegar a la segunda ronda, a través de la marcación de extensión.


Segunda ronda


En este punto, ya programé al máximo, en promedio de 4 a 6 horas al día. Un par de días antes del comienzo de la segunda ronda, cargué la primera versión del bot. Inmediatamente subió rápidamente y se metió rápidamente en los 10 mejores cajones de arena. Entonces comenzó la ronda, donde ocupé el quinto lugar.


Final


Pasé la primera noche de la última semana (de cuatro, porque estaba programada una salida para el viernes) para encontrar el camino. Todavía había muchas ideas, pero no estaba claro en qué concentrarse y cuál de las ideas potencialmente daría el mejor resultado, así que intenté mejorar en primer lugar qué causó la mayor cantidad de derrotas. El martes y miércoles, estas fueron mejoras en la puntería y el tiro, así como el control de los botiquines de primeros auxilios.


Entonces vi que había sucedido algo que había estado esperando durante mucho tiempo, pero esperaba que esto no sucediera, algunos oponentes comenzaron a usar minas activamente.


El hecho es que durante el OBT, los organizadores escucharon la propuesta de los participantes y cambiaron una de las mecánicas del juego. En el caso general, esto, por supuesto, es bueno cuando se escucha a los participantes. Pero específicamente esta vez, la presencia de comentarios jugó una broma cruel. En resumen, los organizadores hicieron posible que las minas fueran detonadas con un disparo.


Esto en sí mismo es lógico. Si fuera una mina y me dispararan, también explotaría. Pero el problema es que jugamos en puntos, y se otorgaron puntos no solo por asesinatos, sino también por daños. Por lo tanto, resultó que si te acercas lo suficiente al enemigo, colocas minas debajo de ti y les disparas, matas tanto al enemigo como a ti mismo con una explosión. Es imposible esquivarlo. Ambos obtienen 1000 puntos por los dos personajes que mueren, pero también obtienen puntos por daño. Por lo tanto, si el enemigo estaba sano, recibirás 1000 puntos por matar y 100 por daño, y el enemigo, solo 1000.


Aunque todos lo sabían, pero en la parte superior casi nadie usaba minas hasta el final. No sé cómo otros, pero personalmente, no lo usé simplemente porque no quería abusar del error de cálculo involuntario. Pero, como decían en una película, sabía que tarde o temprano iremos a esta basura.


En resumen, en medio de la noche, de miércoles a jueves, agité la autointerrupción. Puramente situacional, nada avanzado. Solo por el principio de que si en el tic actual mi personaje es rentable para suicidarse, y él puede hacerlo, lo hace. Como lo ha demostrado la práctica, algunos otros participantes planearon usar minas por adelantado, por lo que implementaron un suicidio más avanzado, pero planearon estratégicamente desatarlo justo antes de la final. Como resultado, resultó lo que sucedió: noveno lugar en la final, así como el año pasado.


Sandbox Finale


Después del final, me fui de la ciudad para las vacaciones. Regresó una semana más tarde y la primera noche, literalmente en 2-3 horas de trabajo, mejoró mucho las minas, sobre las que escribiré más en la parte técnica. El resto de los cambios se referían principalmente a la edición de errores y la finalización de la función de evaluación. En resumen, un poco de programación y muchas pruebas.


Parte técnica


Ya sea la velocidad de Java o el radio de curvatura de las manos, sino que ambos juntos, una vez más, no me permitieron llevarme bien con una simulación pura. Por lo tanto, el movimiento, el disparo normal y la instalación de minas están separados de mí.


Simulación


A través de los ojos de un bot, el mundo se veía así:



En el video, creo, y todo está aproximadamente claro. En la esquina superior izquierda se encuentra la depuración de la función de evaluación. Puedes ver en qué consiste la puntuación de cada trayectoria. Siluetas amarillas (por ejemplo, a las 2:30): estas son las mismas trayectorias en 9 direcciones, aproximadamente serán más bajas. Las líneas de flechas en algún lugar entre las 2:50 son una búsqueda de un camino (rojo - al enemigo, verde - al botiquín de primeros auxilios, amarillo-verde - al arma, azul - a la mina). Los cuadrados verdes al final del video son mis mosaicos PVS, que son visibles desde el mosaico seleccionado. Los puntos rojos en sus centros indican de dónde proviene la visibilidad inversa.


Los movimientos de los personajes simulan sin microtics. Las balas también vuelan, pero en ellas utilizo varios hacks para que sea menos probable que ocurran situaciones cuando la garrapata completa la bala "atraviesa" al personaje sin tocarlo, aunque debería haberlo tocado con micrófonos. Por ejemplo, si lo piensa, la situación descrita solo puede ocurrir en las esquinas:



En la marca 1, la viñeta está en la posición 1, en la marca 2 - en la posición 2, respectivamente. Sin Mikrotik - Boris-rábano picante - obtienes, con Mikrotik - oreja de bala. Entonces, para que esto suceda, la posición de la viñeta en las marcas 1 y 2 debe estar en lados diferentes de al menos una de las caras del personaje o mosaico (un rectángulo de menta). Por lo tanto, es posible simular una viñeta sin microtics hasta que, como se aplica en la imagen de arriba, se old_bullet.x > character_left_side.x != new_bullet.x > character_left_side.x la condición old_bullet.x > character_left_side.x != new_bullet.x > character_left_side.x , y si esto sucede, debe analizar esta marca más cuidadosamente .


Además, cada tic recuento las trayectorias de todas las balas voladoras antes de que chocaran con la pared y guardaran su posición en cada tic en una matriz, para que en la simulación pudieras comprobar rápidamente las colisiones con ellos.


Para una bazuca, después de golpear una pared, matemáticamente, sin microtics, calculé el punto exacto de impacto para calcular correctamente el epicentro de la explosión.


Además, cada tick llené la matriz dodge_trajectories : dodge_trajectories el movimiento de cada personaje, incluidos los enemigos, en 8 direcciones para 25 ticks (siluetas amarillas en el video del visualizador, después de activar la posible casilla de verificación de traj. Por ejemplo, a las 2:30). Y, como con las balas, mantuvo todas las posiciones posibles en cada tic. Luego se usó en muchos lugares, algunos de los cuales mencionaré.


También calculé PVS por azulejos de antemano. Para cada celda, mantuve una lista de fichas, cuyo centro se puede ver de pie en ella. Se calculó por trazado de rayos. Esto se puede ver en el video del visualizador, al final. Los cuadrados verdes son mosaicos que son visibles desde la celda seleccionada. Los puntos rojos en sus centros indican de dónde proviene la visibilidad inversa.


Busca un camino


Implementado por el algoritmo de Dijkstra según Waypoints. El mosaico en el que puedes pararte fue considerado Waypoint. La adaptación del algoritmo para el juego de plataformas 2D es propia, propia y, por lo tanto, en aras de la optimización, se basa en muletas. Pero funcionó lo suficientemente rápido: anteriormente construí Dijkstra (no sabía cómo decirlo correctamente) de cada sujeto en el nivel de cada mosaico. Construí caminos solo para aquellos que tenían capacidad de campo a través en dos sentidos. Esto era necesario para que luego pudieras pasar rápidamente de cualquier ficha a cualquier botiquín de primeros auxilios / arma / mina. Era posible deshacerse de esta restricción, pero en la práctica, consideraba más razonable dedicar tiempo a otras cosas, porque no había mucho daño por esta restricción.


Además, cada vez que cambiaba a otra ficha con cualquier personaje (el mío y el del enemigo), contaba para él el camino al enemigo más cercano, el botiquín de primeros auxilios, y también a la mina y las armas, si aún las necesitaba.


En la computadora de mi casa en un juego con 1000 ticks, la búsqueda de ruta completa tomó alrededor de 100 ms en total.


Si el camino se cruza con un enemigo o amigo, simplemente agrego un par de docenas de unidades a su peso. Por lo tanto, si hay un desvío relativamente corto, o si después de eso es más rentable correr hacia otro objeto, lo haré. En el video del visualizador anterior, esto se puede ver a las 2:55, donde se omitió el camino hacia el enemigo más cercano, porque Un camino recto cruza el segundo personaje.


Visualización de ruta de búsqueda:



Los cuadrados púrpuras translúcidos son puntos de referencia, también son los vértices del gráfico. Flechas verde lima - bordes 1 columna 2
1 no debe confundirse con las costillas; 2 cualquier coincidencia de naturaleza monárquica es aleatoria [aprox.]


Movimiento


Dos personajes caminaron por turno. Si solo pensabas: "¡¿y ahora está entre los 10 primeros?!", Entonces pensaste correctamente. :) No había suficientes recursos para caminar con ambos. Una excepción fue el caso de la aparición de una nueva bala voladora.


La base para elegir una trayectoria es la genética sin cruzar. Pero si el año pasado, en el fútbol, ​​la genética se mostró perfectamente, entonces este año dio una ventaja muy pequeña. Mis experimentos mostraron un resultado muy cercano tanto en genética como en búsquedas aleatorias. Veo varias razones para esto, pero la principal me parece la siguiente: en el fútbol, ​​teníamos un objetivo fijo: el balón. La mayor parte de su tiempo su comportamiento era predecible: si encontramos una buena trayectoria para golpear la pelota en la portería y la seguimos, luego de 20 ticks, esta trayectoria probablemente seguirá siendo buena, porque la pelota no puede cambiar espontáneamente su camino. Pero este año jugamos no con la pelota, sino con personajes enemigos. Cambiaron su comportamiento cada tic. Entonces la relevancia de la trayectoria se mantuvo muy corta.


Todavía usé la genética por dos razones:


  1. Acabo de copiar su código del año pasado. Incluso fue divertido lo poco que tuve que hacer cambios, con la excepción de la función de evaluación, para que el bot comenzara a moverse de manera tolerable. En resumen, escribí un simulador durante una semana y media, y luego durante aproximadamente una hora copié la lógica del movimiento del año pasado, en otra hora arrojé una estimación simple y el bot, al disparar a la Quick Start, comenzó a ganar de manera bastante estable.
  2. Aparentemente amo el dolor. De lo contrario, no sé cómo explicar por qué volví a escribir en Java, y no me gustan todas las tapas normales:


Así que tuve que hacer todo en todos los sentidos para que la estrategia simplemente no cayera con un tiempo de espera en cada primer juego. Y el enfoque con la genética me permitió guardar una instantánea de la simulación del mundo y los puntos contados, y luego, al calcular las próximas generaciones, desactivar las instantáneas solo a partir del primer gen mutado .


Es decir Hablando en términos generales, si en la primera generación fui 10 tics a la derecha, y luego salté, y en la segunda generación evalúo un mutante que va 10 ticks a la derecha y luego salta, entonces el punto de referencia para el mutante será la instantánea calculada previamente del mundo y los puntos posteriores 10 movimientos a la derecha. Por lo tanto, reduje significativamente el cálculo.


Igogo, un algoritmo de movimiento aproximado:


  1. Generamos N genotipos aleatorios. Cada uno consta de M genes aleatorios. Cada gen son números codificados por acción: un número es responsable de la dirección del movimiento, el segundo es para saltar / caminar / saltar hacia abajo, el tercero es para el disparo básico (puramente para la función de evaluación, el algoritmo de disparo básico se describirá a continuación), el cuarto para el número de repeticiones de este acción El número total de acciones en el genotipo, junto con las repeticiones, no supera la profundidad de simulación: 40 ticks.
  2. Agregamos una serie de genotipos codificados: movimiento directo en 9 direcciones (8 lados + parado) y un par de ajustes preestablecidos simples, que en la práctica ayudaron a salir de algunas situaciones típicas en el laberinto un poco más rápido. Por ejemplo, estas son las trayectorias: ⮤ ⮥
  3. Agregue el mejor genotipo del movimiento anterior.
  4. Evalúa todo, deja la M mejor.
  5. Estamos creando la próxima generación en la que en cada genotipo mutaron uno o más genes.
  6. Los agregamos al grupo general, que ya contiene el mejor M de la última generación.
  7. Repetimos, comenzando desde el punto 4, varias veces más.

Función de evaluación


En realidad, el lugar donde vive la estrategia.


A lo largo del campeonato, se agregó una gran cantidad de todo tipo de métricas (para ser precisos, 57). Algunos de ellos no vivieron para ver las finales. La otra parte sobrevivió, pero en el contexto de la inflación de la puntuación durante el campeonato, como resultado, prácticamente no afectó el resultado, pero el resto, del orden de 20-25, fue precisamente responsable del movimiento y los disparos básicos.


Daré algunos ejemplos de métricas importantes, en orden aleatorio:


  1. Pena por una explosión de bala / bazooka en mí.
  2. Bonificación por grados de libertad. Es decir para la cantidad de direcciones (arriba / abajo / izquierda / derecha) donde puedo moverme desde el mosaico actual. Como puede suponer, cuantos más grados de libertad, más posibilidades de esquivar una bala. Esta bonificación obligó al bot a adherirse a plataformas y escaleras. La bonificación es tres veces mayor si el oponente tiene una bazuca.
  3. Penalización por la distancia (a través de la búsqueda del camino) al botiquín de primeros auxilios más cercano; para la distancia promedio (a través de la búsqueda de la ruta) a todos los botiquines de primeros auxilios; porque el camino de él al botiquín de primeros auxilios más cercano a él es menor que el camino de mí a su (!) botiquín de primeros auxilios.
  4. Pena por el hecho de que el enemigo ve mi cabeza.
  5. Penalización por el hecho de que el enemigo ve mis piernas (los puntos 4 y 5 obligaron al bot a esconderse detrás de refugios parciales).
  6. Ataque adicional mientras se recargan armas enemigas.
  7. Bonificación por disparos realizados.
  8. Penalización por demasiado o muy poca distancia del enemigo. Depende de varios factores: la salud de ambos, si las armas de ambos fueron recargadas, etc.
  9. Bonificación por condiciones de trabajo peligrosas.
  10. Sanciones por el estado de caída y por el estado de vuelo en la plataforma de salto (porque estos dos estados no se pueden interrumpir y, por lo tanto, es mucho menos probable que evadan la bala).
  11. La bonificación por la cantidad de botiquines de primeros auxilios que están en el lado opuesto de mí, en comparación con el enemigo (es decir, si el enemigo está a mi derecha, entonces obtendré una bonificación por la cantidad de botiquines de primeros auxilios que están a mi izquierda. Esto básicamente tenía sentido en Rondas 1 y 2, donde el único camino del enemigo a estos botiquines fue a través de mí).
  12. Bonificación por seguir el camino al enemigo, al botiquín de primeros auxilios (si estoy herido), a una mina (si tengo menos de dos), a las armas (si no hay ninguna).
  13. Penalización por la proximidad de las paredes en caso de que el enemigo tenga una bazuca. Más específicamente, consideré cuántas garrapatas el cohete del enemigo, si lo libera, volará a la pared cerca de mí, y en base a esto, esa zona cerca de la pared de la que es imposible escapar de la explosión se consideró peligrosa y recibí una multa por estar en ella.
  14. Bono de año nuevo.

Evasión de bala


Sucede automáticamente, debido a lo anterior.


Apuntando


Están sucediendo muchas cosas. Quizás lo más importante es un algoritmo simple y efectivo que decida si debo apuntar un arma al enemigo (porque apuntar aumenta la propagación de armas):


  1. Consideramos el ángulo entre (last_angle - min_spread) y (last_angle + max_spread).
  2. Disparamos los rayos desde el centro de mi personaje a las dos esquinas del enemigo AABB más cercano a la perpendicular. Si alguno de ellos está fuera de rango (last_angle - min_spread) ... (last_angle + max_spread), cortamos este rango.
  3. Consideramos el ángulo entre estos rayos.
  4. Divida la primera segunda esquina entre la primera, obtenemos cobertura (cobertura). Representa la probabilidad actual en porcentaje de que la trayectoria de la bala disparada se cruce con la caja del oponente.
  5. Simulamos una Acción en la que se apunta a un enemigo, junto con un cambio en la dispersión.
  6. Repita los pasos 1..4 para el nuevo conjunto [last_angle, min_spread, max_spread]. Por lo tanto, consideramos cuál será la cobertura en caso de que apuntemos.
  7. Como resultado, tenemos la cobertura actual, así como la cobertura prevista en caso de que apuntemos con el arma al enemigo. Si la cobertura estimada es mayor que la actual, apuntamos.

Demo 2 puntos:



Si las líneas son verdes, entonces el enemigo cae completamente en el área de puntería. Naranja - parcialmente, rojo - no cae en absoluto.


Pero generalmente apunto no al vientre del enemigo, sino a algún punto más óptimo.


Para una pistola y un rifle de asalto, si el enemigo está en el aire, considero cuántas garrapatas alcanzará la bala en su posición actual, luego tomo de su matriz dodge_trajectories descrita anteriormente todas sus posiciones posibles a través de este número de ticks, promedia las 8 posiciones y, al apuntar, considero que el enemigo está en este punto promedio.


Si se para en el suelo, apunto "a la cabeza", sobre la base de esas consideraciones de que solo puede saltar hacia arriba, de modo que un disparo en la cabeza es mucho más difícil de esquivar que un disparo en el estómago, y aún más, en las piernas.


Para una bazuca, también tomo 8 posiciones posibles a través de tantas garrapatas como el cohete llega al enemigo. Para ellos, construyo un cono (más precisamente, un sector de un círculo) que describe todas estas posiciones y dentro de este cono clasifico las trayectorias con algún paso. Para cada trayectoria, simulo un vuelo de cohete, junto con una explosión en caso de un golpe en la pared. La trayectoria en la que el enemigo tiene menos posibilidades de esquivar, la uso para apuntar.


Incluso en algunos casos, para caer y volar desde la plataforma de salto (es decir, aquellos estados que el enemigo no puede interrumpir), simplemente considero el punto de intercepción por la bala del enemigo, resolviendo el sistema de ecuaciones de movimiento. El código fue copiado de su propio robot de hockey 2014, donde fue utilizado para interceptar el disco. :)


Entre otras cosas, en ciertas situaciones, cancelé el apuntar si hay una posición en mi camino para los próximos 10 ticks en los que el rayo desde el centro de esta posición en la dirección del último ángulo actual se cruza con el enemigo. Esto me permitió apuntar con movimiento, sin cambiar el ángulo y, por lo tanto, sin aumentar la extensión.


Disparando


El disparo básico fue cosido en la trayectoria encontrada y fue puramente situacional. Pero también, cada vez que mi bot intentaba calcular si tiene sentido disparar en este momento, y si lo hizo, disparó. Por ejemplo:


  • si no se garantiza que el enemigo esquive;
  • si la cobertura fue superior a algún valor fijo.

Además, si mi arma era una bazuca, simulaba todo tipo de trayectorias dentro de la propagación en caso de un disparo inmediato, con algún paso. Y si el resultado mostró que el beneficio potencial excede el daño potencial, disparé. Evalué el beneficio mediante 4 métricas en orden descendente de importancia para mí y para el enemigo: muerte garantizada (mía o del enemigo), posible muerte, daño garantizado, posible daño. Si había al menos una trayectoria en la que, por ejemplo, se me garantizaba matarme a mí mismo y solo posiblemente matar al enemigo, no disparaba.


Hubo otros controles. Por ejemplo, podría cancelar un disparo si en el siguiente tic de mi trayectoria la cobertura potencial es mayor que en el actual.


En general, la lógica me dijo que los disparos básicos deberían desactivarse, pero las pruebas mostraron lo contrario. No puedo imaginar por qué.


Minas


En el momento de la final, estaban en su infancia: marcaba cada tic lo que sucedería si ponía 1 o 2 minas en este momento, dependiendo de la salud del oponente, y les disparaba. Si tengo la garantía de matar al enemigo y si es rentable para mí (ya sea en puntos o porque solo el enemigo morirá), lo hice.


Después del año nuevo, volviendo de un viaje, escribí el algoritmo la primera noche, lo que me llevó a 2-4 lugares en la tabla. El algoritmo era simple: simplemente simulé cada tic que sucedería si cambiara inmediatamente al modo berserker y corriera hacia el enemigo para explotarlo con una mina lo antes posible. Para el enemigo, simulé una evasión simple, usando las mismas esquivar_trajectories: tomé tres trayectorias que aumentan la distancia de mí. Por ejemplo, si estaba a la izquierda del enemigo, entonces analizaba tres casos: el enemigo se escapa a la derecha; el enemigo corre hacia la derecha y salta; corre hacia la derecha y salta hacia abajo. Si en los tres casos se me garantizó que tenía tiempo para matarlo con una mina y se le garantizó que no podría poner minas antes que yo, lo hice.


Además, el algoritmo pudo saltar o saltar en el nivel de estrategia de Inicio rápido, simplemente en función de la diferencia en la coordenada Y.


Como resultado, en los mapas finales, casi todas las partidas terminaron en menos de 1000 ticks con autodestrucción de mis dos personajes.


Pacifismo


Al ver cuán efectivas eran mis minas, decidí verificar mi suposición hecha anteriormente en el canal de telegramas de que en la final era posible tomar un lugar en la parte superior sin disparar en absoluto. Así que simplemente tomé y apagué el disparo en mi bot, con la excepción de un disparo en la mina. Lo único es que, para no perder la calificación, este modo se activó solo si tenía suficientes minas para matar al enemigo.


En resumen, mi bot se ha convertido en un pacifista. Para demostrar su paz y humildad, ni siquiera miró al enemigo (ver video). Bueno, a veces disparó minas, piensas. ¡No le disparé a los oponentes! Y si murieron al mismo tiempo, bueno, qué puedes hacer, un trágico accidente.


En general, cargué esta versión en el sitio, creé 4 juegos con cada uno de los 3 principales y ...



... ganó 3 de 4 juegos contra cada uno. En general, es extraño que los vecinos no vinieran a quejarse de mi risa homérica en medio de la noche. :)


Admirate a ti mismo:



Eh, si pasara estas 2-3 horas antes de la final, y no después, tal vez este artículo diría cómo ganar un MacBook, y no cómo evitarlo por todos los medios, quién sabe. Por desgracia, la historia no conoce el modo subjuntivo.


Prueba


En general, probé los cambios tantas veces que el resultado fue estadísticamente significativo. Más precisamente, el límite inferior del intervalo de confianza debe superar el 50%. Para las finales del sandbox, el script seleccionó los coeficientes. En años anteriores probé la genética, pero funcionó mal: para un resultado normal, había que jugar demasiados juegos. Así que esta vez solo cambié un coeficiente a la vez y recuperé 200 juegos. En aquellos casos en que el resultado fue bueno, realicé pruebas adicionales. En resumen, dejó durante la noche dos docenas de coeficientes y tuvo un resultado en la mañana.


En conclusión


De alguna manera, todo funcionó con el dolor por la mitad.


En los últimos días antes del final del sandbox, pasé la mayor parte del tiempo en el segundo lugar en la mesa por un margen significativo desde el tercero, pero unas pocas horas antes del final del sandbox no tuve suerte y comencé una serie de juegos de acuerdo con las reglas de las rondas 1 y 2, mientras que mi estrategia jugó más con las reglas del final. Por ejemplo, aquí de 13 juegos solo hay 1 de acuerdo con las reglas finales. Así que también, mi tasa de victorias en estos juegos salió mucho peor de lo habitual. En general, el todopoderoso al azar:



Como resultado, perdí demasiados juegos, perdí toda mi ventaja y caí de 2 a 4 lugares, y no tuve tiempo de recuperarme, porque el campeonato había terminado. Es bueno que el sandbox no haya perdido el primer lugar en la lista de ganadores.


Una vez más, quiero agradecer a Mail.ru Group por este próximo campeonato maravilloso. , , , ( 1100 ). — , . , . , , , !


, . , , . " 8 ".

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


All Articles