Fütüristik silah düello


73

Arka plan Gelecek

2017 yılında, siz ve rakibiniz yalnızca birinin hayatta kalabileceği fütüristik bir silahlı savaşta karşı karşıya kalacaksınız. Are sen rakibin yenmek için yeterli deneyimli? Şimdi silah becerilerinizi en sevdiğiniz programlama dilinde cilalama ve her şeye rağmen mücadele etme zamanı !

Turnuva Sonuçları

Feburary 2'nin UTC sabahı sona Bu turnuva nd bizim yarışmacılar için 2017 sayesinde, heyecan verici futuristik turnuva oldu!

MontePlayer, CBetaPlayer ve StudiousPlayer ile yakın dövüşlerin ardından son galibi. Üç en iyi guen düelloları hatıra fotoğrafı çekti:

                MontePlayer                         - by TheNumberOne
              +------------+
  CBetaPlayer |            |                        - by George V. Williams
 +------------+    #  1    | StudiousPlayer         - by H Walters
 |                         +----------------+
 |    #  2                        #  3      |       
 +------------------------------------------+
    The Futurustic Gun Duel @ PPCG.SE 2017

Kazananları tebrik ediyoruz! Detaylı ilan tahtası bu yazının sonuna doğru görülüyor.

Genel rehberlik

  • Bu turnuvada kullanılan kaynak kodu için resmi depoyu ziyaret edin .
  • C ++ girdileri: lütfen Playersınıfı miras alın .
  • C ++ olmayan girişler: C ++ olmayan başvuruları için Arayüz bölümünde bir arayüz seçin .
  • Şu anda izin verilen C ++ olmayan diller: Python 3, Java.

Düello

  • Her oyuncu, sınırsız miktarda cephane yükleyebilen boş bir silahla başlar.
  • Her turda, oyuncular aynı anda aşağıdaki işlemlerden birini seçer:
    • 0 - Tabancaya 1 mermi yerleştirin.
    • 1- Rakibe bir mermi at; 1 yüklü cephane maliyeti.
    • 2- Rakibe bir plazma ışını ateşleyin; 2 yüklü cephane maliyeti.
    • - - Metal bir kalkan kullanarak gelen mermiyi savun.
    • = - Bir termal deflektör kullanarak gelen plazma ışınını savun.
  • Her iki oyuncu da 100'üncü dönüşten sonra hayatta kalırsa, ikisi de ölüme tükenir ve bu da berabere sonuçlanır .

Bir oyuncu silah düellolarını kaybeder

  • Did DEĞİL gelen mermiyi savunmak için metal koruyucuyu kullanın.
  • Did DEĞİL gelen plazma savunmak için termal saptırıcısını kullanın.
  • Yeterli cephane yüklemeden bir silahı ateşleyin, silahları kendiliğinden patlar ve sahibini öldürür.

Uyarılar

Fütüristik Silah Sahipleri El Kitabı'na göre :

  • Bir metal kalkan CAN gelen plazma ışını savunmak. Aynı şekilde, bir termal saptırıcı CAN NOT gelen kurşunuyla savunmak.
  • Plazma ışını mermi atar (çünkü daha fazla cephane gerektirir). Bu nedenle, bir oyuncu aynı sırayla bir mermi atan rakibe bir plazma ışını atarsa, rakip öldürülür.
  • Her iki oyuncu da aynı sırayla bir mermi ateşlerse, mermiler iptal edilir ve her iki oyuncu da hayatta kalır. Benzer şekilde, her iki oyuncu da aynı sırayla birbirlerine plazma ışını attığında, her iki oyuncu da hayatta kalır.

Ayrıca dikkat çekicidir:

  • Sen olacak DEĞİL biter kadar sırayla rakibin eylemi biliyorum.
  • Plazma huzmelerini saptırmak ve koruyucu mermiler olacak DEĞİL rakibin zarar verirler.

Bu nedenle, her turda toplam 25 geçerli işlem kombinasyonu vardır:

+-------------+---------------------------------------------+
|   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.

Örnek Düello

İşte bir zamanlar bir arkadaşımla yaşadığım bir düello. O zamanlar programlama hakkında pek bir şey bilmiyorduk, bu yüzden el hareketlerini kullandık ve saniyede iki tur hızında sinyal verdik. Soldan sağa, sırayla eylemlerimiz:

    Me: 001-000-1201101001----2
Friend: 00-10-=1-==--0100-1---1

Yukarıdaki kurallara göre kaybettim. Neden görüyor musun Bunun sebebi, sadece 1 adet yüklü cephane varken silahımı patlatarak son plazma ışınını ateşlememdi.


C ++ Oynatıcı

Siz , medeni bir fütüristik programcı olarak, silahlarla doğrudan ilgilenmeyeceksiniz. Bunun yerine, Playerbaşkalarına karşı savaşan bir kod yazıyorsunuz '. GitHub projesinde sınıfını herkese açık bir şekilde devralarak, şehir efsanenizi yazmaya başlayabilirsiniz.

Player.hpp can be found in Tournament\Player.hpp
An example of a derived class can be found in Tournament\CustomPlayer.hpp

Ne gerekir veya olabilir yapmak

  • PlayerGenel miras yoluyla ders miras almalı ve sınıf finalini beyan etmelisiniz.
  • Her çağrıldığında Player::fightgeçerli bir sonuç veren geçersiz kılmalısınız Player::Action.
  • İsteğe bağlı olarak, geçersiz kılmak Player::perceiveve Player::declaredrakibin eylemleri göz kulak ve Zaferlerinin takip etmek.
  • İsteğe bağlı olarak, daha karmaşık hesaplamalar yapmak için türetilmiş sınıfınızdaki özel statik üyeleri ve yöntemleri kullanın.
  • İsteğe bağlı olarak, diğer C ++ standart kütüphanelerini kullanın.

Yapmamanız gerekenler

  • Sen gerekir DEĞİL her turnuvanın başında karıştırılır edilir verilen rakip tanımlayıcı dışında rakibin tanımak için herhangi bir doğrudan yöntemini kullanın. Bir turnuvada sadece bir oyuncunun oyunda kim olduğunu tahmin etmenize izin verilir.
  • Sen gerekir DEĞİL içinde herhangi bir yöntemi geçersiz Playersanal ilan edilmez sınıfın.
  • Sen gerekir DEĞİL beyan veya global kapsamda şey başlatmak.
  • (Şimdi diskalifiye) başlangıcından beri BlackHatPlayer, oyuncular vardır DEĞİL gözetleme veya rakibin durumunu değiştirmek için izin verdi.

Örnek bir düello

Bir silah düello işlemi, GunDuelsınıf kullanılarak yapılır . Örnek bir dövüş için bir düello başlatılmasıSource.cpp bölümüne bakın .

Biz vitrin GunClubPlayer, HumanPlayerve GunDuelsınıf bulunabilir hangi Tournament\deponun dizininde.

Her düelloda GunClubPlayerbir mermi yüklenecek; kov onu; durulayın ve tekrarlayın. Her turda HumanPlayersizden rakibinize karşı oynayabileceğiniz bir aksiyon isteyecektir. Klavyeniz kontrolleri karakterlerdir 0, 1, 2, -ve =. Windows'ta HumanPlayergönderiminizi hata ayıklamak için kullanabilirsiniz .

Bir düello başlatmak

Müzikçalarınızı bu şekilde konsoldan hata ayıklayabilirsiniz.

// 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();
}

Örnek Oyunlar

Yenmek için gereken en az tur GunClubPlayersayısı 3'tür. İşte 0-1karşı oynadığın tekrar GunClubPlayer. Parantez içindeki sayı, sıra sona erdiğinde her oyuncu için yüklü cephane sayısıdır.

 :: 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 . . .

GunClubPlayerGeçersiz hareketler yapmadan mağlup olmanın en hızlı yolu sekanstır 0=, çünkü mermi ısıl saptırıcıdan doğrudan vurur. Tekrar

 :: 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 . . .

Turnuva

Turnuva "Son Oyuncu Daimi" formatını izler. Bir turnuvada, geçerli tüm başvurular (dahil olanlar dahil GunClubPlayer) bir havuza yerleştirilir. Her gönderime, tüm turnuva boyunca aynı kalacak olan randomize ancak benzersiz bir tanımlayıcı atanır. Her turda:

  • Her gönderim 0 puanla başlar ve diğer gönderilere karşı 100 düello oynar.
  • Her muzaffer düello 1 puan verecek; çizim ve kaybetme 0 puan verir.
  • Turun sonunda, minimum puan alan başvurular turnuvadan ayrılır. Beraberlik durumunda, turnuva başlangıcından bu yana kazanılan en az puana sahip oyuncu ayrılır.
  • Eğer birden fazla oyuncu kalırsa, bir sonraki tur başlayacaktır.
  • Nokta do DEĞİL üst tura aktarılır.

boyun eğme

Cevap başına bir oyuncu göndereceksin. Sen yeter ki yok gibi, bir oyuncu için birden fazla dosya gönderebilirsiniz DEĞİL diğer gönderimleri müdahale. İşlerin akmasını sağlamak için lütfen:

  • Ana başlık dosyanızı <Custom>Player.hpp,
  • Diğer dosyalarınızı <Custom>Player*.*, örneğin MyLittlePlayer.txtsınıf adınız MyLittlePlayerveya EmoPlayerHates.cppsınıf adınız gibi adlandırın EmoPlayer.
  • Adınız Shooterveya bu turnuva bağlamına uygun kelimeler içeriyorsa Player, sonunda eklemeniz gerekmez . Gönderim adınızın sonek olmadan daha iyi çalıştığını düşünüyorsanız Player, ayrıca eklemeniz gerekmez Player.
  • Kodunuzun Windows altında derlendiğinden ve bağlandığından emin olun.

Açıklama istemek veya boşluk boşlukları görmek için yorum yapabilirsiniz. Bu Fütüristik Silah Düellolarının tadını çıkarmanızı ve Mutlu Yıllar dileriz!

açıklama

  • Rasgele davranış sergilemenize izin verilir.
  • Geçersiz işlemlere (yüklü cephane ateş etmesine yetmez) izin verilir.
  • Bir oyuncu geçersiz bir giriş yaparsa, tabancası derhal patlayacak.
  • Cevapları incelemenize izin var.
  • Sen edilir açıkça izin her turnuvanın içindeki rakip davranışlarını kaydetmek için.
  • Her rauntta, her rakibe karşı 100 düello oynayacaksın; Bununla birlikte, 100 düello sırasının sırası rastgeledir - aynı rakibin arka arkaya 100 düelloyla savaşması garanti edilmez.

Ek kaynaklar

@flawr, C ++ girişlerini göndermek istiyorsanız , sağlanan C ++ kaynağını referans olarak Java'ya çevirdi .

C ++ Dışı Gönderme Arayüzü

Şu anda kabul edilen: Python 3, Java.

Lütfen aşağıdaki özelliklerden birini takip edin:

Arayüz özellikleri 1: çıkış kodu

Gönderiniz her turda bir kez yayınlanacaktır.

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.

Gönderinizi PythonPlayer\ve JavaPlayer\dizinlerinizi test edebilirsiniz .

Arayüz özellikleri 2: stdin / stdout

