O futuro do fundo
No ano de 2017, você e seu oponente enfrentarão uma batalha futurista em que apenas um poderá sobreviver. Você é experiente o suficiente para derrotar seu oponente? Agora é a hora de aprimorar suas habilidades com armas na sua linguagem de programação favorita e lutar contra todas as probabilidades!
Resultados do Torneio
Este torneio terminou no UTC manhã de Feburary 2 nd , 2017. Graças aos nossos concorrentes, nós tivemos um torneio futurista emocionante!
O MontePlayer é o vencedor final depois de batalhas fechadas com CBetaPlayer e StudiousPlayer. Os três principais duelos de guen tiraram uma foto comemorativa:
MontePlayer - by TheNumberOne
+------------+
CBetaPlayer | | - by George V. Williams
+------------+ # 1 | StudiousPlayer - by H Walters
| +----------------+
| # 2 # 3 |
+------------------------------------------+
The Futurustic Gun Duel @ PPCG.SE 2017
Parabéns aos vencedores! O cabeçalho detalhado é visto no final deste post.
Orientação Geral
- Visite o repositório oficial para obter o código fonte usado neste torneio.
- Entradas em C ++: por favor, herde a
Player
classe. - Entradas não C ++: selecione uma interface na seção Interface para envios não C ++ .
- Linguagens não C ++ atualmente permitidas: Python 3, Java.
O duelo
- Cada jogador começa com uma arma descarregada que pode carregar uma quantidade infinita de munição.
- A cada turno, os jogadores escolherão simultaneamente uma das seguintes ações:
0
- Coloque 1 munição na arma.1
- Disparar uma bala no oponente; custa 1 munição carregada.2
- Disparar um feixe de plasma no oponente; custa 2 munição carregada.-
- Defenda a bala recebida usando um escudo de metal.=
- Defenda o feixe de plasma recebido usando um defletor térmico.
- Se ambos os jogadores sobreviver após a 100 ª vez, ambos de escape para a morte, o que resulta em um empate .
Um jogador perde o duelo de armas se
- Será que não usar a blindagem metálica para defender uma bala de entrada.
- Será que não usar o deflector térmica para defender um plasma de entrada.
- Dispara uma arma sem carregar munição suficiente, na qual a arma explode e mata o proprietário.
Ressalvas
De acordo com o Manual para proprietários de armas futuristas :
- Um escudo de metal NÃO PODE defender do feixe de plasma recebido. Da mesma forma, um defletor térmico NÃO PODE defender da bala recebida.
- O feixe de plasma domina a bala (porque a primeira requer mais munição carregada). Portanto, se um jogador dispara um raio de plasma no oponente que dispara uma bala no mesmo turno, o oponente é morto.
- Se os dois jogadores disparam uma bala um no outro no mesmo turno, as balas são canceladas e os dois sobrevivem. Da mesma forma, se os dois jogadores disparam um feixe de plasma um no outro no mesmo turno, os dois sobrevivem.
Também é digno de nota que:
- Você NÃO conhecerá a ação do seu oponente até que ela termine.
- Desviar os feixes de plasma e balas de proteção NÃO prejudicará seu oponente.
Portanto, há um total de 25 combinações de ações válidas a 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 exemplo
Aqui está um duelo que tive com um amigo. Naquela época, não sabíamos muito sobre programação, então usamos gestos com as mãos e sinalizamos na velocidade de dois turnos por segundo. Da esquerda para a direita, nossas ações foram por sua vez:
Me: 001-000-1201101001----2
Friend: 00-10-=1-==--0100-1---1
De acordo com as regras acima, eu perdi. Você vê o porquê? É porque eu disparei o feixe de plasma final quando eu tinha apenas 1 munição carregada, fazendo com que minha arma explodisse.
O jogador C ++
Você , como programador futurista civilizado, não vai lidar diretamente com as armas. Em vez disso, você codifica um Player
que luta contra os outros. Ao herdar publicamente a classe c ++ no projeto GitHub, você pode começar a escrever sua legenda urbana.
Player.hpp can be found in Tournament\Player.hpp
An example of a derived class can be found in Tournament\CustomPlayer.hpp
O que você deve ou pode fazer
- Você deve herdar a
Player
classe por herança pública e declarar sua classe final. - Você deve substituir
Player::fight
, que retorna um válidoPlayer::Action
toda vez que é chamado. - Opcionalmente, substitua
Player::perceive
ePlayer::declared
fique de olho nas ações de seu oponente e acompanhe suas vitórias. - Opcionalmente, use membros e métodos estáticos privados em sua classe derivada para executar cálculos mais complexos.
- Opcionalmente, use outras bibliotecas padrão do C ++.
O que você NÃO deve fazer
- Você NÃO deve usar nenhum método direto para reconhecer seu oponente que não seja o identificador de oponente fornecido, que é embaralhado no início de cada torneio. Você só pode adivinhar quem é um jogador durante o jogo dentro de um torneio.
- Você NÃO deve substituir nenhum método na
Player
classe que não seja declarado virtual. - Você NÃO deve declarar ou inicializar nada no escopo global.
- Desde a estreia de (agora desclassificado)
BlackHatPlayer
, os jogadores NÃO têm permissão para espiar ou modificar o estado do seu oponente.
Um exemplo de duelo
O processo de um duelo de armas é realizado usando a GunDuel
classe. Para um exemplo de luta, consulte a Source.cpp
seção Iniciando um duelo .
Nós mostrar GunClubPlayer
, HumanPlayer
ea GunDuel
classe, que pode ser encontrado no Tournament\
diretório do repositório.
Em cada duelo, GunClubPlayer
carregará uma bala; demiti-lo; enxague e repita. Durante cada turno, HumanPlayer
você será solicitado a executar uma ação contra seu oponente. Seus controles do teclado são os personagens 0
, 1
, 2
, -
e =
. No Windows, você pode usar HumanPlayer
para depurar seu envio.
Iniciando um duelo
É assim que você pode depurar seu player através do console.
// 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();
}
Jogos de exemplo
A menor quantidade de turnos que você precisa derrotar GunClubPlayer
é 3. Aqui está o replay do jogo 0-1
contra GunClubPlayer
. O número entre parênteses é o número de munição carregada para cada jogador quando o turno termina.
:: 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 . . .
A maneira mais rápida de ser derrotado GunClubPlayer
sem fazer movimentos inválidos é a sequência 0=
, porque a bala dispara através do defletor térmico. A repetição é
:: 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 . . .
O torneio
O torneio segue o formato "Último jogador em pé". Em um torneio, todos os envios válidos (incluindo o GunClubPlayer
) são colocados em um pool. A cada envio é atribuído um identificador aleatório, porém único, que permanecerá o mesmo durante todo o torneio. Durante cada rodada:
- Cada envio começa com 0 pontos e jogará 100 duelos contra todos os outros pedidos.
- Cada duelo vitorioso concederá 1 ponto; desenhar e perder dá 0 pontos.
- No final da rodada, as inscrições com o mínimo de pontos deixam o torneio. Em caso de empate, o jogador com a menor quantidade de pontos ganhos desde o início do torneio sairá.
- Se restar mais de um jogador, a próxima rodada começará.
- Os pontos NÃO são transferidos para a próxima rodada.
Submissão, obediência, inscrição, candidatura
Você enviará um jogador por resposta. Você pode enviar vários arquivos para um jogador, desde que NÃO interfira com outros envios. Para manter as coisas fluindo, por favor:
- Nomeie seu arquivo de cabeçalho principal como
<Custom>Player.hpp
, - Nomeie seus outros arquivos como
<Custom>Player*.*
, por exemplo,MyLittlePlayer.txt
se o nome da sua classe éMyLittlePlayer
ouEmoPlayerHates.cpp
se o nome da sua classe éEmoPlayer
. - Se o seu nome contiver
Shooter
ou palavras semelhantes que se encaixem no contexto deste torneio, você não precisará adicionarPlayer
no final. Se você acha que o nome do seu envio funciona melhor sem o sufixoPlayer
, também não é necessário adicioná-loPlayer
. - Verifique se o seu código pode ser compilado e vinculado no Windows.
Você pode comentar para pedir esclarecimentos ou identificar brechas. Espero que você goste deste duelo futurista de armas e deseje um feliz ano novo!
Esclarecimento
- Você tem permissão para ter um comportamento aleatório.
- Ações inválidas (disparar quando munição carregada não são suficientes) são permitidas.
- Se um jogador fizer uma entrada inválida, sua arma explodirá imediatamente.
- Você tem permissão para estudar as respostas.
- Você tem permissão explícita para registrar o comportamento do oponente em cada torneio.
- Cada rodada, você jogará 100 duelos contra cada oponente; a ordem dos 100 duelos, no entanto, é aleatória - você não tem garantia de lutar contra o mesmo oponente por 100 duelos consecutivos.
Recursos adicionais
O @flawr traduziu a fonte C ++ fornecida para Java como uma referência, se você quiser enviar entradas C ++.
Interface para envios não C ++
Atualmente aceito: Python 3, Java.
Siga uma das especificações abaixo:
Especificação da interface 1: código de saída
Seu envio será executado uma 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.
Você pode testar sua submissão PythonPlayer\
e JavaPlayer\
diretórios.
Especificação da interface 2: stdin / stdout
(Crédito para H Walters)
Sua inscrição será realizada uma vez por torneio.
Há um requisito fixo para todas as entradas sobre como fazer E / S, já que stdin e stdout estão conectados ao driver do torneio. Violar isso pode levar a um impasse. Todas as entradas devem seguir este algoritmo EXATO (em pseudo-có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
Aqui, F é um dos 0
, 1
, 2
, -
, ou =
para load / bullet / plasma / metal / thermal
. PROCESSO significa responder opcionalmente ao que o seu oponente fez (incluindo rastrear a munição do seu oponente, se você estiver fazendo isso). Observe que a ação do oponente também é '0', '1', '2', '-' ou '=' e está no segundo caractere.
Painel 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 |
O torneio durará até 1º de fevereiro de 2017, salvo indicação em contrário.
fonte
Player
implementação que invoque outro processo para calcular a curva atual. Isso permitiria que as pessoas participassem de qualquer idioma que você tenha prazer em executar em sua máquina.Player::fight
" / "você pode herdarPlayer::perceive
" ... nos dois casos, o termo é substituído , não herdado .GunDuel.hpp
, tantovalidA
evalidB
use #actionA
Respostas:
MontePlayer
Este jogador usa o algoritmo de pesquisa de árvore desacoplada do Monte Carlo UCT para decidir quais escolhas ele deve fazer. Ele rastreia o que o inimigo faz para prever suas ações. Ele simula o inimigo como ele mesmo, se não houver dados.
Este bot se sai muito bem contra todos os outros bot, exceto cβ. Em uma partida de 10000 duelos contra cβ, Monte venceu 5246 duelos. Com um pouco de matemática, isso significa que Monte vencerá um duelo contra cβ 51,17% a 53,74% das vezes (99% de confiança).
fonte
Agora: tenho quase certeza de que isso deve ser desqualificado imediatamente, mas é engraçado não estar violando explicitamente nenhuma das regras acima:
BlackHat não tenta reconhecer o oponente - na verdade, é completamente irrelevante quem é o oponente, já que seu cérebro é substituído imediatamente.
Tudo acontece localmente na
fight
função virtual.fonte
#ifdef __BLACKHAT_PLAYER_HPP__
O que você#error "Dependency issue; to compile, please include this file before BlackHatPlayer.hpp"
precisa#else
saber#define __BLACKHAT_PLAYER_HPP__
é o#endif
seguinte#pragma once
;-) #Em seguida, a mais temida de todas as criaturas, foi ao inferno e lutou com literalmente 900000 outros bots , seus ...
O BotRobot foi nomeado, treinado e construído automaticamente por um algoritmo genético muito básico.
Duas equipes de 9 foram montadas uma contra a outra, em cada geração, cada robô da equipe 1 é colocado contra cada robô da equipe 2. Os robôs com mais vitórias do que perdas, mantiveram a memória e o outro voltaram ao último passo , e teve a chance de esquecer algo, espero que ruim. Os bots em si são tabelas de pesquisa glorificadas, onde se encontrassem algo que não tinham visto antes, simplesmente escolheriam uma opção válida aleatória e a salvariam na memória. A versão C ++ não faz isso, deveria ter aprendido . Como dito anteriormente, os bots vencedores mantêm essa nova memória encontrada, como claramente funcionou. Perder bots não, e manter o que eles começaram.
No final, as brigas de bot foram bastante próximas, raramente obsoletas. O vencedor foi escolhido a partir de uma piscina das duas equipes postar evolução, que foi 100000 gerações.
O BotRobot, com seu nome gerado aleatoriamente e BONITO , foi o sortudo.
Gerador
bot.lua
Revisão: Embora o robô fosse bastante inteligente contra ele e outros robôs gerados de maneira semelhante, ele se mostrou bastante inútil em batalhas reais. Então, eu regenerei seu cérebro contra alguns dos bots já criados.
Os resultados, como podem ser facilmente vistos, são um cérebro muito mais complexo, com opções para o jogador inimigo ter 12 munições.
Eu não tenho certeza do que ele estava lutando que chegou a 12 munições, mas algo aconteceu.
E, claro, o produto acabado ...
Eu odeio C ++ agora ...
fonte
00
.fonte
GetRandomDouble
, você pode remover o argumento max.Não tenho o comentário certo em todos os lugares, por isso ainda não posso fazer minhas perguntas. Portanto, este é um jogador muito básico para vencer o primeiro bot.
[Editar] Obrigado, agora o status anterior não é mais verdadeiro, mas acho que é melhor mantê-lo para que possamos entender o contexto desse bot.
O oportunista frequenta o mesmo clube de armas que os GunClubPlayers, no entanto, ele apostou para um novato que poderia vencer todos os GunClubPlayers. Então, ele explora o hábito que há muito percebe e se força a não atirar, mas espera um pouco para vencer.
fonte
Alterações mais recentes:
Números aleatórios aprimorados (obrigado Frenzy Li).
fonte
getAmmoOpponent
nãogetOpponentAmmo
. Você também está perdendo#endif // !__BARRICADE_PLAYER_HPP__
Observe que isso rastreia informações sobre os oponentes de acordo com as regras do desafio; consulte o método "Meyers style singleton" com escopo "storedLs ()" na parte inferior. (Algumas pessoas estavam se perguntando como fazer isso; agora você sabe!)
fonte
É
GunClubPlayer
como ir ao clube das armas. Durante cada duelo, eles primeiro carregavam munição, depois disparavam uma bala e repetiam esse processo até o final do duelomundial. Na verdade, eles não se importam se vencem ou não e se concentram exclusivamente em ter uma experiência agradável.fonte
fonte
fonte
Este bot não é particularmente bom - no entanto, todo KOTH precisa de algumas entradas iniciais para executá-lo :)
Testes locais descobriram que este ganha tanto contra
GunClubPlayer
eOpportunist
100% do tempo. Uma batalha contraBotRobotPlayer
parecia sempre resultar em empate, pois ambos se escondiam atrás de seus escudos.fonte
Como não codifico em c ++, quaisquer melhorias no código serão bem-vindas.
fonte
DeceptivePlayer
seja um nome melhor?HanSoloPlayer
Atira primeiro! Ainda estou trabalhando na revisão, mas isso é muito bom.
fonte
fonte
#endif // ! __CAMTO_HPP__
<>&
é uma dor.using namespace std
porque interfere com o torneio. Se você deseja depurar, você pode usarstd::cout
etc.fonte
... porque eu gostaria de ver como um jogador aleatório classifica.
fonte
SpecificPlayer
O SpecificPlayer segue um plano simples de escolher algumas ações aleatórias (válidas). No entanto, sua principal característica é que ele procura determinadas situações, analisando a contagem de munição e o movimento anterior do oponente.
Esta é minha primeira vez escrevendo algo em C ++ e tentando fazer qualquer tipo de escrita competitiva de bot. Então, espero que minha escassa tentativa faça pelo menos algo interessante. :)
fonte
NotSoPatientPlayer
fonte