El futuro de fondo
En el año 2017, usted y su oponente se enfrentarán en un tiroteo futurista donde solo uno puede sobrevivir. ¿ Tienes suficiente experiencia para derrotar a tu oponente? ¡Ahora es el momento de pulir tus habilidades con las armas en tu lenguaje de programación favorito y luchar contra todo pronóstico!
Resultados del torneo
Este torneo terminó en la mañana del UTC de Feburary 2 nd , 2017. Gracias a nuestros concursantes, hemos tenido un emocionante torneo futurista!
MontePlayer es el ganador final después de una estrecha batalla con CBetaPlayer y StudiousPlayer. Los tres mejores duendes de guen han tomado una fotografía conmemorativa:
MontePlayer - by TheNumberOne
+------------+
CBetaPlayer | | - by George V. Williams
+------------+ # 1 | StudiousPlayer - by H Walters
| +----------------+
| # 2 # 3 |
+------------------------------------------+
The Futurustic Gun Duel @ PPCG.SE 2017
¡Felicitaciones a los ganadores! La tabla de clasificación detallada se ve cerca del final de esta publicación.
Orientación general
- Visita el repositorio oficial del código fuente utilizado en este torneo.
- Entradas de C ++: herede la
Player
clase. - Entradas que no son de C ++: seleccione una interfaz en la sección Interfaz para envíos que no sean de C ++ .
- Lenguajes actualmente no permitidos en C ++: Python 3, Java.
El duelo
- Cada jugador comienza con un arma descargada que puede cargar una cantidad infinita de munición.
- Cada turno, los jugadores elegirán simultáneamente una de las siguientes acciones:
0
- Cargue 1 munición en la pistola.1
- Dispara una bala al oponente; cuesta 1 munición cargada.2
- Dispara un rayo de plasma al oponente; cuesta 2 municiones cargadas.-
- Defiende la bala entrante con un escudo de metal.=
- Defienda el haz de plasma entrante con un deflector térmico.
- Si ambos jugadores sobreviven después de la 100 ª vez, ambos de escape a la muerte, lo que resulta en un empate .
Un jugador pierde el duelo armado si
- Did NO usar el escudo de metal que defender una bala entrante.
- Did NO utilizar el deflector térmico para defender un plasma entrante.
- Dispara un arma sin cargar suficiente munición, en la cual su arma explotará y matará al propietario.
Advertencias
De acuerdo con el Manual para propietarios de armas futuristas :
- Un escudo de metal NO PUEDE defenderse del haz de plasma entrante. Del mismo modo, un deflector térmico NO PUEDE defenderse de la bala entrante.
- El rayo de plasma domina la bala (porque el primero requiere más munición cargada). Por lo tanto, si un jugador dispara un rayo de plasma al oponente que dispara una bala en el mismo turno, el oponente es asesinado.
- Si ambos jugadores se disparan una a la otra en el mismo turno, las balas se cancelan y ambos jugadores sobreviven. Del mismo modo, si ambos jugadores se disparan un rayo de plasma el uno al otro en el mismo turno, ambos jugadores sobreviven.
También es digno de mención que:
- Usted no sabe la acción de su oponente en una vuelta hasta que se termina.
- Desviar los rayos de plasma y las balas de protección NO dañará a tu oponente.
Por lo tanto, hay un total de 25 combinaciones de acción válidas cada turno:
+-------------+---------------------------------------------+
| Outcome | P L A Y E R B |
| Table +--------+-----------------+------------------+
| for Players | Load | Bullet Plasma | Metal Thermal |
+---+---------+--------+--------+--------+--------+---------+
| P | Load | | B wins | B wins | | |
| L +---------+--------+--------+--------+--------+---------+
| A | Bullet | A wins | | B wins | | A wins |
| Y | +--------+--------+--------+--------+---------+
| E | Plasma | A wins | A wins | | A wins | |
| R +---------+--------+--------+--------+--------+---------+
| | Metal | | | B wins | | |
| | +--------+--------+--------+--------+---------+
| A | Thermal | | B wins | | | |
+---+---------+--------+--------+---------------------------+
Note: Blank cells indicate that both players survive to the next turn.
Duelo de ejemplo
Aquí hay un duelo que tuve una vez con un amigo. En aquel entonces, no sabíamos mucho sobre programación, así que usamos gestos con las manos y señalábamos a la velocidad de dos vueltas por segundo. De izquierda a derecha, nuestras acciones fueron a su vez:
Me: 001-000-1201101001----2
Friend: 00-10-=1-==--0100-1---1
Según las reglas anteriores, perdí. ¿Ves por qué? Es porque disparé el rayo de plasma final cuando solo tenía 1 munición cargada, haciendo que mi arma explotara.
El reproductor de C ++
Usted , como programador futurista civilizado, no manejará directamente las armas. En cambio, codificas una Player
que lucha contra los demás. Al heredar públicamente la clase c ++ en el proyecto GitHub, puede comenzar a escribir su leyenda urbana.
Player.hpp can be found in Tournament\Player.hpp
An example of a derived class can be found in Tournament\CustomPlayer.hpp
Lo que debes o puedes hacer
- Debe heredar la
Player
clase a través de la herencia pública y declarar su clase final. - Debe anular
Player::fight
, lo que devuelve un valor válidoPlayer::Action
cada vez que se llama. - Opcionalmente, anula
Player::perceive
y vigilaPlayer::declared
las acciones de tu oponente y realiza un seguimiento de tus victorias. - Opcionalmente, use miembros y métodos estáticos privados en su clase derivada para realizar cálculos más complejos.
- Opcionalmente, use otras bibliotecas estándar de C ++.
Lo que NO debes hacer
- NO debes usar ningún método directo para reconocer a tu oponente que no sea el identificador de oponente dado, que se baraja al comienzo de cada torneo. Solo puedes adivinar quién es un jugador a través de su juego dentro de un torneo.
- NO debe anular ningún método en la
Player
clase que no se declare virtual. - NO debe declarar ni inicializar nada en el ámbito global.
- Desde el debut de (ahora descalificado)
BlackHatPlayer
, los jugadores NO pueden mirar o modificar el estado de tu oponente.
Un ejemplo de duelo
El proceso de un duelo de armas se realiza utilizando la GunDuel
clase. Para ver un ejemplo de pelea, mira Source.cpp
en la sección Iniciando un duelo .
Mostramos GunClubPlayer
, HumanPlayer
y la GunDuel
clase, que se puede encontrar en el Tournament\
directorio del repositorio.
En cada duelo, GunClubPlayer
cargará una bala; dispararlo; enjuague y repita. Durante cada turno, HumanPlayer
te pedirá una acción para jugar contra tu oponente. Sus controles de teclado son los personajes 0
, 1
, 2
, -
y =
. En Windows, puede usar HumanPlayer
para depurar su envío.
Iniciando un duelo
Así es como puedes depurar tu reproductor a través de la consola.
// Source.cpp
// An example duel between a HumanPlayer and GunClubPlayer.
#include "HumanPlayer.hpp"
#include "GunClubPlayer.hpp"
#include "GunDuel.hpp"
int main()
{
// Total number of turns per duel.
size_t duelLength = 100;
// Player identifier 1: HumanPlayer.
HumanPlayer human(2);
// Player identifier 2: GunClubPlayer.
GunClubPlayer gunClub(1);
// Prepares a duel.
GunDuel duel(human, gunClub, duelLength);
// Start a duel.
duel.fight();
}
Juegos de ejemplo
La menor cantidad de turnos que necesitas para derrotar GunClubPlayer
es 3. Aquí está la repetición de jugar 0-1
contra GunClubPlayer
. El número en la parántesis es el número de munición cargada para cada jugador cuando finaliza el turno.
:: Turn 0
You [0/12/-=] >> [0] load ammo (1 ammo)
Opponent selects [0] load ammo (1 ammo)
:: Turn 1
You [0/12/-=] >> [-] defend using metal shield (1 ammo)
Opponent selects [1] fire a bullet (0 ammo)
:: Turn 2
You [0/12/-=] >> [1] fire a bullet (0 ammo)
Opponent selects [0] load ammo (1 ammo)
:: You won after 3 turns!
:: Replay
YOU 0-1
FOE 010
Press any key to continue . . .
La forma más rápida de ser derrotado GunClubPlayer
sin hacer movimientos inválidos es la secuencia 0=
, porque la bala dispara a través del deflector térmico. La repetición es
:: Turn 0
You [0/12/-=] >> [0] load ammo (1 ammo)
Opponent selects [0] load ammo (1 ammo)
:: Turn 1
You [0/12/-=] >> [=] defend using thermal deflector (1 ammo)
Opponent selects [1] fire a bullet (0 ammo)
:: You lost after 2 turns!
:: Replay
YOU 0=
FOE 01
Press any key to continue . . .
El torneo
El torneo sigue el formato "Último jugador en pie". En un torneo, todos los envíos válidos (incluido el GunClubPlayer
) se colocan en un grupo. A cada envío se le asigna un identificador aleatorio pero único que permanecerá igual durante todo el torneo. Durante cada ronda:
- Cada envío comienza con 0 puntos y jugará 100 duelos contra cualquier otro envío.
- Cada duelo victorioso otorgará 1 punto; dibujar y perder dan 0 puntos.
- Al final de la ronda, los envíos con los puntos mínimos abandonan el torneo. En caso de empate, el jugador con la menor cantidad de puntos ganados desde el comienzo del torneo se irá.
- Si queda más de un jugador, comenzará la siguiente ronda.
- Los puntos NO se transfieren a la siguiente ronda.
Sumisión
Enviarás un jugador por respuesta. Puede enviar varios archivos para un jugador, siempre que NO interfieran con otras presentaciones. Para mantener las cosas fluyendo, por favor:
- Nombre su archivo de encabezado principal como
<Custom>Player.hpp
, - Nombre sus otros archivos como
<Custom>Player*.*
, por ejemplo,MyLittlePlayer.txt
si el nombre de su clase esMyLittlePlayer
, oEmoPlayerHates.cpp
si su nombre de clase esEmoPlayer
. - Si su nombre contiene
Shooter
o palabras similares que se ajustan al contexto de este torneo, no necesita agregarPlayer
al final. Si cree firmemente que su nombre de envío funciona mejor sin el sufijoPlayer
, tampoco necesita agregarloPlayer
. - Asegúrese de que su código se pueda compilar y vincular en Windows.
Puede comentar para pedir una aclaración o para detectar lagunas. ¡Espero que disfrutes de este duelo de armas futurista y te deseo un feliz año nuevo!
Aclaración
- Se le permite tener un comportamiento aleatorio.
- Se permiten acciones no válidas (disparar cuando la munición cargada no es suficiente).
- Si un jugador realiza una entrada no válida, su arma explotará inmediatamente.
- Puedes estudiar las respuestas.
- Se le permite explícitamente registrar el comportamiento del oponente dentro de cada torneo.
- Cada ronda, jugarás 100 duelos contra cada oponente; Sin embargo, el orden de los 100 duelos es aleatorio: no se garantiza que pelees con el mismo oponente 100 duelos seguidos.
Recursos adicionales
@flawr ha traducido la fuente de C ++ proporcionada a Java como referencia si desea enviar entradas de C ++.
Interfaz para envíos no C ++
Actualmente aceptado: Python 3, Java.
Siga una de las especificaciones a continuación:
Especificación de interfaz 1: código de salida
Su envío se ejecutará una vez por turno.
Expected Command Line Argument Format:
<opponent-id> <turn> <status> <ammo> <ammo-opponent> <history> <history-opponent>
Expected Return Code: The ASCII value of a valid action character.
'0' = 48, '1' = 49, '2' = 50, '-' = 45, '=' = 61
<opponent-id> is an integer in [0, N), where N is size of tournament.
<turn> is 0-based.
If duel is in progress, <status> is 3.
If duel is draw / won / lost, <status> is 0 / 1 / 2.
<history> and <history-opponent> are strings of actions, e.g. 002 0-=
If turn is 0, <history> and <history-opponent> are not provided.
You can ignore arguments you don't particularly need.
Puede probar su presentación en PythonPlayer\
y JavaPlayer\
directorios.
Especificación de interfaz 2: stdin / stdout
(Crédito a H Walters)
Tu envío se ejecutará una vez por torneo.
Hay un requisito fijo para todas las entradas sobre cómo hacer E / S, ya que tanto stdin como stdout están conectados al controlador del torneo. Violar esto podría llevar a un punto muerto. Todas las entradas DEBEN seguir este algoritmo EXACTO (en pseudocódigo):
LOOP FOREVER
READ LINE INTO L
IF (LEFT(L,1) == 'I')
INITIALIZE ROUND
// i.e., set your/opponent ammo to 0, if tracking them
// Note: The entire line at this point is a unique id per opponent;
// optionally track this as well.
CONTINUE LOOP
ELSE IF (LEFT(L,1) == 'F')
WRITELN F // where F is your move
ELSE IF (LEFT(L,1) == 'P')
PROCESS MID(L,2,1) // optionally perceive your opponent's action.
END IF
CONTINUE LOOP
QUIT
Aquí, M es uno de 0
, 1
, 2
, -
, o =
para load / bullet / plasma / metal / thermal
. PROCESO significa responder opcionalmente a lo que hizo tu oponente (incluido el seguimiento de la munición de tu oponente si estás haciendo esto). Tenga en cuenta que la acción del oponente también es una de '0', '1', '2', '-' o '=', y está en el segundo carácter.
Marcador final
08:02 AM Tuesday, February 2, 2017 Coordinated Universal Time (UTC)
| Player | Language | Points | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|:------------------ |:---------- | ------:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:| -----:|
| MontePlayer | C++ | 11413 | 1415 | 1326 | 1247 | 1106 | 1049 | 942 | 845 | 754 | 685 | 555 | 482 | 381 | 287 | 163 | 115 | 61 |
| CBetaPlayer | C++ | 7014 | 855 | 755 | 706 | 683 | 611 | 593 | 513 | 470 | 414 | 371 | 309 | 251 | 192 | 143 | 109 | 39 |
| StudiousPlayer | C++ | 10014 | 1324 | 1233 | 1125 | 1015 | 907 | 843 | 763 | 635 | 555 | 478 | 403 | 300 | 201 | 156 | 76 |
| FatedPlayer | C++ | 6222 | 745 | 683 | 621 | 655 | 605 | 508 | 494 | 456 | 395 | 317 | 241 | 197 | 167 | 138 |
| HanSoloPlayer | C++ | 5524 | 748 | 668 | 584 | 523 | 490 | 477 | 455 | 403 | 335 | 293 | 209 | 186 | 153 |
| SurvivorPlayer | C++ | 5384 | 769 | 790 | 667 | 574 | 465 | 402 | 354 | 338 | 294 | 290 | 256 | 185 |
| SpecificPlayer | C++ | 5316 | 845 | 752 | 669 | 559 | 488 | 427 | 387 | 386 | 340 | 263 | 200 |
| DeceptivePlayer | C++ | 4187 | 559 | 445 | 464 | 474 | 462 | 442 | 438 | 369 | 301 | 233 |
| NotSoPatientPlayer | C++ | 5105 | 931 | 832 | 742 | 626 | 515 | 469 | 352 | 357 | 281 |
| BarricadePlayer | C++ | 4171 | 661 | 677 | 614 | 567 | 527 | 415 | 378 | 332 |
| BotRobotPlayer | C++ | 3381 | 607 | 510 | 523 | 499 | 496 | 425 | 321 |
| SadisticShooter | C++ | 3826 | 905 | 780 | 686 | 590 | 475 | 390 |
| TurtlePlayer | C++ | 3047 | 754 | 722 | 608 | 539 | 424 |
| CamtoPlayer | C++ | 2308 | 725 | 641 | 537 | 405 |
| OpportunistPlayer | C++ | 1173 | 426 | 420 | 327 |
| GunClubPlayer | C++ | 888 | 500 | 388 |
| PlasmaPlayer | C++ | 399 | 399 |
El torneo durará hasta el 1 de febrero de 2017 a menos que se indique lo contrario.
fuente
Player
implementación que invoque otro proceso para calcular el turno actual. Eso permitiría a las personas participar en cualquier idioma que esté dispuesto a ejecutar en su máquina.Player::fight
" / "puede heredarPlayer::perceive
" ... en ambos casos, el término es anular , no heredar .GunDuel.hpp
, ambosvalidA
yvalidB
usoactionA
Respuestas:
MontePlayer
Este jugador utiliza el algoritmo de búsqueda de árbol de Monte Carlo desacoplado UCT para decidir qué elecciones debe hacer. Realiza un seguimiento de lo que hace el enemigo para predecir sus acciones. Simula al enemigo como sí mismo si carece de datos.
Este bot funciona muy bien contra cualquier otro bot excepto cβ. En un duelo de 10000 contra cβ, Monte ganó 5246 duelos. Con un poco de matemática, eso significa que Monte ganará un duelo contra cβ 51.17% al 53.74% del tiempo (99% de confianza).
fuente
Ahora: estoy casi seguro de que esto debería ser descalificado de inmediato, pero es curioso que no estoy violando explícitamente ninguna de las reglas mencionadas anteriormente:
BlackHat no trata de reconocer al oponente; en realidad, es completamente irrelevante quién es el oponente, dado que su cerebro es reemplazado de inmediato.
Todo sucede localmente a la
fight
función virtual.fuente
#ifdef __BLACKHAT_PLAYER_HPP__
␊#error "Dependency issue; to compile, please include this file before BlackHatPlayer.hpp"
␊#else
␊#define __BLACKHAT_PLAYER_HPP__
␊#endif
␊#pragma once
;-)Luego, la más temida de todas las criaturas, ha estado en el infierno y de regreso y ha luchado con literalmente 900000 otros robots , es ...
BotRobot fue nombrado, entrenado y construido automáticamente por un algoritmo genético muy básico.
Dos equipos de 9 se formaron uno contra el otro, en cada generación, cada robot del equipo 1 se enfrenta a cada robot del equipo 2. Los robots con más victorias que pérdidas, mantuvieron su memoria, el otro, volvieron al último paso , y tuve la oportunidad de olvidar algo, espero que sea malo. Los bots mismos son tablas de búsqueda glorificadas, donde si encuentran algo que no habían visto antes, simplemente elegirían una opción válida aleatoria y la guardarían en la memoria. La versión C ++ no hace esto, debería haberlo aprendido . Como se dijo anteriormente, los bots ganadores mantienen esta nueva memoria encontrada, ya que claramente funcionó. Los bots que pierden no lo hacen, y mantienen lo que comenzaron.
Al final, las peleas de bots fueron bastante cercanas, rara vez estancadas. El ganador fue elegido de un grupo de los dos equipos después de la evolución, que fue de 100000 generaciones.
BotRobot, con su nombre HERMOSO y generado aleatoriamente , fue el afortunado.
Generador
bot.lua
Revisión: Aunque el robot era bastante inteligente contra sí mismo y otros robots generados de manera similar, demostró ser bastante inútil en las batallas reales. Entonces, regenere su cerebro contra algunos de los bots ya creados.
El resultado, como se puede ver fácilmente, es un cerebro mucho más complejo, con opciones para que el jugador enemigo tenga 12 municiones.
No estoy seguro de contra qué estaba luchando, eso consiguió hasta 12 municiones, pero algo lo hizo.
Y, por supuesto, el producto terminado ...
Me gusta C ++ ahora ...
fuente
00
.fuente
GetRandomDouble
, puede eliminar el argumento max.Me falta el comentario en todas partes, así que todavía no puedo hacer mis preguntas. Así que este es un jugador muy básico para ganar contra el primer bot.
[Editar] Gracias, ahora el estado anterior ya no es cierto, pero creo que es mejor mantenerlo para que podamos entender el contexto de este bot.
El oportunista frecuenta el mismo club de armas que los GunClubPlayers, sin embargo, apostó a un recién llegado que podía vencer a todos los GunClubPlayers. Así que explota el hábito que ha notado por mucho tiempo y se obliga a no disparar, sino que espera un poco para ganar.
fuente
Cambios más recientes:
Números aleatorios mejorados (gracias Frenzy Li).
fuente
getAmmoOpponent
nogetOpponentAmmo
. También te estás perdiendo#endif // !__BARRICADE_PLAYER_HPP__
Tenga en cuenta que esto rastrea la información sobre los oponentes de acuerdo con las reglas del desafío; vea el método "singleton de estilo Meyers" "scopedLs ()" en la parte inferior. (Algunas personas se preguntaban cómo hacer esto; ¡ahora lo sabes!)
fuente
A los
GunClubPlayer
s les gusta ir al club de armas. Durante cada duelo, primero cargarían munición, luego dispararían una bala y repetirían este proceso hasta el final del duelomundial. En realidad no les importa si ganan o no, y se centran exclusivamente en tener una experiencia agradable ellos mismos.fuente
fuente
fuente
Este bot no es particularmente bueno, sin embargo, cada KOTH necesita algunas entradas iniciales para que funcione :)
Las pruebas locales encontraron que esto gana contra ambos
GunClubPlayer
y elOpportunist
100% del tiempo. Una batalla en contraBotRobotPlayer
siempre parecía resultar en un empate ya que ambos se esconden detrás de sus escudos.fuente
No codifico en c ++, por lo que cualquier mejora en el código será bienvenida.
fuente
DeceptivePlayer
sea un nombre mejor?HanSoloPlayer
¡Dispara primero! Todavía estoy trabajando en revisarlo, pero esto es bastante bueno.
fuente
fuente
#endif // ! __CAMTO_HPP__
<>&
es un dolor.using namespace std
porque interfiere con el torneo. Si desea depurar, puede usarstd::cout
etc.fuente
... porque me gustaría ver cómo se clasifica un jugador aleatorio.
fuente
Jugador específico
SpecificPlayer sigue un plan simple de elegir algunas acciones aleatorias (válidas). Sin embargo, su característica principal es que busca ciertas situaciones analizando los recuentos de munición y el movimiento anterior del oponente.
Esta es la primera vez que escribo algo en C ++, y la primera vez que intento hacer algún tipo de escritura competitiva de bot. Así que espero que mi exiguo intento al menos haga algo interesante. :)
fuente
NotSoPatientPlayer
fuente