(H Walters'a teşekkür ederiz)

Gönderiniz, turnuva başına bir kez yayınlanacak.

Turnuva sürücüsüne hem stdin hem de stdout bağlandığı için G / Ç'nin nasıl yapılacağı ile ilgili tüm girişler için sabit bir gereksinim vardır. Bunu ihlal etmek kilitlenmeye yol açabilir. Tüm girişler GEREKİR bu takip EXACT (sözde kod) algoritması:

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

Burada, F biridir 0, 1, 2, -, veya =için load / bullet / plasma / metal / thermal. PROSES isteğe bağlı olarak rakibinizin ne yaptığını yanıtlamak anlamına gelir (eğer bunu yapıyorsanız rakibinizin cephanesini izlemek dahil). Rakibin hareketinin aynı zamanda '0', '1', '2', '-' veya '=' arasında olduğunu ve ikinci karakterde olduğunu unutmayın.

Final Scoreboard

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 |

Aksi belirtilmedikçe , turnuva 1 Şubat 2017 tarihine kadar sürecek .


15
Etkileyici ilk meydan okuma, bu arada!
Martin Ender

3
Başka dilleri de çalıştırmak istiyorsanız Player, mevcut sırayı hesaplamak için başka bir işlemi çağıran bir uygulamaya izin verebilirsiniz . Bu, insanların makinenizde çalıştırmaktan mutluluk duyduğunuz herhangi bir dile katılmalarını sağlar.
Martin Ender

5
Rasgele izin verildi? (Tamamen rastgele olmayan dönüşler, belirli bir durumda sadece 50/50'lik bir eylem seçimi)
FlipTack

2
Teknik nokta; "Miras almalısınız Player::fight" / "miras alabilirsiniz Player::perceive" ... her iki durumda da, terim geçersiz kılınır , devralınmaz .
H Walters

3
Sanırım GunDuel.hpp, validAvalidBactionA
ikinizde

Yanıtlar:


9

MontePlayer

Bu oyuncu, hangi seçimleri yapması gerektiğine karar vermek için Dekuplajlı UCT Monte Carlo Ağacı Arama algoritmasını kullanır. Düşmanın eylemlerini tahmin etmek için neler yaptığını izler. Veri yoksa, düşmanı kendisi gibi taklit eder.

Bu bot cβ hariç diğer tüm botlara karşı gerçekten iyi sonuç veriyor. Cβ'ye karşı 10000 düello karşılaşmasında Monte, 5246 düello kazandı. Birazcık matematik ile, bu Monte'nin zamanın% 51.17 ila% 53.74'üne (% 99 güven) karşı bir düello kazanacağı anlamına geliyor.

#ifndef __Monte_PLAYER_HPP__
#define __Monte_PLAYER_HPP__

#include "Player.hpp"
#include <cstdlib>
#include <ctime>
#include <memory>
#include <iostream>


class MontePlayer final : public Player
{
    static const int MAX_TURNS = 100;
    static const int TOTAL_ACTIONS = 5;

    //Increase this if number of players goes above 20.
    static const int MAX_PLAYERS = 20;

    //The number of simulated games we run every time our program is called.
    static const int MONTE_ROUNDS = 1000;


    /**
    * Represents the current state of the game.
    */
    struct Game
    {
        int turn;
        int ammo;
        int opponentAmmo;
        bool alive;
        bool opponentAlive;

        Game(int turn, int ammo, int opponentAmmo, bool alive, bool opponentAlive)
            : turn(turn), ammo(ammo), opponentAmmo(opponentAmmo), alive(alive), opponentAlive(opponentAlive) {}
        Game() : turn(0), ammo(0), opponentAmmo(0), alive(false), opponentAlive(false) {}
    };

    struct Stat
    {
        int wins;
        int attempts;

        Stat() : wins(0), attempts(0) {}
    };

    /**
    * A Monte tree data structure.
    */
    struct MonteTree
    {
        //The state of the game.
        Game game;

        //myStats[i] returns the statistic for doing the i action in this state.
        Stat myStats[TOTAL_ACTIONS];
        //opponentStats[i] returns the statistic for the opponent doing the i action in this state.
        Stat opponentStats[TOTAL_ACTIONS];
        //Total number of times we've created statistics from this tree.
        int totalPlays = 0;
        //The action that led to this tree.
        int myAction;
        //The opponent action that led to this tree.
        int opponentAction;

        //The tree preceding this one.
        MonteTree *parent = NULL;

        //subtrees[i][j] is the tree that would follow if I did action i and the
        //opponent did action j.
        MonteTree *subtrees[TOTAL_ACTIONS][TOTAL_ACTIONS] = { { NULL } };

        MonteTree(int turn, int ammo, int opponentAmmo) :
            game(turn, ammo, opponentAmmo, true, true) {}


        MonteTree(Game game, MonteTree *parent, int myAction, int opponentAction) :
            game(game), parent(parent), myAction(myAction), opponentAction(opponentAction)
        {
            //Make sure the parent tree keeps track of this tree.
            parent->subtrees[myAction][opponentAction] = this;
        }

        //The destructor so we can avoid slow ptr types and memory leaks.
        ~MonteTree()
        {
            //Delete all subtrees.
            for (int i = 0; i < TOTAL_ACTIONS; i++)
            {
                for (int j = 0; j < TOTAL_ACTIONS; j++)
                {
                    auto branch = subtrees[i][j];

                    if (branch)
                    {
                        branch->parent = NULL;
                        delete branch;
                    }
                }
            }
        }
    };

    //The previous state.
    Game prevGame;
    //The id of the opponent.
    int opponent;
    //opponentHistory[a][b][c][d] returns the number of times
    //that opponent a did action d when I had b ammo and he had c ammo.
    static int opponentHistory[MAX_PLAYERS][MAX_TURNS][MAX_TURNS][TOTAL_ACTIONS];

public:
    MontePlayer(size_t opponent = -1) : Player(opponent)
    {
        srand(time(NULL));
        this->opponent = opponent;
    }

public:

    virtual Action fight()
    {
        //Create the root tree. Will be auto-destroyed after this function ends.
        MonteTree current(getTurn(), getAmmo(), getAmmoOpponent());

        //Set the previous game to this one.
        prevGame = current.game;

        //Get these variables so we can log later if nessecarry.
        int turn = getTurn(),
            ammo = getAmmo(),
            opponentAmmo = getAmmoOpponent();

        for (int i = 0; i < MONTE_ROUNDS; i++)
        {
            //Go down the tree until we find a leaf we haven't visites yet.
            MonteTree *leaf = selection(&current);

            //Randomly simulate the game at the leaf and get the result.
            int score = simulate(leaf->game);

            //Propagate the scores back up the root.
            update(leaf, score);
        }

        //Get the best move.
        int move = bestMove(current);

        //Move string for debugging purposes.
        const char* m;

        //We have to do this so our bots state is updated.
        switch (move)
        {
        case Action::LOAD:
            load();
            m = "load";
            break;
        case Action::BULLET:
            bullet();
            m = "bullet";
            break;
        case Action::PLASMA:
            plasma();
            m = "plasma";
            break;
        case Action::METAL:
            metal();
            m = "metal";
            break;
        case Action::THERMAL:
            thermal();
            m = "thermal";
            break;
        default: //???
            std::cout << move << " ???????\n";
            throw move;
        }

        return (Action)move;
    }

    /**
    * Record what the enemy does so we can predict him.
    */
    virtual void perceive(Action action)
    {
        Player::perceive(action);
        opponentHistory[opponent][prevGame.ammo][prevGame.opponentAmmo][action]++;
    }
private:

    /**
    * Trickle down root until we have to create a new leaf MonteTree or we hit the end of a game.
    */
    MonteTree * selection(MonteTree *root)
    {
        while (!atEnd(root->game))
        {
            //First pick the move that my bot will do.

            //The action my bot will do.
            int myAction;
            //The number of actions with the same bestScore.
            int same = 0;
            //The bestScore
            double bestScore = -1;

            for (int i = 0; i < TOTAL_ACTIONS; i++)
            {
                //Ignore invalid or idiot moves.
                if (!isValidMove(root->game, i, true))
                {
                    continue;
                }

                //Get the score for doing move i. Uses
                double score = computeScore(*root, i, true);

                //Randomly select one score if multiple actions have the same score.
                //Why this works is boring to explain.
                if (score == bestScore)
                {
                    same++;
                    if (Random(same) == 0)
                    {
                        myAction = i;
                    }
                }
                //Yay! We found a better action.
                else if (score > bestScore)
                {
                    same = 1;
                    myAction = i;
                    bestScore = score;
                }
            }

            //The action the enemy will do.
            int enemyAction;

            //The number of times the enemy has been in this same situation.
            int totalEnemyEncounters = 0;
            for (int i = 0; i < TOTAL_ACTIONS; i++)
            {
                totalEnemyEncounters += opponentHistory[opponent][root->game.ammo][root->game.opponentAmmo][i];
            }

            //Assume the enemy will choose an action it has chosen before if we've
            //seen it in this situation before. Otherwise we assume that the enemy is ourselves.
            if (totalEnemyEncounters > 0)
            {
                //Randomly select an action that the enemy has done with
                //weighted by the number of times that action has been done.
                int selection = Random(totalEnemyEncounters);
                for (int i = 0; i < TOTAL_ACTIONS; i++)
                {
                    selection -= opponentHistory[opponent][root->game.ammo][root->game.opponentAmmo][i];
                    if (selection < 0)
                    {
                        enemyAction = i;
                        break;
                    }
                }
            }
            else
            {
                //Use the same algorithm to pick the enemies move we use for ourselves.
                same = 0;
                bestScore = -1;
                for (int i = 0; i < TOTAL_ACTIONS; i++)
                {
                    if (!isValidMove(root->game, i, false))
                    {
                        continue;
                    }

                    double score = computeScore(*root, i, false);
                    if (score == bestScore)
                    {
                        same++;
                        if (Random(same) == 0)
                        {
                            enemyAction = i;
                        }
                    }
                    else if (score > bestScore)
                    {
                        same = 1;
                        enemyAction = i;
                        bestScore = score;
                    }
                }
            }

            //If this combination of actions hasn't been explored yet, create a new subtree to explore.
            if (!(*root).subtrees[myAction][enemyAction])
            {
                return expand(root, myAction, enemyAction);
            }

            //Do these actions and explore the next subtree.
            root = (*root).subtrees[myAction][enemyAction];
        }
        return root;
    }

    /**
    * Creates a new leaf under root for the actions.
    */
    MonteTree * expand(MonteTree *root, int myAction, int enemyAction)
    {
        return new MonteTree(
            doTurn(root->game, myAction, enemyAction),
            root,
            myAction,
            enemyAction);
    }

    /**
    * Computes the score of the given move in the given position.
    * Uses the UCB1 algorithm and returns infinity for moves not tried yet.
    */
    double computeScore(const MonteTree &root, int move, bool me)
    {
        const Stat &stat = me ? root.myStats[move] : root.opponentStats[move];
        return stat.attempts == 0 ?
            HUGE_VAL :
            double(stat.wins) / stat.attempts + sqrt(2 * log(root.totalPlays) / stat.attempts);
    }

    /**
    * Randomly simulates the given game.
    * Has me do random moves that are not stupid.
    * Has opponent do what it has done in similar positions or random moves if not
    * observed in those positions yet.
    *
    * Returns 1 for win. 0 for loss. -1 for draw.
    */
    int simulate(Game game)
    {
        while (!atEnd(game))
        {
            game = doRandomTurn(game);
        }

        if (game.alive > game.opponentAlive)
        {
            return 1;
        }
        else if (game.opponentAlive > game.alive)
        {
            return 0;
        }
        else //Draw
        {
            return -1;
        }
    }

    /**
    * Returns whether the game is over or not.
    */
    bool atEnd(Game game)
    {
        return !game.alive || !game.opponentAlive || game.turn > MAX_TURNS;
    }

    /**
    * Simulates the given actions on the game.
    */
    Game doTurn(Game game, int myAction, int enemyAction)
    {
        game.turn++;

        switch (myAction)
        {
        case Action::LOAD:
            game.ammo++;
            break;
        case Action::BULLET:
            if (game.ammo < 1)
            {
                game.alive = false;
                break;
            }
            game.ammo--;
            if (enemyAction == Action::LOAD || enemyAction == Action::THERMAL)
            {
                game.opponentAlive = false;
            }
            break;
        case Action::PLASMA:
            if (game.ammo < 2)
            {
                game.alive = false;
                break;
            }
            game.ammo -= 2;
            if (enemyAction != Action::PLASMA && enemyAction != Action::THERMAL)
            {
                game.opponentAlive = false;
            }
            break;
        }

        switch (enemyAction)
        {
        case Action::LOAD:
            game.opponentAmmo++;
            break;
        case Action::BULLET:
            if (game.opponentAmmo < 1)
            {
                game.opponentAlive = false;
                break;
            }
            game.opponentAmmo--;
            if (myAction == Action::LOAD || myAction == Action::THERMAL)
            {
                game.alive = false;
            }
            break;
        case Action::PLASMA:
            if (game.opponentAmmo < 2)
            {
                game.opponentAlive = false;
            }
            game.opponentAmmo -= 2;
            if (myAction != Action::PLASMA && myAction != Action::THERMAL)
            {
                game.alive = false;
            }
            break;
        }

        return game;
    }

    /**
    * Chooses a random move for me and my opponent and does it.
    */
    Game doRandomTurn(Game &game)
    {
        //Select my random move.
        int myAction;
        int validMoves = 0;

        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            //Don't do idiotic moves.
            //Select one at random.
            if (isValidMove(game, i, true))
            {
                validMoves++;
                if (Random(validMoves) == 0)
                {
                    myAction = i;
                }
            }
        }

        //Choose random opponent action.
        int opponentAction;

        //Whether the enemy has encountered this situation before
        bool enemyEncountered = false;

        validMoves = 0;

        //Weird algorithm that works and I don't want to explain.
        //What it does:
        //If the enemy has encountered this position before,
        //then it chooses a random action weighted by how often it did that action.
        //If they haven't, makes the enemy choose a random not idiot move.
        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            int weight = opponentHistory[opponent][game.ammo][game.opponentAmmo][i];
            if (weight > 0)
            {
                if (!enemyEncountered)
                {
                    enemyEncountered = true;
                    validMoves = 0;
                }
                validMoves += weight;
                if (Random(validMoves) < weight)
                {
                    opponentAction = i;
                }
            }
            else if (!enemyEncountered && isValidMove(game, i, false))
            {
                validMoves++;
                if (Random(validMoves) == 0)
                {
                    opponentAction = i;
                }
            }
        }

        return doTurn(game, myAction, opponentAction);
    }

    /**
    * Returns whether the given move is valid/not idiotic for the game.
    */
    bool isValidMove(Game game, int move, bool me)
    {
        switch (move)
        {
        case Action::LOAD:
            return true;
        case Action::BULLET:
            return me ? game.ammo > 0 : game.opponentAmmo > 0;
        case Action::PLASMA:
            return me ? game.ammo > 1 : game.opponentAmmo > 1;
        case Action::METAL:
            return me ? game.opponentAmmo > 0 : game.ammo > 0;
        case Action::THERMAL:
            return me ? game.opponentAmmo > 1 : game.ammo > 1;
        default:
            return false;
        }
    }

    /**
    * Propagates the score up the MonteTree from the leaf.
    */
    void update(MonteTree *leaf, int score)
    {
        while (true)
        {
            MonteTree *parent = leaf->parent;
            if (parent)
            {
                //-1 = draw, 1 = win for me, 0 = win for opponent
                if (score != -1)
                {
                    parent->myStats[leaf->myAction].wins += score;
                    parent->opponentStats[leaf->opponentAction].wins += 1 - score;
                }
                parent->myStats[leaf->myAction].attempts++;
                parent->opponentStats[leaf->opponentAction].attempts++;
                parent->totalPlays++;
                leaf = parent;
            }
            else
            {
                break;
            }
        }
    }

    /**
    * There are three different strategies in here.
    * The first is not random, the second more, the third most.
    */
    int bestMove(const MonteTree &root)
    {
        //Select the move with the highest win rate.
        int best;
        double bestScore = -1;
        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            if (root.myStats[i].attempts == 0)
            {
                continue;
            }

            double score = double(root.myStats[i].wins) / root.myStats[i].attempts;
            if (score > bestScore)
            {
                bestScore = score;
                best = i;
            }
        }

        return best;

        ////Select a move weighted by the number of times it has won the game.
        //int totalScore = 0;
        //for (int i = 0; i < TOTAL_ACTIONS; i++)
        //{
        //  totalScore += root.myStats[i].wins;
        //}
        //int selection = Random(totalScore);
        //for (int i = 0; i < TOTAL_ACTIONS; i++)
        //{
        //  selection -= root.myStats[i].wins;
        //  if (selection < 0)
        //  {
        //      return i;
        //  }
        //}

        ////Select a random move weighted by win ratio.
        //double totalScore = 0;
        //for (int i = 0; i < TOTAL_ACTIONS; i++)
        //{
        //  if (root.myStats[i].attempts == 0)
        //  {
        //      continue;
        //  }
        //  totalScore += double(root.myStats[i].wins) / root.myStats[i].attempts;
        //}
        //double selection = Random(totalScore);
        //for (int i = 0; i < TOTAL_ACTIONS; i++)
        //{
        //  if (root.myStats[i].attempts == 0)
        //  {
        //      continue;
        //  }
        //  selection -= double(root.myStats[i].wins) / root.myStats[i].attempts;
        //  if (selection < 0)
        //  {
        //      return i;
        //  }
        //}
    }

    //My own random functions.
    int Random(int max)
    {
        return GetRandomInteger(max - 1);
    }
    double Random(double max)
    {
        static auto seed = std::chrono::system_clock::now().time_since_epoch().count();
        static std::default_random_engine generator((unsigned)seed);
        std::uniform_real_distribution<double> distribution(0.0, max);
        return distribution(generator);
    }
};
//We have to initialize this here for some reason.
int MontePlayer::opponentHistory[MAX_PLAYERS][MAX_TURNS][MAX_TURNS][TOTAL_ACTIONS]{ { { { 0 } } } };

