Tle przyszłych
W 2017 roku ty i twój przeciwnik zmierzą się w futurystycznej bitwie na broń, w której tylko jeden może przetrwać. Czy masz wystarczające doświadczenie, aby pokonać przeciwnika? Nadszedł czas, aby szlifować umiejętności posługiwania się bronią w swoim ulubionym języku programowania i walczyć z wszelkimi przeciwnościami!
Wyniki turnieju
Ten turniej zakończył na UTC rano lutym 2 nd , 2017. Dzięki naszym zawodników, mieliśmy ekscytujący turniej futurystyczny!
MontePlayer jest ostatecznym zwycięzcą po bliskich bitwach z CBetaPlayer i StudiousPlayer. Trzech najlepszych pojedynków Guen wykonało pamiątkową fotografię:
MontePlayer - by TheNumberOne
+------------+
CBetaPlayer | | - by George V. Williams
+------------+ # 1 | StudiousPlayer - by H Walters
| +----------------+
| # 2 # 3 |
+------------------------------------------+
The Futurustic Gun Duel @ PPCG.SE 2017
Gratulacje dla zwycięzców! Szczegółowa tabela wyników znajduje się na końcu tego postu.
Ogólne wskazówki
- Odwiedź oficjalne repozytorium kodu źródłowego używanego w tym turnieju.
- Wpisy w C ++: odziedzicz
Player
klasę. - Pozycje inne niż C ++: wybierz jeden interfejs w sekcji Interfejs dla zgłoszeń innych niż C ++ .
- Obecnie dozwolone języki inne niż C ++: Python 3, Java.
Pojedynek
- Każdy gracz zaczyna od rozładowanego pistoletu, który może załadować nieskończoną ilość amunicji.
- W każdej turze gracze jednocześnie wybiorą jedną z następujących akcji:
0
- Załaduj 1 amunicję do pistoletu.1
- Wystrzel kulę w przeciwnika; kosztuje 1 załadowaną amunicję.2
- Wystrzel promień plazmy na przeciwnika; kosztuje 2 załadowanej amunicji.-
- Broń przychodzącą kulę za pomocą metalowej osłony.=
- Broń przychodzącą wiązkę plazmy za pomocą deflektora termicznego.
- Jeśli obaj gracze przetrwać po 100 th kolei obaj wydechowego do śmierci, co przekłada się remisem .
Gracz przegrywa pojedynek z bronią, jeśli on
- Czy nie używać metalową osłonę bronić przychodzące kulę.
- Czy NIE UŻYWAĆ deflektora termiczną bronić plazmy przychodzące.
- Wystrzel z pistoletu bez załadowania wystarczającej ilości amunicji, w której jego działo wybuchnie i zabije właściciela.
Ostrzeżenia
Zgodnie z instrukcją dla futurystycznych właścicieli broni :
- Metalowa osłona NIE MOŻE bronić przed nadchodzącą wiązką plazmy. Podobnie deflektor termiczny NIE MOŻE bronić przed nadlatującym pociskiem.
- Promień plazmy obezwładnia pocisk (ponieważ ten pierwszy wymaga więcej załadowanej amunicji). Dlatego jeśli gracz strzela wiązką plazmy w przeciwnika, który strzela kulą w tej samej turze, przeciwnik zostaje zabity.
- Jeśli obaj gracze wystrzelą do siebie pocisk w tej samej turze, pociski zostaną anulowane i obaj gracze przeżyją. Podobnie, jeśli obaj gracze wystrzelą na siebie wiązkę plazmy w tej samej turze, obaj gracze przeżyją.
Warto również zauważyć, że:
- NIE będziesz znać akcji przeciwnika po kolei, dopóki się nie skończy.
- Odbijanie wiązek plazmowych i pociski osłaniające NIE zaszkodzą przeciwnikowi.
Dlatego w każdej turze jest 25 prawidłowych kombinacji akcji:
+-------------+---------------------------------------------+
| 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.
Przykładowy pojedynek
Oto pojedynek, który kiedyś miałem z przyjacielem. Wtedy nie wiedzieliśmy wiele o programowaniu, więc używaliśmy gestów rąk i sygnalizowaliśmy prędkość dwóch obrotów na sekundę. Od lewej do prawej nasze działania były z kolei:
Me: 001-000-1201101001----2
Friend: 00-10-=1-==--0100-1---1
Zgodnie z powyższymi zasadami przegrałem. Czy rozumiesz dlaczego? To dlatego, że wystrzeliłem ostatnią wiązkę plazmy, gdy miałem tylko 1 załadowaną amunicję, co spowodowało wybuch mojej broni.
Odtwarzacz C ++
Ty , jako cywilizowanego futurystycznym programista, nie będzie bezpośrednio obsługiwać broń. Zamiast tego piszesz kod, Player
który walczy z innymi ”. Publicznie dziedzicząc klasę c ++ w projekcie GitHub, możesz zacząć pisać miejską legendę.
Player.hpp can be found in Tournament\Player.hpp
An example of a derived class can be found in Tournament\CustomPlayer.hpp
Co musisz lub możesz zrobić
- Musisz odziedziczyć
Player
klasę w drodze publicznego dziedziczenia i ogłosić klasę jako ostateczną. - Musisz zastąpić
Player::fight
, która zwraca wartość poprawną zaPlayer::Action
każdym razem, gdy zostanie wywołana. - Opcjonalnie możesz przesłonić
Player::perceive
iPlayer::declared
kontrolować działania przeciwnika i śledzić swoje zwycięstwa. - Opcjonalnie użyj prywatnych elementów statycznych i metod w klasie pochodnej, aby wykonać bardziej złożone obliczenia.
- Opcjonalnie użyj innych standardowych bibliotek C ++.
Czego NIE wolno robić
- NIE wolno używać żadnej bezpośredniej metody rozpoznawania przeciwnika poza podanym identyfikatorem przeciwnika, który jest tasowany na początku każdego turnieju. Możesz zgadywać, kto jest graczem podczas gry w turnieju.
- NIE wolno zastępować żadnych metod w
Player
klasie, które nie są zadeklarowane jako wirtualne. - NIE wolno deklarować ani inicjować niczego w zakresie globalnym.
- Od debiutu (teraz zdyskwalifikowanego)
BlackHatPlayer
gracze NIE mogą podglądać ani modyfikować stanu przeciwnika.
Przykładowy pojedynek
Proces pojedynku z bronią odbywa się za pomocą GunDuel
klasy. Przykład walki można znaleźć Source.cpp
w sekcji Inicjowanie pojedynku .
Mamy pokazać GunClubPlayer
, HumanPlayer
a GunDuel
klasa, które można znaleźć w Tournament\
katalogu repozytorium.
W każdym pojedynku GunClubPlayer
załaduje pocisk; odpal to; wypłukać i powtórzyć. Podczas każdej tury HumanPlayer
poprosi cię o akcję przeciwko przeciwnikowi. Twoje kontrole klawiaturowe są znaki 0
, 1
, 2
, -
i =
. W systemie Windows można użyć HumanPlayer
do debugowania zgłoszenia.
Rozpoczęcie pojedynku
W ten sposób możesz debugować odtwarzacz za pomocą konsoli.
// 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();
}
Przykładowe gry
Minimalna liczba tur, które musisz pokonać, GunClubPlayer
to 3. Oto powtórka z gry 0-1
przeciwko GunClubPlayer
. Liczba w nawiasie to liczba załadowanej amunicji dla każdego gracza po zakończeniu tury.
:: 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 . . .
Najszybszym sposobem na pokonanie GunClubPlayer
bez wykonywania nieprawidłowych ruchów jest sekwencja 0=
, ponieważ pocisk strzela prosto przez deflektor termiczny. Powtórka jest
:: 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 . . .
Turniej
Turniej odbywa się w formacie „Last Player Standing”. W turnieju wszystkie ważne zgłoszenia (w tym GunClubPlayer
) są umieszczane w puli. Każde zgłoszenie ma przydzielony losowy, ale niepowtarzalny identyfikator, który pozostanie taki sam podczas całego turnieju. Podczas każdej rundy:
- Każde zgłoszenie rozpoczyna się od 0 punktów i rozegra 100 pojedynków z każdym innym zgłoszeniem.
- Każdy zwycięski pojedynek da 1 punkt; losowanie i przegrywanie daje 0 punktów.
- Pod koniec rundy zgłoszenia z minimalną liczbą punktów opuszczają turniej. W przypadku remisu, gracz z najmniejszą ilością punktów zdobytych od początku turnieju odejdzie.
- Jeśli pozostanie więcej niż jeden gracz, rozpocznie się następna runda.
- Punkty NIE przenoszą się do następnej rundy.
Zgłoszenie
Prześlesz jednego gracza na odpowiedź. Możesz przesłać wiele plików do odtwarzacza, o ile NIE kolidują one z innymi przesyłkami. Aby utrzymać płynność, proszę:
- Nazwij główny plik nagłówka jako
<Custom>Player.hpp
, - Nazwij swoje inne pliki jako
<Custom>Player*.*
, np.MyLittlePlayer.txt
Jeśli nazwa klasy toMyLittlePlayer
, lubEmoPlayerHates.cpp
jeśli nazwa klasy toEmoPlayer
. - Jeśli twoje imię
Shooter
lub podobne słowa pasują do kontekstu tego turnieju, nie musisz dodawaćPlayer
na końcu. Jeśli uważasz, że nazwa zgłoszenia działa lepiej bez przyrostkaPlayer
, nie musisz go dodawaćPlayer
. - Upewnij się, że kod można skompilować i połączyć w systemie Windows.
Możesz skomentować, poprosić o wyjaśnienia lub znaleźć luki. Mam nadzieję, że spodoba ci się ten futurystyczny pojedynek z bronią i życzę szczęśliwego nowego roku!
Wyjaśnienie
- Możesz mieć losowe zachowanie.
- Dozwolone są nieprawidłowe działania (strzelanie, gdy załadowana amunicja nie wystarcza).
- Jeśli gracz dokona nieprawidłowego wejścia, jego pistolet natychmiast wybuchnie.
- Możesz studiować odpowiedzi.
- Możesz wyraźnie rejestrować zachowanie przeciwnika w każdym turnieju.
- W każdej rundzie rozegrasz 100 pojedynków z każdym przeciwnikiem; kolejność 100 pojedynków jest jednak losowa - nie masz gwarancji stoczenia 100 pojedynczych przeciwników z rzędu.
Dodatkowe zasoby
@flawr przetłumaczył dostarczone źródło C ++ na Javę jako odniesienie, jeśli chcesz przesłać wpisy C ++.
Interfejs dla zgłoszeń innych niż C ++
Obecnie akceptowane: Python 3, Java.
Postępuj zgodnie z jedną z poniższych specyfikacji:
Specyfikacja interfejsu 1: kod wyjścia
Twoje zgłoszenie będzie uruchamiane raz na turę.
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.
Możesz przetestować swoje zgłoszenie PythonPlayer\
i JavaPlayer\
katalogi.
Specyfikacja interfejsu 2: stdin / stdout
(Podziękowania dla H Waltersa)
Twoje zgłoszenie będzie realizowane raz na turniej.
Istnieje stały wymóg dla wszystkich zgłoszeń dotyczących wykonywania operacji we / wy, ponieważ zarówno stdin, jak i stdout są podłączone do sterownika turnieju. Naruszenie tego może doprowadzić do impasu. Wszystkie wpisy MUSZĄ być zgodne z tym DOKŁADNYM algorytmem (w pseudokodzie):
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
Tutaj F jest jednym z 0
, 1
, 2
, -
, lub =
za load / bullet / plasma / metal / thermal
. PROCES oznacza opcjonalną reakcję na to, co zrobił twój przeciwnik (w tym śledzenie amunicji przeciwnika, jeśli to robisz). Zauważ, że akcja przeciwnika jest również jedną z „0”, „1”, „2”, „-” lub „=” i występuje w drugim znaku.
Ostateczna tablica wyników
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 |
Turniej potrwa do 1 lutego 2017 r., Chyba że zaznaczono inaczej.
źródło
Player
implementację, która wywołuje inny proces obliczania bieżącej tury. Dzięki temu ludzie będą mogli uczestniczyć w dowolnym języku, którym z przyjemnością posługujesz się na swoim komputerze.Player::fight
” / „możesz odziedziczyćPlayer::perceive
” ... w obu przypadkach termin zastępuje , a nie dziedziczy .GunDuel.hpp
, obavalidA
ivalidB
użyjactionA
Odpowiedzi:
MontePlayer
Ten gracz korzysta z algorytmu wyszukiwania drzewa Decoupled UCT Monte Carlo, aby zdecydować, jakich wyborów powinien dokonać. Śledzi to, co robi wróg, aby przewidzieć jego działania. Symuluje wroga jako samego, jeśli brakuje mu danych.
Ten bot radzi sobie naprawdę dobrze z każdym innym botem oprócz cβ. W meczu 10000 pojedynków z cβ, Monte wygrał 5246 pojedynków. Przy odrobinie matematyki oznacza to, że Monte wygra pojedynek z cβ 51,17% do 53,74% czasu (99% zaufania).
źródło
Teraz: jestem prawie pewien, że należy to natychmiast zdyskwalifikować, ale to zabawne, że nie naruszam wyraźnie żadnej z powyższych zasad:
BlackHat nie próbuje rozpoznać przeciwnika - w rzeczywistości nie ma znaczenia, kim jest przeciwnik, biorąc pod uwagę, że jego mózg został natychmiast zastąpiony.
Wszystko dzieje się lokalnie z
fight
funkcją wirtualną.źródło
#ifdef __BLACKHAT_PLAYER_HPP__
␊#error "Dependency issue; to compile, please include this file before BlackHatPlayer.hpp"
␊#else
␊#define __BLACKHAT_PLAYER_HPP__
␊#endif
␊#pragma once
;-)Następnie, najbardziej przerażający ze wszystkich stworzeń, był w piekle iz powrotem i walczył z dosłownie 900000 innymi botami , jego ...
BotRobot został nazwany, wyszkolony i zbudowany automatycznie przez bardzo podstawowy algorytm genetyczny.
Dwie drużyny po 9 zostały ustawione przeciwko sobie, w każdym pokoleniu, każdy robot z zespołu 1 jest wystawiany przeciwko każdemu robotowi z zespołu 2. Roboty z większą liczbą zwycięstw niż strat, zachowały pamięć, a drugie wróciły do ostatniego kroku i miał okazję coś zapomnieć, miejmy nadzieję, że źle. Same boty to uwielbiane tabele odnośników, w których gdyby znalazły coś, czego wcześniej nie widzieli, po prostu wybraliby losową prawidłową opcję i zapisali ją w pamięci. Wersja C ++ tego nie robi, należy się tego nauczyć . Jak wspomniano wcześniej, zwycięskie boty zachowują tę nowo znalezioną pamięć, ponieważ wyraźnie działała. Utracone boty nie zachowują tego, od czego zaczęły.
W końcu walki botów były dość bliskie, rzadko patowe. Zwycięzca został wybrany z puli dwóch zespołów po ewolucji, która wynosiła 100 000 pokoleń.
BotRobot, z losowo wygenerowanym i PIĘKNA nazwie, był szczęśliwym.
Generator
bot.lua
Rewizja: Chociaż robot był dość inteligentny przeciwko sobie i innym podobnie generowanym robotom, okazał się dość bezużyteczny w rzeczywistych bitwach. Tak więc zregenerowałem jego mózg przeciwko niektórym już stworzonym botom.
Rezultaty, jak można łatwo zobaczyć, są znacznie bardziej złożonym mózgiem, z opcjami do wroga gracza posiadającego 12 amunicji.
Nie jestem pewien, z czym walczył, że dostał 12 amunicji, ale coś się udało.
I oczywiście gotowy produkt ...
Ja nienawidzę C ++ teraz ...
źródło
00
.źródło
GetRandomDouble
, możesz usunąć argument max.Wszędzie brakuje mi komentarza, więc nie mogę jeszcze zadawać pytań. Jest to więc bardzo prosty gracz, aby wygrać z pierwszym botem.
[Edytuj] Dzięki, teraz poprzedni status nie jest już prawdziwy, ale myślę, że lepiej go zachować, abyśmy mogli zrozumieć kontekst tego bota.
Oportunista uczęszcza do tego samego klubu strzelców, co GunClubPlayers, jednak obstawił nowego gracza, że może pokonać każdego GunClubPlayers. Wykorzystuje więc nawyk, który od dawna zauważył i zmusza się, aby nie strzelać, ale tylko chwilę poczekać, aby wygrać.
źródło
Najnowsze zmiany:
Poprawione losowe liczby (dzięki Frenzy Li).
źródło
getAmmoOpponent
niegetOpponentAmmo
. Tęsknisz także#endif // !__BARRICADE_PLAYER_HPP__
Zauważ, że śledzi to informacje o przeciwnikach zgodnie z regułami wyzwania; zobacz metodę „Meyers style singleton” „scoped” przechowywane Ls () na dole. (Niektórzy zastanawiali się, jak to zrobić; teraz już wiesz!)
źródło
Do
GunClubPlayer
s, aby przejść do klubu pistoletu. Podczas każdego pojedynku najpierw ładowali amunicję, a następnie strzelali kulą i powtarzali ten proces do końca pojedynku naświecie. Nie obchodzi ich to, czy wygrają, czy nie, i koncentrują się wyłącznie na przyjemnym doświadczeniu.źródło
źródło
źródło
Ten bot nie jest szczególnie świetny - jednak każdy KOTH potrzebuje kilku początkowych wpisów, aby go uruchomić :)
Badania wykazały, że to lokalny wygrywa zarówno przed
GunClubPlayer
iOpportunist
100% czasu. Bitwa przeciwkoBotRobotPlayer
zawsze wydawała się remisem, ponieważ obaj chowali się za tarczami.źródło
Nie koduję w c ++, więc wszelkie poprawki do kodu będą mile widziane.
źródło
DeceptivePlayer
jest to lepsze imię?HanSoloPlayer
Najpierw strzela! Nadal pracuję nad jego poprawieniem, ale to całkiem nieźle.
źródło
źródło
#endif // ! __CAMTO_HPP__
<>&
jest uciążliwe.using namespace std
ponieważ przeszkadza w turnieju. Jeśli chcesz debugować, możesz użyćstd::cout
itp.źródło
... bo chciałbym zobaczyć, jak ocenia się losowy gracz.
źródło
SpecificPlayer
SpecificPlayer postępuje według prostego planu wybierania losowych (prawidłowych) akcji. Jednak jego główną cechą jest to, że zwraca uwagę na pewne sytuacje, analizując liczbę amunicji i poprzedni ruch przeciwnika.
Po raz pierwszy piszę cokolwiek w C ++ i po raz pierwszy próbuję pisać konkurencyjne boty. Mam więc nadzieję, że moja skromna próba przyniesie przynajmniej coś interesującego. :)
źródło
NotSoPatientPlayer
źródło