Le futur de fond
En 2017, vous et votre adversaire s'affronterez dans une bataille d'armes à feu futuriste où un seul d'entre eux pourrait survivre. Êtes- vous assez expérimenté pour vaincre votre adversaire? Il est maintenant temps de perfectionner vos compétences en armes à feu dans votre langage de programmation préféré et de vous battre contre toute attente!
Résultats du tournoi
Ce tournoi terminé à l'UTC matin du Feburary 2 e 2017. Merci à nos concurrents, nous avons eu un tournoi futuriste passionnant!
MontePlayer est le vainqueur final après une bataille serrée avec CBetaPlayer et StudiousPlayer. Les trois meilleurs duels de guen ont pris une photo commémorative:
MontePlayer - by TheNumberOne
+------------+
CBetaPlayer | | - by George V. Williams
+------------+ # 1 | StudiousPlayer - by H Walters
| +----------------+
| # 2 # 3 |
+------------------------------------------+
The Futurustic Gun Duel @ PPCG.SE 2017
Félicitations aux gagnants! Classement détaillé est vu vers la fin de ce post.
Orientation générale
- Visitez le dépôt officiel pour le code source utilisé dans ce tournoi.
- Entrées C ++: veuillez hériter de la
Player
classe. - Entrées non C ++: sélectionnez une interface dans la section Interface pour les soumissions non C ++ .
- Langages actuellement non autorisés: Python 3, Java.
Le duel
- Chaque joueur commence avec un pistolet non chargé pouvant charger une quantité infinie de munitions.
- À chaque tour, les joueurs choisiront simultanément l'une des actions suivantes:
0
- Charge 1 munition dans le pistolet.1
- tirer une balle sur l'adversaire; coûte 1 munitions chargées.2
- tirer un faisceau de plasma sur l'adversaire; coûte 2 munitions chargées.-
- Défendre la balle en utilisant un bouclier métallique.=
- Défendre le faisceau de plasma entrant à l'aide d'un déflecteur thermique.
- Si les deux joueurs survivent après le 100 e tour, ils s'épuisent à la mort, ce qui entraîne un match nul .
Un joueur perd le duel lorsqu’il
- N'a PAS utilisé le bouclier métallique pour défendre une balle entrante.
- N'a PAS utilisé le déflecteur thermique pour défendre un plasma entrant.
- Tirez avec une arme à feu sans charger assez de munitions, dans laquelle leur arme explose et tue le propriétaire.
Mises en garde
Selon le Manuel du propriétaire d'armes futuriste :
- Un bouclier métallique NE PEUT PAS se défendre du faisceau de plasma entrant. De même, un déflecteur thermique NE PEUT PAS se défendre contre les balles entrantes.
- Le faisceau de plasma domine la balle (car la première nécessite des munitions plus chargées). Par conséquent, si un joueur tire un faisceau de plasma sur l'adversaire qui tire une balle dans le même tour, l'adversaire est tué.
- Si les deux joueurs se tirent une balle dans le même tour, les balles s'annulent et les deux joueurs survivent. De même, si les deux joueurs se lancent un faisceau de plasma au cours du même tour, ils survivent.
Il convient également de noter que:
- Vous ne connaîtrez PAS l'action de votre adversaire dans un tour jusqu'à la fin.
- La déviation des faisceaux de plasma et des balles de protection ne nuira pas à votre adversaire.
Par conséquent, il y a un total de 25 combinaisons d'actions valides à chaque tour:
+-------------+---------------------------------------------+
| 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.
Exemple de duel
Voici un duel que j'ai eu une fois avec un ami. À l'époque, nous ne savions pas grand chose à propos de la programmation, nous avons donc utilisé des gestes de la main et signalé à la vitesse de deux tours par seconde. De gauche à droite, nos actions ont été à leur tour:
Me: 001-000-1201101001----2
Friend: 00-10-=1-==--0100-1---1
Selon les règles ci-dessus, j'ai perdu. Tu vois pourquoi? C'est parce que j'ai tiré le dernier faisceau de plasma alors que je n'avais qu'une cartouche chargée, ce qui a fait exploser mon arme.
Le lecteur C ++
En tant que programmeur futuriste civilisé, vous ne manipulerez pas directement les armes. Au lieu de cela, vous codez un Player
qui se bat contre les autres. En héritant publiquement de la classe c ++ du projet GitHub, vous pouvez commencer à écrire votre légende urbaine.
Player.hpp can be found in Tournament\Player.hpp
An example of a derived class can be found in Tournament\CustomPlayer.hpp
Ce que vous devez ou pouvez faire
- Vous devez hériter d'une
Player
classe par héritage public et déclarer votre classe finale. - Vous devez remplacer
Player::fight
, ce qui retourne une valeur valide àPlayer::Action
chaque appel. - Vous pouvez éventuellement passer outre
Player::perceive
,Player::declared
surveiller les actions de votre adversaire et garder trace de vos victoires. - Utilisez éventuellement des membres et des méthodes statiques privés dans votre classe dérivée pour effectuer des calculs plus complexes.
- Utilisez éventuellement d'autres bibliothèques standard C ++.
Ce que vous ne devez PAS faire
- Vous ne devez PAS utiliser une méthode directe pour reconnaître votre adversaire autre que l'identifiant donné de l'adversaire, qui est mélangé au début de chaque tournoi. Vous êtes seulement autorisé à deviner qui est un joueur grâce à son jeu dans un tournoi.
- Vous ne devez PAS écraser les méthodes de la
Player
classe qui ne sont pas déclarées virtuelles. - Vous devez pas déclarer ou initialiser quoi que ce soit dans la portée globale.
- Depuis le début de (maintenant disqualifié)
BlackHatPlayer
, les joueurs ne sont PAS autorisés à jeter un coup d'œil ou à modifier l'état de votre adversaire.
Un exemple de duel
Le processus d'un duel d'armes à feu est effectué en utilisant la GunDuel
classe. Pour un exemple de combat, voir la Source.cpp
section Initiation d’un duel .
Nous présentons GunClubPlayer
, HumanPlayer
et la GunDuel
classe, qui peuvent être trouvés dans le Tournament\
répertoire du référentiel.
Dans chaque duel, GunClubPlayer
chargera une balle; tirez-le; rincer et répéter. À chaque tour, HumanPlayer
vous demandera une action à jouer contre votre adversaire. Vos commandes du clavier sont les personnages 0
, 1
, 2
, -
et =
. Sous Windows, vous pouvez utiliser HumanPlayer
pour déboguer votre soumission.
Initier un duel
Voici comment vous pouvez déboguer votre lecteur via la 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();
}
Exemple de jeux
Le nombre de tours minimum que vous devez vaincre GunClubPlayer
est de 3. Voici la répétition de jouer 0-1
contre GunClubPlayer
. Le nombre entre parenthèses est le nombre de munitions chargées pour chaque joueur à la fin du tour.
:: 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 . . .
Le moyen le plus rapide pour être vaincu GunClubPlayer
sans faire de mouvements invalides est la séquence 0=
, car la balle tire à travers le déflecteur thermique. La rediffusion est
:: 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 . . .
Le tournoi
Le tournoi suit le format "Dernier joueur debout". Dans un tournoi, toutes les soumissions valables (y compris les GunClubPlayer
) sont placées dans un pool. Chaque soumission se voit attribuer un identifiant aléatoire mais unique qui restera le même pendant tout le tournoi. À chaque tour:
- Chaque candidature commence par 0 point et disputera 100 duels contre toutes les autres candidatures.
- Chaque duel victorieux accordera 1 point; dessiner et perdre donne 0 points.
- À la fin du tour, les soumissions avec le minimum de points quittent le tournoi. En cas d'égalité, le joueur avec le moins de points gagnés depuis le début du tournoi partira.
- S'il reste plus d'un joueur, le tour suivant commence.
- Les points NE sont PAS reportés au tour suivant.
Soumission
Vous allez soumettre un joueur par réponse. Vous pouvez soumettre plusieurs fichiers pour un joueur, à condition qu'ils n'interfèrent PAS avec d'autres soumissions. Pour que les choses continuent de couler, s'il vous plaît:
- Nommez votre fichier d'en-tête principal comme suit
<Custom>Player.hpp
: - Nommez vos autres fichiers comme
<Custom>Player*.*
, par exemple,MyLittlePlayer.txt
si votre nom de classe estMyLittlePlayer
ouEmoPlayerHates.cpp
si votre nom de classe estEmoPlayer
. - Si votre nom contient
Shooter
des mots similaires qui correspondent au contexte de ce tournoi, vous n’aurez pas besoin d’ajouterPlayer
à la fin. Si vous êtes fermement convaincu que votre nom de soumission fonctionne mieux sans suffixePlayer
, vous n'avez pas besoin d'ajouterPlayer
. - Assurez-vous que votre code peut être compilé et lié sous Windows.
Vous pouvez commenter pour demander des éclaircissements ou pour repérer des échappatoires. J'espère que vous apprécierez ce duel d'armes futuriste et vous souhaite une bonne année!
Clarification
- Vous êtes autorisé à avoir un comportement aléatoire.
- Les actions non valides (tirer lorsque des munitions chargées ne suffisent pas) sont autorisées.
- Si un joueur fait une entrée invalide, son arme explose immédiatement.
- Vous êtes autorisé à étudier les réponses.
- Vous êtes explicitement autorisé à enregistrer le comportement de votre adversaire dans chaque tournoi.
- Chaque tour, vous jouerez 100 duels contre chaque adversaire; Cependant, l'ordre des 100 duels est aléatoire. Vous n'êtes pas assuré de combattre le même adversaire 100 duels d'affilée.
Ressources additionnelles
@flawr a traduit la source C ++ fournie en Java en tant que référence si vous souhaitez soumettre des entrées C ++.
Interface pour les soumissions non C ++
Actuellement accepté: Python 3, Java.
Veuillez suivre l'une des spécifications ci-dessous:
Spécification d'interface 1: code de sortie
Votre soumission sera exécutée une fois par tour.
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.
Vous pouvez tester votre soumission dans PythonPlayer\
et des JavaPlayer\
répertoires.
Spécification d'interface 2: stdin / stdout
(Crédit à H Walters)
Votre soumission sera exécutée une fois par tournoi.
Il existe une exigence fixe pour toutes les entrées sur la manière de faire des E / S, puisque stdin et stdout sont tous les deux connectés au pilote du tournoi. Violer cela pourrait mener à une impasse. Toutes les entrées DOIVENT suivre cet algorithme EXACT (en pseudo-code):
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
Ici, F est l' un 0
, 1
, 2
, -
ou =
pour load / bullet / plasma / metal / thermal
. PROCESSUS signifie éventuellement répondre à ce que votre adversaire a fait (y compris le suivi des munitions de votre adversaire si vous le faites). Notez que l'action de l'adversaire est également l'une des actions "0", "1", "2", "-" ou "=", et se trouve dans le deuxième caractère.
Tableau de bord 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 |
Le tournoi durera jusqu'au 1er février 2017, sauf indication contraire.
la source
Player
implémentation qui appelle un autre processus pour calculer le tournant actuel. Cela permettrait aux gens de participer à n’importe quelle langue que vous êtes heureux d’utiliser sur votre machine.Player::fight
" / "vous pouvez hériterPlayer::perceive
" ... dans les deux cas, le terme est redéfini , pas hérité .GunDuel.hpp
deuxvalidA
et que vous lesvalidB
utilisezactionA
Réponses:
MontePlayer
Ce joueur utilise l'algorithme de recherche d'arborescence découplée UCT Monte Carlo pour décider des choix à effectuer. Il surveille ce que l'ennemi fait pour prédire ses actions. Il simule l'ennemi comme s'il manquait de données.
Ce bot fait vraiment bien contre tous les autres, sauf cβ. Dans un match de 10000 duels contre cβ, Monte a remporté 5246 duels. Avec un peu de maths, cela signifie que Monte remportera un duel contre 51,17% à 53,74% des fois (confiance de 99%).
la source
Maintenant: je suis presque sûr que cela devrait être immédiatement disqualifié, mais il est amusant de ne pas enfreindre explicitement les règles énoncées ci-dessus:
BlackHat n'essaie pas de reconnaître l'adversaire - en réalité, l'identité de l'adversaire est totalement indifférente étant donné que son cerveau est immédiatement remplacé.
Tout se passe localement à la
fight
fonction virtuelle.la source
#ifdef __BLACKHAT_PLAYER_HPP__
␊#error "Dependency issue; to compile, please include this file before BlackHatPlayer.hpp"
␊#else
␊#define __BLACKHAT_PLAYER_HPP__
␊#endif
#pragma once
;-)Ensuite, la plus redoutée de toutes les créatures, elle a été en enfer et à l'arrière et s'est battue avec 900 000 autres bots , sa ...
BotRobot a été nommé, formé et construit automatiquement par un algorithme génétique très basique.
Deux équipes de 9 ont été mises en place, chaque génération, chaque robot de l’équipe 1 est confronté à chaque robot de l’équipe 2. Les robots avec plus de victoires que de défaites ont gardé leur mémoire, l’autre est revenu à la dernière étape. , et eu la chance d’oublier quelque chose, heureusement mauvais. Les robots eux-mêmes sont des tables de consultation enrichies. Dans le cas où ils trouveraient quelque chose qu'ils n'avaient jamais vu auparavant, ils choisiraient simplement une option valide aléatoire et l'enregistreraient en mémoire. La version C ++ ne fait pas cela, elle aurait dû apprendre . Comme indiqué précédemment, les bots gagnants conservent cette nouvelle mémoire, de toute évidence, ils ont fonctionné. Perdre des robots ne le fait pas, et conserve ce avec quoi ils ont commencé.
À la fin, les combats de bots étaient assez serrés, rarement dans l’impasse. Le gagnant a été choisi parmi un groupe de deux équipes post évolution, soit 100 000 générations.
BotRobot, avec son nom généré BEAU et de manière aléatoire , a été le plus chanceux.
Générateur
bot.lua
Révision: Bien que le robot fût assez intelligent contre lui-même et contre d’autres robots générés de la même manière, il s’avéra relativement peu utile dans les combats réels. Alors, j'ai régénéré son cerveau contre certains des bots déjà créés.
Comme on peut facilement le constater, le résultat est un cerveau beaucoup plus complexe, avec des options allant jusqu’au joueur ennemi disposant de 12 munitions.
Je ne suis pas sûr de savoir ce qu'il combattait contre 12 munitions, mais quelque chose l'a fait.
Et bien sûr, le produit fini ...
Je déteste le C ++ maintenant ...
la source
00
.la source
GetRandomDouble
, vous pouvez supprimer l’argument max.Le commentaire me manque partout, donc je ne peux pas encore poser mes questions. C'est donc un joueur très basique à gagner contre le premier bot.
[Edit] Merci, le statut précédent n'est plus vrai, mais je pense qu'il est préférable de le conserver pour que nous puissions comprendre le contexte de ce bot.
L'opportuniste fréquente le même club de tir que les GunClubPlayers, mais il a parié sur un nouveau venu qu'il pourrait battre tous les GunClubPlayers. Il exploite donc l’habitude qu’il a longtemps remarquée et s’oblige à ne pas tirer mais à attendre un peu pour gagner.
la source
Nouveaux changements:
Amélioration des nombres aléatoires (merci Frenzy Li).
la source
getAmmoOpponent
pasgetOpponentAmmo
. Vous manquez également#endif // !__BARRICADE_PLAYER_HPP__
Notez que cela suit les informations sur les adversaires selon les règles du défi; voir le "singleton de style Meyers" scoped "stocked Ls () "méthode en bas. (Certaines personnes se demandaient comment faire cela; maintenant vous savez!)
la source
Ils
GunClubPlayer
aiment aller au club de tir. Au cours de chaque duel, ils chargeaient d’abord des munitions, puis tiraient une balle et répètent ce processus jusqu’au duel de la fin dumonde. Ils ne se soucient pas vraiment de savoir s'ils gagnent ou non, et se concentrent exclusivement sur une expérience agréable.la source
la source
la source
Ce bot n'est pas particulièrement génial - cependant, chaque KOTH a besoin de quelques entrées initiales pour le faire fonctionner :)
Des tests locaux ont montré que cela l'emporte à la fois
GunClubPlayer
et dansOpportunist
100% des cas. Une bataille contreBotRobotPlayer
semblait avoir toujours pour résultat un match nul puisque les deux se cachent derrière leurs boucliers.la source
Je ne code pas en c ++, toute amélioration du code sera la bienvenue.
la source
DeceptivePlayer
- être un meilleur nom?HanSoloPlayer
Tire d'abord! Je travaille toujours sur la révision, mais c’est très bien.
la source
la source
#endif // ! __CAMTO_HPP__
<>&
est une douleur.using namespace std
car cela interfère avec le tournoi. Si vous voulez déboguer, vous pouvez utiliserstd::cout
etc.la source
... parce que j'aimerais voir comment un joueur au hasard se classe.
la source
SpecificPlayer
SpecificPlayer suit un plan simple consistant à choisir des actions aléatoires (valides). Cependant, sa principale caractéristique est qu'il surveille certaines situations en analysant le nombre de munitions et le coup précédent de l'adversaire.
C’est la première fois que j’écris quelque chose en C ++ et j’essaie d’écrire des bots concurrents. J'espère donc que ma maigre tentative fera au moins quelque chose d'intéressant. :)
la source
NotSoPatientPlayer
la source