#endif // !__Monte_PLAYER_HPP__

25

BlackHatPlayer

BlackHat Oyuncu mermi ve kalkanların geçmişte kaldığını; Asıl savaşlar rakibin programlarını kırabilecekler tarafından kazanılır.

Böylece, sabit bir metal kalkanı koyar ve işini yapmaya başlar.

İlk defa sorulduğunda fight, düşmanı hafızasında lokalize etmeye çalışır. Dövüş arenasının yapısı göz önüne alındığında, derleyicinin adresini (bir içine sarılmış unique_ptr) ve rakibinin diğerini yan yana koymasıyla sonuçlanacağı kesin.

Bu yüzden, BlackHat, kendine bir işaretçi bulana kadar, taşmadığından emin olmak için bazı basit sezgisel yöntemler kullanarak yığını dikkatlice yürüyor; daha sonra bitişik konumlardaki değerlerin makul bir şekilde rakibi olup olmadığını kontrol eder - benzer bir adres, kararsızın benzer adresi, makul typeid.

Onu bulmayı başarırsa, beynini emer ve onları bir kafa başı aptalınınkilerle değiştirir. Pratikte bu, rakibin işaretçisini vtable yerine Idiotvtable - her zaman ateş eden salak bir oyuncunun adresiyle değiştirerek yapılır .

Bunların hepsi başarılı olursa (ve testlerimde - Linux 64 bit'te gcc 6, 32 bit şarapta MinGW 4.8 - bu oldukça güvenilir şekilde çalışır), savaş kazanılır. Rakip ilk rauntta ne yaptıysa önemli değil - en kötüsü bizi vurdu ve metal kalkanı ele geçirdik.

Şu andan itibaren, sadece ateş eden bir aptalımız var; Kalkanımız her zaman açıktır, bu nedenle korunuruz ve ilk turda yaptığı ilk fightçağrıya bağlı olarak 1 ila 3 turda patlar .


Şimdi: Bunun derhal diskalifiye edilmesi gerektiğinden neredeyse eminim, ancak yukarıda belirtilen kuralların hiçbirini açıkça ihlal etmemem komik.

Yapmamanız gerekenler

  • Her turnuvanın başında tamamen rastgele bir şekilde verilen rakip tanımlayıcısı dışındaki rakibinizi tanımlamak için doğrudan bir yöntem kullanmamalısınız. Bir turnuvada yalnızca bir oyuncunun oyun boyunca kim olduğunu tahmin etmenize izin verilir.

BlackHat, rakibi tanımaya çalışmaz - aslında, beyninin derhal değiştirildiği taktirde rakibin kim olduğu tamamen alakasızdır.

  • Player sınıfında sanal olarak bildirilmeyen hiçbir yöntemi geçersiz kılmamalısınız.
  • Global kapsamda hiçbir şey beyan etmemeli veya başlamamalısınız.

Her şey yerel olarak fightsanal işleve olur.


// BlackHatPlayer.hpp

#ifndef __BLACKHAT_PLAYER_HPP__
#define __BLACKHAT_PLAYER_HPP__

#include "Player.hpp"
#include <stddef.h>
#include <typeinfo>
#include <algorithm>
#include <string.h>

class BlackHatPlayer final : public Player
{
public:
    using Player::Player;

    virtual Action fight()
    {
        // Always metal; if the other is an Idiot, he only shoots,
        // and if he isn't an Idiot yet (=first round) it's the only move that
        // is always safe
        if(tricked) return metal();
        // Mark that at the next iterations we don't have to do all this stuff
        tricked = true;

        typedef uintptr_t word;
        typedef uintptr_t *pword;
        typedef uint8_t *pbyte;

        // Size of one memory page; we use it to walk the stack carefully
        const size_t pageSize = 4096;
        // Maximum allowed difference between the vtables
        const ptrdiff_t maxVTblDelta = 65536;
        // Maximum allowed difference between this and the other player
        ptrdiff_t maxObjsDelta = 131072;

        // Our adversary
        Player *c = nullptr;

        // Gets the start address of the memory page for the given object
        auto getPage = [&](void *obj) {
            return pword(word(obj) & (~word(pageSize-1)));
        };
        // Gets the start address of the memory page *next* to the one of the given object
        auto getNextPage = [&](void *obj) {
            return pword(pbyte(getPage(obj)) + pageSize);
        };

        // Gets a pointer to the first element of the vtable
        auto getVTbl = [](void *obj) {
            return pword(pword(obj)[0]);
        };

        // Let's make some mess to make sure that:
        // - we have an actual variable on the stack;
        // - we call an external (non-inline) function that ensures everything
        //   is spilled on the stack
        // - the compiler actually generates the full vtables (in the current
        //   tournament this shouldn't be an issue, but in earlier sketches
        //   the compiler inlined everything and killed the vtables)
        volatile word i = 0;
        for(const char *sz = typeid(*(this+i)).name(); *sz; ++sz) i+=*sz;

        // Grab my vtable
        word *myVTbl = getVTbl(this);

        // Do the stack walk
        // Limit for the stack walk; use i as a reference
        word *stackEnd = getNextPage((pword)(&i));
        for(word *sp = pword(&i);       // start from the location of i
            sp!=stackEnd && c==nullptr;
            ++sp) {                     // assume that the stack grows downwards
            // If we find something that looks like a pointer to memory
            // in a page just further on the stack, take it as a clue that the
            // stack in facts does go on
            if(getPage(pword(*sp))==stackEnd) {
                stackEnd = getNextPage(pword(*sp));
            }
            // We are looking for our own address on the stack
            if(*sp!=(word)this) continue;

            auto checkCandidate = [&](void *candidate) -> Player* {
                // Don't even try with NULLs and the like
                if(getPage(candidate)==nullptr) return nullptr;
                // Don't trust objects too far away from us - it's probably something else
                if(abs(pbyte(candidate)-pbyte(this))>maxObjsDelta) return nullptr;
                // Grab the vtable, check if it actually looks like one (it should be
                // decently near to ours)
                pword vtbl = getVTbl(candidate);
                if(abs(vtbl-myVTbl)>maxVTblDelta) return nullptr;
                // Final check: try to see if its name looks like a "Player"
                Player *p = (Player *)candidate;
                if(strstr(typeid(*p).name(), "layer")==0) return nullptr;
                // Jackpot!
                return p;
            };

            // Look around us - a pointer to our opponent should be just near
            c = checkCandidate((void *)sp[-1]);
            if(c==nullptr) c=checkCandidate((void *)sp[1]);
        }

        if(c!=nullptr) {
            // We found it! Suck his brains out and put there the brains of a hothead idiot
            struct Idiot : Player {
                virtual Action fight() {
                    // Always fire, never reload; blow up in two turns
                    // (while we are always using the metal shield to protect ourselves)
                    return bullet();
                }
            };
            Idiot idiot;
            // replace the vptr
            (*(word *)(c)) = word(getVTbl(&idiot));
        }
        // Always metal shield to be protected from the Idiot
        return metal();
    }
private:
    bool tricked = false;
};

#endif // !__BLACKHAT_PLAYER_HPP__

6
@TheNumberOne: Ayrıca, boşluklar konusuna ilk (ve en çok oy alan) yorumuna göre: "Loopholes, oyunu ilginç kılan şeyin bir parçasıdır. IMO bu orjinaldir (en azından burada hiçbir zaman benzer bir şey görmedim) ve terbiyesizce ilginç; bu yüzden burada paylaştım.
Matteo Italia

3
#ifdef __BLACKHAT_PLAYER_HPP__#error "Dependency issue; to compile, please include this file before BlackHatPlayer.hpp"#else#define __BLACKHAT_PLAYER_HPP__#endif
H Walters

1
@MatteoItalia BlackHat her zaman standart boşluklar hakkındaki bilgimizi artırır :-)
Frenzy Li

2
@HWalters: #pragma once;-)
Matteo Italia

3
Her oyuncu ayrı bir süreçte çalıştırmak ve hakem ile iletişim kurmak için soketleri kullanmak yeterince basit görünüyor.
Jasen

19

Sonra, tüm yaratıklardan en çok korkulan şey cehenneme döndü ve geri döndü ve kelimenin tam anlamıyla 900000 diğer botla savaştı ...

BotRobot

BotRobot, çok basit bir Genetik algoritma tarafından otomatik olarak adlandırıldı, eğitildi ve üretildi.

Her kuşakta, 1. takımdaki her robot, 2. takımın her robotuna karşı kurulmuştur. İki takım, her takımda, 2. takımın her robotuna karşı kurulmuştur. ve umarım kötü bir şeyi unutmak için bir şansım oldu. Botlar, daha önce görmedikleri bir şey bulduklarında, rastgele geçerli bir seçenek seçip hafızaya kaydedecekleri, yüceltilmiş arama tablolarıdır. C ++ sürümü bunu yapmaz , öğrenmiş olması gerekir . Daha önce de belirtildiği gibi, kazanan botlar bu yeni bulunan hafızayı net bir şekilde çalıştığı gibi tutar. Kaybeden botlar kaybetmezler ve neyle başladıklarını korurlar.

Sonunda, bot kavgaları nadiren düzensiz, oldukça yakındı. Kazanan iki takımın evriminden sonra 100000 nesiller olan bir havuzdan seçildi .

Rastgele oluşturulmuş ve GÜZEL ismi ile BotRobot şanslıydı.

Jeneratör

bot.lua

Revizyon: Robot kendine ve diğer benzer robotlara karşı oldukça zeki olmasına rağmen, gerçek savaşlarda oldukça yararsız olduğunu kanıtladı. Bu yüzden beynini, yaratılmış olan botların bir kısmına karşı yeniden ürettim.

Sonuçlar kolayca görülebildiği gibi, 12 mühimmatlı düşman oyuncuya kadar seçeneklerle çok daha karmaşık bir beyin .

Buna karşı savaştığı şeye emin değilim, 12 mühimmat aldı ama bir şeyler oldu.

Ve elbette, bitmiş ürün ...

// BotRobot
// ONE HUNDRED THOUSAND GENERATIONS TO MAKE THE ULTIMATE LIFEFORM!

#ifndef __BOT_ROBOT_PLAYER_HPP__
#define __BOT_ROBOT_PLAYER_HPP__

#include "Player.hpp"

class BotRobotPlayer final : public Player
{
public:
    BotRobotPlayer(size_t opponent = -1) : Player(opponent) {}

public:
    virtual Action fight()
    {
        std::string action = "";
        action += std::to_string(getAmmo());
        action += ":";
        action += std::to_string(getAmmoOpponent());

        int toDo = 3;

        for (int i = 0; i < int(sizeof(options)/sizeof(*options)); i++) {
            if (options[i].compare(action)==0) {
                toDo = outputs[i];
                break;
            }
        }

        switch (toDo) {
            case 0:
                return load();
            case 1:
                return bullet();
            case 2:
                return plasma();
            case 3:
                return metal();
            default:
                return thermal();
        }
    }

private:
    std::string options[29] =
    {
        "0:9",
        "1:12",
        "1:10",
        "0:10",
        "1:11",
        "0:11",
        "0:6",
        "2:2",
        "0:2",
        "2:6",
        "3:6",
        "0:7",
        "1:3",
        "2:3",
        "0:3",
        "2:0",
        "1:0",
        "0:4",
        "1:4",
        "2:4",
        "0:0",
        "3:0",
        "1:1",
        "2:1",
        "2:9",
        "0:5",
        "0:8",
        "3:1",
        "0:1"
    };

    int outputs[29] =
    {
        0,
        1,
        1,
        4,
        1,
        0,
        0,
        4,
        4,
        0,
        0,
        3,
        0,
        1,
        3,
        0,
        1,
        4,
        0,
        1,
        0,
        1,
        0,
        3,
        4,
        3,
        0,
        1,
        0
    };
};

#endif // !__BOT_ROBOT_PLAYER_HPP__

Ben nefret şimdi ++ C ...


@FrenzyLi Bunu nasıl farketmediğimden emin değilim, şimdi düzeltiyorum.
ATaco,

Eh, bu güncellemeden sonra, bot sabit bir açılış var gibi görünüyor 00.
Frenzy Li

Neden şimdi ... "1: 1" "0" veriyor.
Frenzy Li

1
Buradaki birkaç oyuncu tüm oyunlarını sırayla belirledi, bu yüzden sabit bir açılışın bir sorun olacağını düşünmüyorum
eis

10

CBetaPlayer (Cp)

Yaklaşık Nash Dengesi.

Bu bot sadece kod sarmalayıcıyla süslü bir matematik.

Bunu oyun teorisi problemi olarak yeniden çerçevelendirebiliriz. Bir kazancı +1 ve -1 ile bir kaybı gösterir. Şimdi B (x, y), x cephanemizin olduğu ve rakibimizin y cephanesi olduğu oyunun değeri olsun. B (a, b) = -B (b, a) ve böylece B (a, a) = 0 olduğuna dikkat edin. B değerlerini diğer B değerleri açısından bulmak için, ödeme matrisinin değerini hesaplayabiliriz. Örneğin, B (1, 0) aşağıdaki alt-oyunun değeri ile verildi:

       load      metal
load    B(0, 1)   B(2, 0)
bullet  +1        B(0, 0)

("Kötü" seçenekleri, yani mevcut çözümler tarafından kesinlikle tahakkuk ettirilmiş olanları kaldırdım. Örneğin, sadece 1 cephanemiz olduğu için plazma çekmeye çalışmayız. Aynı şekilde rakibimiz asla termal saptırıcı kullanmazdı. çünkü asla plazma çekmeyeceğiz.)

Oyun teorisi bize bazı teknik koşulları dikkate alarak bu kazanç matrisinin değerini nasıl bulacağımızı bilmemizi sağlar. Yukarıdaki matrisin değerinin şöyle olduğunu görüyoruz:

                B(2, 0)
B(1, 0) = ---------------------
          1 + B(2, 0) - B(2, 1)

Tüm olası oyunlar için devam etmek ve B (x, y) -> 1 olarak x -> sonsuz olarak sabit olduğuna dikkat ederek, tüm B değerlerini bulabiliriz; bu da mükemmel hareketleri hesaplayabilmemizi sağlar!

Tabii ki, teori nadiren gerçeğe göre sıralanır. Denklemi, küçük x ve y değerleri için bile çözmek çok karmaşık bir hal alır. Bununla başa çıkmak için cβ-yaklaşımı dediğim şeyi tanıttım. Bu yaklaşımın 7 parametresi vardır: c0, β0, c1, β1, c, β ve k. B değerlerinin aşağıdaki formu aldığını varsaydım (önce en özel formlar):

B(1, 0) = k
B(x, 0) = 1 - c0 β0^x
B(x, 1) = 1 - c1 β1^x
B(x, y) = 1 - c β^(x - y)   (if x > y)

Bu parametreleri neden seçtiğime dair bazı kaba akıl yürütme. Öncelikle, her biri farklı seçenekler açtığı için ayrı ayrı 0, 1 ve 2 veya daha fazla cephane bulundurmak istediğimi biliyordum. Ayrıca, geometrik bir hayatta kalma fonksiyonunun en uygun olacağını düşündüm, çünkü savunma oyuncusu esasen hangi hareketi yapacağını tahmin ediyor. 2 ya da daha fazla cephane bulundurmanın temelde aynı olduğunu düşündüm, bu yüzden aradaki farka odaklandım. Ayrıca B (1, 0) 'a süper özel bir vaka olarak bakmak istedim çünkü çok fazla görüneceğini düşündüm. Bu yaklaşık formları kullanmak, B değerlerinin hesaplamasını büyük ölçüde basitleştirmiştir.

Sonuçtaki denklemleri her bir B değerini elde etmek için çözdüm, sonra geri ödeme matrislerini elde etmek için matrisi geri koydum. Sonra doğrusal bir programlama çözücüsü kullanarak, her hareketi yapmak için en uygun olasılıkları buldum ve programa sürdüm.

Program, yüceltilmiş bir arama tablosu. Her iki oyuncunun da 0-4 mühimmatı varsa, hangi hamleyi yapması gerektiğini rastgele belirlemek için olasılık matrisini kullanır. Aksi takdirde, tabloya dayanarak hesaplanmaya çalışır.

Aptal deterministik botlara karşı problemi var, ama rasyonel botlara karşı oldukça başarılı. Tüm yaklaşımlar nedeniyle, bu bazen gerekmemesi gereken StudiousPlayer'a kaybolacaktır.

Tabii, eğer bunu tekrar yapacak olsaydım, muhtemelen daha bağımsız parametreler veya belki daha iyi bir ansatz formu eklemeye çalışırdım ve daha kesin bir çözüm bulurdum. Ayrıca ben (bilerek) dönüş sınırını görmezden geldim, çünkü işleri daha da zorlaştırdı. Yeterli cephane varsa ve yeterince geri dönüş yoksa, her zaman plazmayı ateşlemek için hızlı bir değişiklik yapılabilir.

// CBetaPlayer (cβ)
// PPCG: George V. Williams

#ifndef __CBETA_PLAYER_HPP__
#define __CBETA_PLAYER_HPP__

#include "Player.hpp"
#include <iostream>

class CBetaPlayer final : public Player
{
public:
    CBetaPlayer(size_t opponent = -1) : Player(opponent)
    {
    }

public:
    virtual Action fight()
    {
        int my_ammo = getAmmo(), opp_ammo = getAmmoOpponent();

        while (my_ammo >= MAX_AMMO || opp_ammo >= MAX_AMMO) {
            my_ammo--;
            opp_ammo--;
        }

        if (my_ammo < 0) my_ammo = 0;
        if (opp_ammo < 0) opp_ammo = 0;

        double cdf = GetRandomDouble();
        int move = -1;
        while (cdf > 0 && move < MAX_MOVES - 1)
            cdf -= probs[my_ammo][opp_ammo][++move];

        switch (move) {
            case 0: return load();
            case 1: return bullet();
            case 2: return plasma();
            case 3: return metal();
            case 4: return thermal();
            default: return fight();
        }
    }

    static double GetRandomDouble() {
        static auto seed = std::chrono::system_clock::now().time_since_epoch().count();
        static std::default_random_engine generator((unsigned)seed);
        std::uniform_real_distribution<double> distribution(0.0, 1.0);
        return distribution(generator);
    }

private:
    static const int MAX_AMMO = 5;
    static const int MAX_MOVES = 5;

    double probs[MAX_AMMO][MAX_AMMO][5] =
        {
            {{1, 0, 0, 0, 0}, {0.58359, 0, 0, 0.41641, 0}, {0.28835, 0, 0, 0.50247, 0.20918}, {0.17984, 0, 0, 0.54611, 0.27405}, {0.12707, 0, 0, 0.56275, 0.31018}},
            {{0.7377, 0.2623, 0, 0, 0}, {0.28907, 0.21569, 0, 0.49524, 0}, {0.0461, 0.06632, 0, 0.53336, 0.35422}, {0.06464, 0.05069, 0, 0.43704, 0.44763}, {0.02215, 0.038, 0, 0.33631, 0.60354}},
            {{0.47406, 0.37135, 0.1546, 0, 0}, {0.1862, 0.24577, 0.15519, 0.41284, 0}, {0, 0.28343, 0.35828, 0, 0.35828}, {0, 0.20234, 0.31224, 0, 0.48542}, {0, 0.12953, 0.26546, 0, 0.605}},
            {{0.33075, 0.44563, 0.22362, 0, 0}, {0.17867, 0.20071, 0.20071, 0.41991, 0}, {0, 0.30849, 0.43234, 0, 0.25916}, {0, 0.21836, 0.39082, 0, 0.39082}, {0, 0.14328, 0.33659, 0, 0.52013}},
            {{0.24032, 0.48974, 0.26994, 0, 0}, {0.14807, 0.15668, 0.27756, 0.41769, 0}, {0, 0.26804, 0.53575, 0, 0.19621}, {0, 0.22106, 0.48124, 0, 0.2977}, {0, 0.15411, 0.42294, 0, 0.42294}}
        };


};

#endif // !__CBETA_PLAYER_HPP__

Bir parametreyi geçmediğiniz GetRandomDoubleiçin, max değişkenini kaldırabilirsiniz.
Frenzy Li

@FrenzyLi, whoops, teşekkürler!
George V. Williams,

Tensör olasılığına nasıl geldiğiniz gibi, oyuncuya biraz daha bilgi ekler misiniz?
Frenzy Li

2
Ben seviyorum bu bot. SP'nin şu ana kadar yalnızca diğer girdilerin belirleyiciliği nedeniyle avantajı olduğunu düşünüyorum; ne kadar (en iyi şekilde ağırlıklandırılmamış) rastgele botlar eklenirse, CBP ücretleri o kadar iyi olur. Bu test ile desteklenir; Her zamanki gibi şüphelilerle yaptığım iç sınavlarda SP her zaman CBP ile kazanıyor ... ancak CBP, SP ve FP ile ilgili mini bir yarışmada CBP, SP ve FP ile eşit bir şekilde yürüterek zamanın% 55'ini kesti.
H Walters

1
Bu arada, bu, nash dengesinin etkileyici olarak doğru bir yaklaşımıdır. Monte denge stratejisini bulmaya çalışmaz, fakat verilen herhangi bir rakibe karşı en iyi hamle. C and ile cβ arasındaki düelloların yalnızca% 52'sini kazanması, cβ'nin nash dengesine çok yakın bir şekilde sarkan olduğu anlamına geliyor.
TheNumberOne

8

Her yerde doğru yorum bulamıyorum, bu yüzden henüz sorularımı soramadım. Bu yüzden bu ilk botta kazanmak için çok basit bir oyuncu.

[Düzenle] Teşekkürler, şimdi önceki durum artık doğru değil ama bu botun içeriğini anlayabilmemiz için daha iyi olduğunu düşünüyorum.

Opportunist

Oportünist, GunClubPlayers ile aynı silah klübünü sık sık ziyaret etse de, her GunClubPlayers'ı yenebileceği konusunda yeni bir oyuncuya bahis yaptı. Bu yüzden uzun zamandır fark ettiği alışkanlıktan yararlanıyor ve kendisini çekmemeye zorlamakta, sadece kazanmak için biraz beklemektedir.

#ifndef __OPPORTUNIST_PLAYER_HPP__
#define __OPPORTUNIST_PLAYER_HPP__

#include <string>
#include <vector>

class OpportunistPlayer final: public Player
{
public:
    OpportunistPlayer(size_t opponent = -1) : Player(opponent) {}

public:
    virtual Action fight()
    {
        switch (getTurn() % 3)
        {
        case 0:
            return load();
            break;
        case 1:
            return metal();
            break;
        case 2:
            return bullet();
            break;
        }
        return plasma();
    }
};
#endif // !__OPPORTUNIST_PLAYER_HPP__

7

BarricadePlayer

Barricade Player ilk turda bir mermi yükler, daha sonra uygun bir kalkan tutar (hala biraz rastgele). Ayrıca her 5. turda bir kez daha atış yapıyor. Her turda, algoritmayı yok sayma (ilk sırayı yeniden yükleme hariç) ve bir kurşun atma şansı% 15'tir. Düşmanın cephanesi olmadığında yükler. Bir şekilde her şey ters giderse, oh oğlum, sadece vuruyor.

En yeni değişiklikler:

Geliştirilmiş rasgele sayılar (teşekkürler Frenzy Li).

// BarricadePlayer by devRicher
// PPCG: http://codegolf.stackexchange.com/a/104909/11933

// BarricadePlayer.hpp
// A very tactical player.

#ifndef __BARRICADE_PLAYER_HPP__
#define __BARRICADE_PLAYER_HPP__

#include "Player.hpp"
#include <cstdlib>
#include <ctime>

class BarricadePlayer final : public Player
{
public:
    BarricadePlayer(size_t opponent = -1) : Player(opponent) {}

public:
    virtual Action fight()
    {
        srand(time(NULL));
        if (getTurn() == 0) { return load(); }
        int r = GetRandomInteger(99) + 1; //Get a random
        if ((r <= 15) && (getAmmo() > 0)) { return bullet(); } //Override any action, and just shoot
        else
        {
            if (getTurn() % 5 == 0) //Every first and fifth turn
                return load();
            if (getAmmoOpponent() == 1) return metal();
            if (getAmmoOpponent() > 1) { return r <= 50 ? metal() : thermal(); }
            if (getAmmoOpponent() == 0) return load();

        }
        return bullet();
    }
};

#endif // !__BARRICADE_PLAYER_HPP__

1
En azından ateş etmeden önce cephane olup olmadığını kontrol etmek ister misin?
Pavel,

8
Hayır. Tehlikeli hayatı yaşıyorum. @Pavel
devRicher,

1
Isıl saptırıcıyı ikinci turda kullanmak anlamsız değil mi? İlk fırsatta iki mermi yükleyemezsin. Bence rasgele olmasını isteseniz bile, rakibin mermileri 1 (ya da daha az) ise termal zırhı kullanmaktan kaçınmalısınız.
Southpaw Hare

1
Tüm önerileriniz için teşekkürler, dersi çok değiştirdim. @SouthpawHare
devRicher

2
O var getAmmoOpponentdeğil getOpponentAmmo. Siz de kaçırıyorsunuz#endif // !__BARRICADE_PLAYER_HPP__
Blue

7

StudiousPlayer

Çalışkan Oyuncu avını karşılaştığında karşılaştığı her rakibi modellenir. Bu oyuncu, rastgele yerlere yönlendirilen temel bir strateji ile başlar ve rakip yanıtın sık ölçütlerine dayanan basit uyarlanabilir stratejilere ilerler. Cephane kombinasyonlarına nasıl tepki gösterdiklerini temel alan basit bir rakip model kullanıyor.

#ifndef __STUDIOUS_PLAYER_H__
#define __STUDIOUS_PLAYER_H__

#include "Player.hpp"
#include <unordered_map>

class StudiousPlayer final : public Player
{
public:
   using Player::GetRandomInteger;
   // Represents an opponent's action for a specific state.
   struct OpponentAction {
      OpponentAction(){}
      unsigned l=0;
      unsigned b=0;
      unsigned p=0;
      unsigned m=0;
      unsigned t=0;
   };
   // StudiousPlayer models every opponent that it plays,
   // and factors said model into its decisions.
   //
   // There are 16 states, corresponding to
   // 4 inner states (0,1,2,3) and 4 outer states
   // (0,1,2,3). The inner states represent our
   // (SP's) ammo; the outer represents the
   // Opponent's ammo.  For the inner or outer
   // states, 0-2 represent the exact ammo; and
   // 3 represents "3 or more".
   //
   // State n is (4*outer)+inner.
   //
   // State 0 itself is ignored, since we don't care
   // what action the opponent takes (we always load);
   // thus, it's not represented here.
   //
   // os stores states 1 through 15 (index 0 through 14).
   struct Opponent {
      std::vector<OpponentAction> os;
      Opponent() : os(15) {}
   };
   StudiousPlayer(size_t opponent)
      : Player(opponent)
      , strat(storedLs()[opponent])
      , ammoOpponent()
   {
   }
   Player::Action fight() {
      // Compute the current "ammo state".
      // For convenience here (aka, readability in switch),
      // this is a two digit octal number.  The lso is the
      // inner state, and the mso the outer state.
      unsigned ss,os;
      switch (ammoOpponent) {
      default: os=030; break;
      case 2 : os=020; break;
      case 1 : os=010; break;
      case 0 : os=000; break;
      }
      switch (getAmmo()) {
      default: ss=003; break;
      case 2 : ss=002; break;
      case 1 : ss=001; break;
      case 0 : ss=000; break;
      }
      // Store the ammo state.  This has a side effect
      // of causing actn() to return an OpponentAction
      // struct, with the opponent's history during this
      // state.
      osa = os+ss;
      // Get the opponent action pointer
      const OpponentAction* a=actn(osa);
      // If there's no such action structure, assume
      // we're just supposed to load.
      if (!a) return load();
      // Apply ammo-state based strategies:
      switch (osa) {
      case 001:
         // If opponent's likely to load, shoot; else load
         if (a->l > a->m) return bullet();
         return load();
      case 002:
      case 003:
         // Shoot in the way most likely to kill (or randomly)
         if (a->t > a->m+a->l) return bullet();
         if (a->m > a->t+a->l) return plasma();
         if (GetRandomInteger(1)) return bullet();
         return plasma();
      case 010:
         // If opponent tends to load, load; else defend
         if (a->l > a->b) return load();
         return metal();
      case 011:
         // Shoot if opponent tends to load
         if (a->l > a->b+a->m) return bullet();
         // Defend if opponent tends to shoot
         if (a->b > a->l+a->m) return metal();
         // Load if opponent tends to defend
         if (a->m > a->b+a->l) return load();
         // Otherwise randomly respond
         if (!GetRandomInteger(2)) return metal();
         if (!GetRandomInteger(1)) return load(); 
         return bullet();                         
      case 012:
      case 013:
         // If opponent most often shoots, defend
         if (a->b > a->l+a->m+a->t) return metal();
         // If opponent most often thermals, use bullet
         if (a->t > a->m) return bullet();
         // If opponent most often metals, use plasma
         if (a->m > a->t) return plasma();
         // Otherwise use a random weapon
         return (GetRandomInteger(1))?bullet():plasma();
      case 020:
         // If opponent most often loads or defends, load
         if (a->l+a->m+a->t > a->b+a->p) return load();
         // If opponent most often shoots bullets, raise metal
         if (a->b > a->p) return metal();
         // If opponent most often shoots plasma, raise thermal
         if (a->p > a->b) return thermal();
         // Otherwise raise random defense
         return (GetRandomInteger(1))?metal():thermal();
      case 021:
      case 031:
         // If opponent loads more often than not,
         if (a->l > a->m+a->b+a->p) {
            // Tend to shoot (67%), but possibly load (33%)
            return (GetRandomInteger(2))?bullet():load();
         }
         // If opponent metals more often than loads or shoots, load
         if (a->m > a->l+a->b+a->p) return load();
         // If opponent thermals (shrug) more often than loads or shoots, load
         if (a->t > a->l+a->b+a->p) return load();
         // If opponent tends to shoot bullets, raise metal
         if (a->b > a->p) return metal();
         // If opponent tends to shoot plasma, raise thermal
         if (a->p > a->b) return thermal();
         // Raise random shield
         return (GetRandomInteger(2))?metal():thermal();
      case 022:
         // If opponent loads or thermals more often than not, shoot bullet
         if (a->l+a->t > a->b+a->p+a->m) return bullet();
         // If opponent loads or metals more often than not, shoot plasma
         if (a->l+a->m > a->b+a->p+a->t) return plasma();
         // If opponent shoots more than loads or defends, defend
         if (a->b+a->p > a->l+a->m+a->t) {
            if (a->b > a->p) return metal();
            if (a->p > a->b) return thermal();
            return (GetRandomInteger(1))?metal():thermal();
         }
         // If opponent defends more than opponent shoots, load
         if (a->m+a->t > a->b+a->p) return load();
         // Use random substrategy;
         // load(33%)
         if (GetRandomInteger(2)) return load();
         // defend(33%)
         if (GetRandomInteger(1)) {
            if (a->b > a->p) return metal();
            if (a->b > a->b) return thermal();
            return (GetRandomInteger(1))?metal():thermal();
         }
         // Shoot in a way that most often kills (or randomly)
         if (a->m > a->t) return plasma();
         if (a->t > a->m) return bullet();
         return (GetRandomInteger(1))?bullet():plasma();
      case 023:
         // If opponent loads or raises thermal more often than not, shoot bullets
         if (a->l+a->t > a->b+a->p+a->m) return bullet();
         // If opponent loads or raises metal more often than not, shoot plasma
         if (a->l+a->m > a->b+a->p+a->t) return plasma();
         // If opponent shoots more than loads or defends, defend
         if (a->b+a->p > a->l+a->m+a->t) {
            if (a->b > a->p) return metal();
            if (a->p > a->b) return thermal();
            return (GetRandomInteger(1))?metal():thermal();
         }
         // If opponent defends more than shoots, shoot
         if (a->m+a->t > a->b+a->p) {
            if (a->m > a->t) return plasma();
            if (a->t > a->m) return bullet();
            return GetRandomInteger(1)?bullet():plasma();
         }
         // 50% defend
         if (GetRandomInteger(1)) {
            if (a->b > a->p) return metal();
            return thermal();
         }
         // 50% shoot
         if (a->m > a->t) return plasma();
         if (a->t > a->m) return bullet();
         return (GetRandomInteger(1))?bullet():plasma();
      case 030:
         // If opponent loads or shields more often than not, load
         if (a->l+a->m+a->t > a->b+a->p) return load();
         // If opponent tends to shoot, defend
         if (a->b+a->p >= a->l+a->m+a->t) {
            if (a->b > a->p) return metal();
            if (a->p > a->b) return thermal();
            return (GetRandomInteger(1))?metal():thermal();
         }
         // Otherwise, randomly shield (50%) or load
         if (GetRandomInteger(1)) {
            return (GetRandomInteger(1))?metal():thermal();
         }
         return load();
      case 032:
         // If opponent loads or thermals more often than not, shoot bullets
         if (a->l+a->t > a->b+a->p+a->m) return bullet();
         // If opponent loads or metals more often than not, shoot plasma
         if (a->l+a->m > a->b+a->p+a->t) return plasma();
         // If opponent shoots more often than loads or shields, defend
         if (a->b+a->p > a->l+a->m+a->t) {
            if (a->b > a->p) return metal();
            if (a->p > a->b) return thermal();
            return (GetRandomInteger(1))?metal():thermal();
         }
         // If opponent shields more often than shoots, load
         if (a->m+a->t > a->b+a->p) return load();
         // Otherwise use random strategy
         if (GetRandomInteger(2)) return load();
         if (GetRandomInteger(1)) {
            if (a->b > a->p) return metal();
            return thermal();
         }
         if (a->m > a->t) return plasma();
         if (a->t > a->m) return bullet();
         return (GetRandomInteger(1))?bullet():plasma();
      case 033:
         {
            // At full 3 on 3, apply random strategy
            // weighted by opponent's histogram of this state...
            // (the extra 1 weights towards plasma)
            unsigned sr=
               GetRandomInteger
               (a->l+a->t+a->p+a->b+a->m+1);
            // Shoot bullets proportional to how much
            // opponent loads or defends using thermal
            if (sr < a->l+a->t) return bullet();
            sr-=(a->l+a->t);
            // Defend with thermal proportional to how
            // much opponent attacks with plasma (tending to
            // waste his ammo)
            if (sr < a->p) return thermal();
            // Shoot plasma proportional to how
            // much opponent shoots bullets or raises metal
            return plasma();
         }
      }
      // Should never hit this; but rather than ruin everyone's fun,
      // if we do, we just load
      return load();
   }
   // Complete override; we use our opponent's model, not history.
   void perceive(Player::Action action) {
      // We want the ammo but not the history; since
      // the framework (Player::perceive) is "all or nothing", 
      // StudiousPlayer just tracks the ammo itself
      switch (action) {
      default: break;
      case Player::LOAD:   ++ammoOpponent; break;
      case Player::BULLET: --ammoOpponent; break;
      case Player::PLASMA: ammoOpponent-=2; break;
      }
      // Now we get the opponent's action based
      // on the last (incoming) ammo state
      OpponentAction* a = actn(osa);
      // ...if it's null just bail
      if (!a) return;
      // Otherwise, count the action
      switch (action) {
      case Player::LOAD    : ++a->l; break;
      case Player::BULLET  : ++a->b; break;
      case Player::PLASMA  : ++a->p; break;
      case Player::METAL   : ++a->m; break;
      case Player::THERMAL : ++a->t; break;
      }
   }
private:
   Opponent& strat;
   OpponentAction* actn(unsigned octalOsa) {
      unsigned ndx = (octalOsa%4)+4*(octalOsa/8);
      if (ndx==0) return 0;
      --ndx;
      if (ndx<15) return &strat.os[ndx];
      return 0;
   }
   unsigned osa;
   unsigned ammoOpponent;
   // Welcome, non-C++ persons, to the "Meyers style singleton".
   // "theMap" is initialized (constructed; initially empty)
   // the first time the declaration is executed.
   static std::unordered_map<size_t, Opponent>& storedLs() {
      static std::unordered_map<size_t, Opponent> theMap;
      return theMap;
   }
};

#endif

Bunun, rakiplerle ilgili bilgileri meydan okuma kurallarına göre takip ettiğini; altta "Meyers tarzı singleton" kapsam "saklı" (")" yöntemine bakın. (Bazı insanlar bunu nasıl yapacağını merak ediyorlardı; şimdi biliyorsunuz!)


1
Bunu görene kadar Meyers tarzı singleton denildiği hakkında hiçbir fikrim yoktu!
Frenzy Li

1
Terimi çok ciddiye almayın - "singleton" bildirilmiş bir yapıdan ziyade bir şablon başlatması olduğundan, bu bir terimin kötüye kullanılmasıdır, ancak aynı tekniktir.
H Walters

6

GunClubPlayer

Orijinal sorudan kesin. Bu, türetilmiş bir oyuncunun minimalist bir uygulamasına örnek teşkil eder. Bu oyuncu turnuvaya katılacak.

GunClubPlayerIn silahı kulübe gitmek ister. Her düello sırasında, önce cephane yükler, sonra bir mermi atarlar ve bu işlemi dünya düellolarının sonuna kadar tekrarlarlardı . Aslında kazanıp kazanmamaları umursamıyorlar ve sadece kendileri için eğlenceli bir deneyim yaşamaya odaklanıyorlar.

// GunClubPlayer.hpp
// A gun club enthusiast. Minimalistic example of derived class

#ifndef __GUN_CLUB_PLAYER_HPP__
#define __GUN_CLUB_PLAYER_HPP__

#include "Player.hpp"

class GunClubPlayer final: public Player
{
public:
    GunClubPlayer(size_t opponent = -1) : Player(opponent) {}

public:
    virtual Action fight()
    {
        return getTurn() % 2 ? bullet() : load();
    }
};

#endif // !__GUN_CLUB_PLAYER_HPP__

1
Bir iade ifadesinden sonra başka birine ihtiyacınız olmaz, değil mi? Kod golf olmadığını biliyorum ama yanlış hissettiriyor.
Pavel,

2
@Pavel Pekala, tamam, yani ... bu şimdi ... bir çeşit golf sahası.
Frenzy Li

5

PlasmaPlayer

Plazma Oyuncusu plazma cıvatalarını ateşlemek gibi. Mümkün olduğunca yüklenmeye ve ateş etmeye çalışacak. Ancak, rakip plazma cephanesi olmasına rağmen, termal kalkanını kullanacak (mermiler zayıflar için).

#ifndef __PLASMA_PLAYER_HPP__
#define __PLASMA_PLAYER_HPP__

#include "Player.hpp"

class PlasmaPlayer final : public Player
{
public:
    PlasmaPlayer(size_t opponent = -1) : Player(opponent) {}

    virtual Action fight()
    {
        // Imma Firin Mah Lazer!
        if (getAmmo() > 1) return plasma();

        // Imma Block Yur Lazer!
        if (getAmmoOpponent() > 1) return thermal();

        // Imma need more Lazer ammo
        return load();
    }
};

#endif // !__PLASMA_PLAYER_HPP__

@FrenzyLi yapıcı için teşekkürler! Benim C ++ biraz paslı ve bu makinede bir derleyici yok.
Brian J

Rica ederim! Hala projeye daha fazla kod ekliyorum (skorbord bas, dış betiği oku vb.) Ve henüz hiçbir gönderimin kırılmadığı için çok şanslıyım.
Frenzy Li

Bu GunClub dışındaki herhangi bir rakip için iyi çalışacaktır. Evet, SadisticShooter'ı öldürecek (en iyisi). @BrianJ
devRicher

5

Çok SadisticShooter

Seni öldürmekten çok acı çekmeni izlemeyi tercih ederdi. Aptal değil ve gerektiği gibi kendini koruyacak.

Eğer çok sıkıcı ve tahmin edilebilir biriyseniz, sizi hemen öldürecek.

// SadisticShooter by muddyfish
// PPCG: http://codegolf.stackexchange.com/a/104947/11933

// SadisticShooter.hpp
// A very sad person. He likes to shoot people.

#ifndef __SAD_SHOOTER_PLAYER_HPP__
#define __SAD_SHOOTER_PLAYER_HPP__

#include <cstdlib>
#include "Player.hpp"
// #include <iostream>

class SadisticShooter final : public Player
{
public:
    SadisticShooter(size_t opponent = -1) : Player(opponent) {}
private:
    bool historySame(std::vector<Action> const &history, int elements) {
        if (history.size() < elements) return false;

        std::vector<Action> lastElements(history.end() - elements, history.end());

        for (Action const &action : lastElements)
            if (action != lastElements[0]) return false;
        return true;
    }
public:
    virtual Action fight()
    {
        int my_ammo = getAmmo();
        int opponent_ammo = getAmmoOpponent();
        int turn_number = getTurn();
        //std::cout << " :: Turn " << turn_number << " ammo: " << my_ammo << " oppo: " << opponent_ammo << std::endl;

        if (turn_number == 90) {
            // Getting impatient
            return load();
        }
        if (my_ammo == 0 && opponent_ammo == 0) {
            // It would be idiotic not to load here
            return load();
        }
        if (my_ammo >= 2 && historySame(getHistoryOpponent(), 3)) {
            if (getHistoryOpponent()[turn_number - 1] == THERMAL) return bullet();
            if (getHistoryOpponent()[turn_number - 1] == METAL) return thermal();
        }
        if (my_ammo < 2 && opponent_ammo == 1) {
            // I'd rather not die thank you very much
            return metal();
        }
        if (my_ammo == 1) {
            if (opponent_ammo == 0) {
                // You think I would just shoot you?
                return load();
            }
            if (turn_number == 2) {
                return thermal();
            }
            return bullet();
        }
        if (opponent_ammo >= 2) {
            // Your plasma weapon doesn't scare me
            return thermal();
        }
        if (my_ammo >= 2) {
            // 85% more bullet per bullet
            if (turn_number == 4) return bullet();
            return plasma();
        }
        // Just load the gun already
        return load();
    }
};

#endif // !__SAD_SHOOTER_PLAYER_HPP__

Gördün mü düzelttin.
devRicher

4

TurtlePlayer

TurtlePlayerbir korkak. Zamanının çoğunu kalkanlarının arkasına saklanarak harcıyor - yani isim. Bazen, kabuğundan çıkıp ateş açabilir ve bir atış yapabilir, ancak düşmanı cephane varken normalde düşük seviyededir.


Bu bot özellikle harika değil - ancak, her KOTH'nin çalışması için bazı ilk girişlere ihtiyacı var :)

Yerel test bu ikisi karşı kazanır bulundu GunClubPlayerve Opportunistzaman% 100. Karşı bir savaş BotRobotPlayerher zaman kalkanlarının arkasına gizlenerek bir çekilişe yol açtı.

#include "Player.hpp"

// For randomness:
#include <ctime>
#include <cstdlib>

class TurtlePlayer final : public Player {

public:
    TurtlePlayer(size_t opponent = -1) : Player(opponent) { srand(time(0)); }

public:
    virtual Action fight() {
        if (getAmmoOpponent() > 0) {
            // Beware! Opponent has ammo!

            if (rand() % 5 == 0 && getAmmo() > 0) 
                // YOLO it:
                return getAmmo() > 1 ? plasma() : bullet();

            // Play it safe:
            if (getAmmoOpponent() == 1) return metal();
            return rand() % 2 ? metal() : thermal();
        }

        if (getAmmo() == 0) 
            // Nobody has ammo: Time to load up.
            return load();

        else if (getAmmo() > 1) 
            // We have enough ammo for a plasma: fire it!
            return plasma();

        else 
            // Either load, or take a shot.
            return rand() % 2 ? load() : bullet();
    }
};

4

DeceptivePlayer

Aldatıcı Oyuncu iki mermi yüklemeye çalışır ve sonra bir tane ateşler.

// DeceiverPlayer.hpp
// If we have two shoots, better shoot one by one

#ifndef __DECEPTIVE_PLAYER_HPP__
#define __DECEPTIVE_PLAYER_HPP__

#include "Player.hpp"

class DeceptivePlayer final: public Player
{
public:
    DeceptivePlayer(size_t opponent = -1) : Player(opponent) {}

public:
    virtual Action fight()
    {
        int ammo = getAmmo();
        int opponentAmmo = getAmmoOpponent();
        int turn = getTurn();

        // Without ammo, always load
        if (ammo == 0)
        {
            return load();
        }

        // Every 10 turns the Deceiver goes crazy
        if (turn % 10 || opponentAmmo >= 3)
        {
            // Generate random integer in [0, 5)
            int random = GetRandomInteger() % 5;
            switch (random)
            {
            case 0:
                return bullet();
            case 1:
                return metal();
            case 2:
                if (ammo == 1)
                {
                    return bullet();
                }

                return plasma();
            case 3:
                return thermal();
            case 4:
                return load();
            }
        }

        // The Deceiver shoots one bullet
        if (ammo == 2)
        {
            return bullet();
        }

        // Protect until we can get bullet 2
        if (opponentAmmo == 0)
        {
            return load();
        }

        if (opponentAmmo == 1)
        {
            return metal();
        }

        if (opponentAmmo == 2)
        {
            return thermal();
        }
    }
};

#endif // !__DECEPTIVE_PLAYER_HPP__

C ++ kodunu kullanmıyorum, bu nedenle kodda yapılacak iyileştirmeler memnuniyetle karşılanacaktır.


Düzenlemem modülo ve makro tanımı üzerine. Hoşunuza gideceğinden emin değilsiniz, ama belki DeceptivePlayerdaha iyi bir isimdir?
Frenzy Li

@FrenzyLi Evet, beğendim, adını değiştireceğim
Sxntk

1
@Sxntk Bu oyuncunun 2 mermili insanlara plazma atmasını beklediği ironiyi sevdim, ama kendisi iki mermi çekip mermi atacak.
Brian J

@Sxntk Şu anda hiçbir şey döndürmeme ihtimaliniz yok. Bir oyuncuya ikiden fazla cephane girebilir. Yani rakibinizde 3+ cephane varsa, hiçbir şey yapmazsınız. Bir yerde patlamış bir silahla kurulabilir. (Tabii ki, bu zaten senin ana planın olabilir :))
Brian J

@BrianJ Teşekkürler, bu konuda düşüneceğim, bu arada Aldatıcı'nın çıldırmasına izin veriyorum ve açıklarda 3+ cephane olduğunda ne yapılacağına karar vereceğim
Sxntk

2

HanSoloPlayer

İlk önce vuruyor! Hala revize etmeye çalışıyorum ama bu oldukça iyi.

// HanSoloPlayer.hpp
// A reluctant rebel. Always shoots first.

// Revision 1: [13HanSoloPlayer][17] | 6 rounds | 2863

#ifndef __HAN_SOLO_PLAYER_HPP__
#define __HAN_SOLO_PLAYER_HPP__

#include "Player.hpp"

class HanSoloPlayer final: public Player
{
public:
    HanSoloPlayer(size_t opponent = -1) : Player(opponent) {}

public:
    virtual Action fight()
    {
        if(getTurn() == 0){
            // let's do some initial work
            agenda.push_back(bullet());     // action 2--han shot first!
            agenda.push_back(load());       // action 1--load a shot
        } else if(getTurn() == 2){
            randomDefensive();
        } else if(getRandomBool(2)){
            // go on the defensive about 1/3rd of the time
            randomDefensive();
        } else if(getRandomBool(5)){
            // all-out attack!
            if(getAmmo() == 0){
                // do nothing, let the agenda work its course
            } else if(getAmmo() == 1){
                // not quite all-out... :/
                agenda.push_back(load());   // overnext
                agenda.push_back(bullet()); // next
            } else if(getAmmo() == 2){
                agenda.push_back(load());   // overnext
                agenda.push_back(plasma()); // next
            } else {
                int ammoCopy = getAmmo();
                while(ammoCopy >= 2){
                    agenda.push_back(plasma());
                    ammoCopy -= 2;
                }
            }
        }

        // execute the next item on the agenda
        if(agenda.size() > 0){
            Action nextAction = agenda.back();
            agenda.pop_back();
            return nextAction;
        } else {
            agenda.push_back(getRandomBool() ? thermal() : bullet()); // overnext
            agenda.push_back(load());                                 // next
            return load();
        }
    }
private:
    std::vector<Action> agenda;
    bool getRandomBool(int weight = 1){
        return GetRandomInteger(weight) == 0;
    }
    void randomDefensive(){
        switch(getAmmoOpponent()){
            case 0:
                // they most likely loaded and fired. load, then metal shield
                agenda.push_back(metal());  // action 4
                agenda.push_back(load());   // action 3
                break;
            case 1:
                agenda.push_back(metal());
                break;
            case 2:
                agenda.push_back(getRandomBool() ? thermal() : metal());
                break;
            default:
                agenda.push_back(getRandomBool(2) ? metal() : thermal());
                break;
        }
        return;
    }
};

#endif // !__HAN_SOLO_PLAYER_HPP__

2

CamtoPlayer

CamtoPlayer HATES , ne pahasına olursa olsun döngüler çizer ve kopar. (intihar hariç)

Bir şey yapan ilk C ++ programım, bu yüzden çok fazla yargılama.

Daha iyi olabileceğini biliyorum ama lütfen düzenlemeyin.
Eğer kodu değiştirmek istiyorsanız, sadece bir öneride bulunun.

#ifndef __CAMTO_HPP__
#define __CAMTO_HPP__

#include "Player.hpp"
#include <iostream>

class CamtoPlayer final : public Player
{
public:
    CamtoPlayer(size_t opponent = -1) : Player(opponent) {}
        int S = 1; // Switch between options. (like a randomness function without any randomness)
        bool ltb = false; // L.ast T.urn B.locked
        bool loop = false; // If there a loop going on.
        int histarray[10]={0,0,0,0,0,0,0,0,0,0}; // The last ten turns.
        int appears(int number) { // How many times a number appears(); in histarray, used for checking for infinite loops.
            int things = 0; // The amount of times the number appears(); is stored in things.
            for(int count = 0; count < 10; count++) { // For(every item in histarray) {if its the correct number increment thing}.
                if(histarray[count]==number) {things++;}
            }
            return things; // Return the result
        }
    virtual Action fight()
    {
        int ammo = getAmmo(); // Ammo count.
        int bad_ammo = getAmmoOpponent(); // Enemy ammo count.
        int turn = getTurn(); // Turn count.
        int pick = 0; // This turn's weapon.

        if(appears(2)>=4){loop=true;} // Simple loop detection
        if(appears(3)>=4){loop=true;} // by checking if
        if(appears(4)>=4){loop=true;} // any weapong is picked a lot
        if(appears(5)>=4){loop=true;} // except for load();

        if(ammo==0&&bad_ammo==1){pick=4;} // Block when he can shoot me.
        if(ammo==0&&bad_ammo>=2){S++;S%2?(pick=4):(pick=5);} // Block against whatever might come!
        if(ammo==0&&bad_ammo>=1&&ltb){pick=1;} // If L.ast T.urn B.locked, then reload instead.
        if(ammo==1&&bad_ammo==0){pick=2;} // Shoot when the opponent can't shoot.
        if(ammo==1&&bad_ammo==1){S++;S%2?(pick=2):(pick=4);} // No risk here.
        if(ammo==1&&bad_ammo>=2){S++;S%2?(pick=4):(pick=5);} // Block!
        if(ammo==1&&bad_ammo>=1&&ltb){pick=2;} // If ltb shoot instead.
        if(ammo>=2){S++;S%2?(pick=2):(pick=3);} // Shoot something!

        /* debugging
            std :: cout << "Turn data: turn: ";
            std :: cout << turn;
            std :: cout << " loop: ";
            std :: cout << loop;
            std :: cout << " ";
            std :: cout << "ltb: ";
            std :: cout << ltb;
            std :: cout << " ";
        */

        // Attempt to break out of the loop. (hoping there is one)
        if(ammo==0&&loop){pick=1;} // After many turns of waiting, just load();
        if(ammo==1&&bad_ammo==0&&loop){loop=false;pick=1;} // Get out of the loop by loading instead of shooting.
        if(ammo==1&&bad_ammo==1&&loop){loop=false;pick=4;} // Get out of the loop (hopefully) by blocking.
        if(ammo>=2&&loop){loop=false;S++;S%2?(pick=2):(pick=3);} // Just shoot.
        if(turn==3&&(appears(1)==2)&&(appears(2)==1)){pick=4;} // If it's just load();, shoot();, load(); then metal(); because it might be a loop.
        // End of loop breaking.

        if(turn==1){pick=2;} // Shoot right after reloading!
        if(ammo==0&&bad_ammo==0){pick=1;} // Always load when no one can shoot.

        for(int count = 0; count < 10; count++) {
            histarray[count]=histarray[count+1]; // Shift all values in histarray[] by 1.
        }
        histarray[9] = pick; // Add the picked weapon to end of histarray[].

        /*  more debugging
            std :: cout << "history: ";
            std :: cout << histarray[0];
            std :: cout << histarray[1];
            std :: cout << histarray[2];
            std :: cout << histarray[3];
            std :: cout << histarray[4];
            std :: cout << histarray[5];
            std :: cout << histarray[6];
            std :: cout << histarray[7];
            std :: cout << histarray[8];
            std :: cout << histarray[9];

            std :: cout << " pick, ammo, bammo: ";
            std :: cout << pick;
            std :: cout << " ";
            std :: cout << ammo;
            std :: cout << " ";
            std :: cout << bad_ammo;
            std :: cout << "\n";
        */
        switch(pick) {
            case 1:
                ltb = false; return load();
            case 2:
                ltb = false; return bullet();
            case 3:
                ltb = false; return plasma();
            case 4:
                ltb = true;return metal();
            case 5:
                ltb = true;return thermal();
        }

    }
};

#endif // !__CAMTO_HPP__

Bir unutuyorsun#endif // ! __CAMTO_HPP__
Mavi

@muddyfish Söylediğiniz için teşekkürler, kodun oluşturulmasını durduran sembollerden daha az simgesine sahibim! XD
Benjamin Philippe,

Hala görünmüyorum. HTML etiketlerini tamamen ve sadece işaretleme (üzerinde "{}" olan "Kod Örneği" düğmesi) kullanarak almanızı öneririm. Elle alıntı <>&yapmak bir acıdır.
H Walters

@HWalters Tahmininiz için teşekkürler!
Benjamin Philippe,

Katılımınız için teşekkür ederiz. Ve bir şey: lütfen kaldırın, using namespace stdçünkü turnuvaya müdahale eder. Hata ayıklamak istiyorsanız, std::coutvb kullanabilirsiniz .
Frenzy Li

1

SurvivorPlayer

Survivor oyuncusu, Turtle ve Barricade Player ile aynı şekilde davranır. Asla ölümüne yol açabilecek bir eylemde bulunmayacak ve mücadeleyi kaybetmekten çok bir beraberlik uygulanacak.

// SurvivorPlayer.hpp
// Live to fight another day

#ifndef __SURVIVOR_PLAYER_HPP__
#define __SURVIVOR_PLAYER_HPP__

#include "Player.hpp"

class SurvivorPlayer final : public Player
{
public:
SurvivorPlayer(size_t opponent = -1) : Player(opponent)
{
}

public:
    virtual Action fight()
    {
        int myAmmo = getAmmo();
        int opponentAmmo = getAmmoOpponent();
        int turn = getTurn();
        if (turn == 0) {
            return load();
        }
        switch (opponentAmmo) {
        case 0:
            if (myAmmo > 2) {
                return GetRandomInteger(1) % 2 ? bullet() : plasma();
            }
            return load();
        case 1:
            if (myAmmo > 2) {
                return plasma();
            }
            return metal();
        default:
            if (myAmmo > 2) {
                return plasma();
            }
            return GetRandomInteger(1) % 2 ? metal() : thermal();
        }
    }
};

#endif // !__SURVIVOR_PLAYER_HPP__

1

FatedPlayer

Clotho tarafından yapılan, Lachesis tarafından atılan ve Atropos tarafından öldürülen ; Bu oyuncunun tek stratejisi, hangi eylemlerin makul olduğunu belirlemek için cephane hakkında bildiklerini kullanmaktır.

Ancak eylemi seçmek mümkün değildir ; bu kısım tanrılara bırakıldı.

#ifndef __FATEDPLAYER_H__
#define __FATEDPLAYER_H__

#include "Player.hpp"
#include <functional>
class FatedPlayer final : public Player
{
public:
   FatedPlayer(size_t o) : Player(o){}
   Action fight() {
      std::vector<std::function<Action()>>c{[&]{return load();}};
      switch(getAmmo()){
      default:c.push_back([&]{return plasma();});
      case 1 :c.push_back([&]{return bullet();});
      case 0 :;}
      switch(getAmmoOpponent()){
      default:c.push_back([&]{return thermal();});
      case 1 :c.push_back([&]{return metal();});
      case 0 :;}
      return c[GetRandomInteger(c.size()-1)]();
   }
};

#endif

... çünkü rastgele bir oyuncunun nasıl sıralandığını görmek istiyorum.


1

SpecificPlayer

SpecificPlayer , bazı rasgele (geçerli) eylemler seçme basit bir plan izler. Ancak, temel özelliği, cephane sayıları ve rakibin önceki hamlesini analiz ederek belirli durumlara dikkat etmesidir.

Bu benim ilk kez C ++ 'ta bir şeyler yazıyor ve ilk kez herhangi bir rekabetçi bot yazımı yapmaya çalışıyor. Umarım yetersiz teşebbüsüm en azından ilginç bir şey yapar. :)

// SpecificPlayer by Charles Jackson (Dysnomian) -- 21/01/2017
// PPCG: http://codegolf.stackexchange.com/a/104933/11933

#ifndef __SPECIFIC_PLAYER_HPP__
#define __SPECIFIC_PLAYER_HPP__

#include "Player.hpp"

class SpecificPlayer final : public Player
{
public:
    SpecificPlayer(size_t opponent = -1) : Player(opponent) {}

    //override
    virtual Action fight()
    {
        returnval = load(); //this should always be overwritten

        // if both players have no ammo we of course load
        if (oa == 0 && ma == 0) { returnval = load(); }

        // if (opponent has increased their ammo to a point they can fire something) then shield from it
        else if (oa == 1 && op == LOAD) { returnval = metal(); }
        else if (oa == 2 && op == LOAD) { returnval = thermal(); }
        else if (op == LOAD) { returnval = randomBlock(oa); }

        // if we have a master plan to follow through on do so, unless a defensive measure above is deemed necessary
        else if (nextDefined) { returnval = next; nextDefined = false; }

        // if opponent didn't fire their first shot on the second turn (turn 1) then we should block
        else if (t == 2 && oa >= 1) { returnval = randomBlock(oa); }

        //if opponent may be doing two attacks in a row
        else if (oa == 1 && op == BULLET) { returnval = metal(); }
        else if (oa == 2 && op == PLASMA) { returnval = thermal(); }

        // if we had no ammo last turn and still don't, load
        else if (ma == 0 && pa == 0) { returnval = load(); }

        // if we have just collected enough ammo to plasma, wait a turn before firing
        else if (ma == 2 && pa == 1) { 
            returnval = randomBlock(oa); next = plasma(); nextDefined = true; }

        // time for some random actions
        else
        {
            int caseval = GetRandomInteger(4) % 3; //loading is less likely than attacking or blocking
            switch (caseval) 
            {
            case 0: returnval = randomBlock(oa); break; // 40%
            case 1: returnval = randomAttack(ma); break; // 40%
            case 2: returnval = load(); break; // 20%
            }
        }

        pa = ma; //update previous ammo then update our current ammo
        switch (returnval)
        {
        case LOAD:
            ma += 1;
            break;
        case BULLET:
            ma -= 1;
            break;
        case PLASMA:
            ma -= 2;
            break;
        }
        t++; //also increment turn counter

        return returnval;
    }

    //override
     void perceive(Action action)
    {
         //record what action opponent took and update their ammo
         op = action;
         switch (action)
         {
         case LOAD:
             oa += 1;
             break;
         case BULLET:
             oa -= 1;
             break;
         case PLASMA:
             oa -= 2;
             break;
         }
    }

private:
    Action returnval; //our action to return
    Action next; //the action we want to take next turn - no matter what!
    bool nextDefined = false; //flag for if we want to be taking the "next" action.
    int t = 0; //turn number
    int ma = 0; //my ammo
    int oa = 0; //opponent ammo
    int pa = 0; //my previous ammo
    Action op; //opponent previous action

    Action randomBlock(int oa)
    {
        Action a;
        if (oa == 0) { a = load(); }
        else if (oa == 1) { a = metal(); }
        else
        {
            // more chance of ordianry block than laser block
            a = GetRandomInteger(2) % 2 ? metal() : thermal();
        }
        return a;
    }

    Action randomAttack(int ma)
    {
        Action a;
        if (ma == 0) { a = load(); }
        else if (ma == 1) { a = bullet(); }
        else
        {
            // more chance of ordianry attack than plasma
            a = GetRandomInteger(2) % 2 ? bullet() : plasma();
        }
        return a;
    }
};

#endif // !__SPECIFIC_PLAYER_HPP__

1

NotSoPatientPlayer

Yaratılışının hikayesi daha sonra gelecek.

// NotSoPatientPlayer.hpp

#ifndef __NOT_SO_PATIENT_PLAYER_HPP__
#define __NOT_SO_PATIENT_PLAYER_HPP__

#include "Player.hpp"
#include <iostream>

class NotSoPatientPlayer final : public Player
{
    static const int TOTAL_PLAYERS = 50;
    static const int TOTAL_ACTIONS = 5;
    static const int MAX_TURNS = 100;
public:
    NotSoPatientPlayer(size_t opponent = -1) : Player(opponent)
    {
        this->opponent = opponent;
    }

public:
    virtual Action fight()
    {
        /*Part which is shamelessly copied from MontePlayer.*/
        int turn = getTurn(),
            ammo = getAmmo(),
            opponentAmmo = getAmmoOpponent();
        int turnsRemaining = MAX_TURNS - turn;
        //The bot starts to shoot when there is enough ammo to fire plasma at least (turnsRemaining-2) times.
        //Did you know that you cannot die when you shoot plasma?
        //Also chooses 1 or 2 move(s) in which will shoot bullet(s) or none if there is plenty of ammo.
        //Also check !burstMode because it needs to be done only once.
        if (!burstMode && ammo + 2 >= turnsRemaining * 2)
        {
            burstMode = true;
            if (!(ammo == turnsRemaining * 2)) {
                turnForBullet1 = GetRandomInteger(turnsRemaining - 1) + turn;
                if (ammo + 2 == turnsRemaining * 2) {
                    //turnForBullet1 should be excluded in range for turnForBullet2
                    turnForBullet2 = GetRandomInteger(turnsRemaining - 2) + turn;
                    if (turnForBullet2 >= turnForBullet1) turnForBullet2++;
                }
            }
        }
        if (burstMode) {
            if (turn == turnForBullet1 || turn == turnForBullet2) {
                return bullet();
            }
            else return plasma();
        }

        //if opponent defended last 3 turns, the bot tries to go with something different
        if (turn >= 3) {
            auto historyOpponent = getHistoryOpponent();
            //if opponent used metal last 3 turns
            if (METAL == historyOpponent[turn - 1] && METAL == historyOpponent[turn - 2] && METAL == historyOpponent[turn - 3]) {
                if (ammo >= 2) return plasma();
                else return load();
            }
            //if opponent used thermal last 3 turns
            if (THERMAL == historyOpponent[turn - 1] && THERMAL == historyOpponent[turn - 2] && THERMAL == historyOpponent[turn - 3]) {
                if (ammo >= 1) return bullet();
                else return load();
            }
            //if the opponent defends, but not consistently
            if ((historyOpponent[turn - 1] == METAL || historyOpponent[turn - 1] == THERMAL)
                && (historyOpponent[turn - 2] == METAL || historyOpponent[turn - 2] == THERMAL)
                && (historyOpponent[turn - 3] == METAL || historyOpponent[turn - 3] == THERMAL)) {
                if (ammo >= 2) return plasma();
                else if (ammo == 1) return bullet();
                else return load();
            }
        }

        /*else*/ {
            if (opponentAmmo == 0) return load();
            if (opponentAmmo == 1) return metal();
            //if opponent prefers bullets or plasmas, choose the appropriate defence
            if (opponentMoves[opponent][BULLET] * 2 >= opponentMoves[opponent][PLASMA]) return metal();
            else return thermal();
        }
    }

    virtual void perceive(Action action)
    {
        Player::perceive(action);
        opponentMoves[opponent][action]++;
    }

    /*virtual void declared(Result result)
    {
        currentRoundResults[opponent][result]++;
        totalResults[opponent][result]++;
        int duels = 0;
        for (int i = 0; i < 3; i++) duels += currentRoundResults[opponent][i];
        if (duels == 100) {
            std::cout << "Score against P" << opponent << ": " <<
                currentRoundResults[opponent][WIN] << "-" << currentRoundResults[opponent][DRAW] << "-" << currentRoundResults[opponent][LOSS] << "\n";
            for (int i = 0; i < 3; i++) currentRoundResults[opponent][i] = 0;
        }
    };*/

private:
    static long opponentMoves[TOTAL_PLAYERS][TOTAL_ACTIONS];
    int opponent;
    //When it becomes true, the bot starts shooting.
    bool burstMode = false;
    //turnForBullet1 and turnForBullet2,
    //the 2 turns in which the bot will shoot bullets
    int turnForBullet1 = -1, turnForBullet2 = -1;
    //For debugging purposes
    //Reminder: enum Result { DRAW, WIN, LOSS };
    static int currentRoundResults[TOTAL_PLAYERS][3], totalResults[TOTAL_PLAYERS][3];
};
long NotSoPatientPlayer::opponentMoves[TOTAL_PLAYERS][TOTAL_ACTIONS] = { { 0 } };
int NotSoPatientPlayer::currentRoundResults[TOTAL_PLAYERS][3] = { { 0 } };
int NotSoPatientPlayer::totalResults[TOTAL_PLAYERS][3] = { { 0 } };
#endif // !__NOT_SO_PATIENT_PLAYER_HPP__

"Yaratılışının hikayesi daha sonra gelecek" 3 aydan fazla oldu :)
HyperNeutrino
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.