Dobry kontra zło

112

Wyniki - 19 lipca 2014 r

Obecny król wzgórza jest najemnikiem użytkownika Fabiglera ! Nadsyłaj wpisy i zrzuć go z tronu!

Kliknij tutaj, aby wyświetlić tabelę wyników.

Uwzględniono programy przesłane 19 lipca 2014 r. Lub wcześniej. Wszystkie pozostałe zgłoszenia zostaną uwzględnione w przyszłych testach. Nowe wyniki powinny zostać opublikowane około 9 sierpnia, dzięki czemu zyskasz dużo czasu.


Ilustracja sporządzona przez brata Ilustrowane przez Chrisa Rainbolta, mojego brata i świeżego absolwenta Savannah College of Art and Design

Wprowadzenie

Aniołowie i demony walczą i, jak zwykle, wykorzystują ziemię jako swoje pole bitwy. Ludzie utknęli w środku i zostali zmuszeni do poparcia stron. Nieznana neutralna siła nagradza tych, którzy konsekwentnie walczą o przegraną stronę.

Gra

W każdej próbie zostaniesz pseudolosowo sparowany, a następnie przetasujesz od 20 do 30 innych zgłoszeń. Każda próba będzie się składać z 1000 rund. W każdej rundzie otrzymujesz dane wejściowe i oczekuje się, że przyniosą wynik. Twoje wyniki zostaną zarejestrowane i ocenione. Ten proces zostanie powtórzony 1000 razy.

Wejście

Otrzymasz pojedynczy argument, który reprezentuje poprzednie głosy każdego gracza. Rundy są rozdzielane przecinkami. 0Oznacza gracza, który po stronie zła, które okrągłe. A 1reprezentuje gracza, który opowiedział się po stronie Dobra. W trakcie okresu próbnego gracze będą zawsze w tej samej kolejności. Twój głos zostanie uwzględniony, ale nie zostanie wyraźnie określony. Na przykład:

101 100,100

W tym przykładzie zakończono trzy rundy i trzech graczy bierze udział w rywalizacji. Gracz jednostronny zawsze z Good. Gracz drugi zawsze był po stronie Zła. Gracz trzeci zamienił z Dobra w rundzie 1 na Zło w rundzie 2 i 3. Jednym z tych graczy byłeś ty.

Wynik

Zgłoszenia Java

  • Zwróć ciąg good jeśli chcesz stanąć po stronie Dobra.
  • Zwróć ciąg, eviljeśli chcesz stanąć po stronie Zła.

Zgłoszenia inne niż Java

  • Wyjście łańcucha good na standardowe wyjście, jeśli chcesz stanąć po stronie Dobra.
  • Podaj ciąg evilna standardowe wyjście, jeśli chcesz stanąć po stronie Zła.

Jeśli Twój program wyświetli lub zwróci cokolwiek innego, zgłosi wyjątek, nie skompiluje się lub wyda cokolwiek dłużej na tej konkretnej maszynie , zostanie zdyskwalifikowany.

Punktacja

Wyniki zostaną opublikowane w arkuszu kalkulacyjnym Dokumentów Google w celu łatwego przeglądania, gdy tylko będę mógł skompilować wszystkie bieżące wpisy. Nie martw się - będę testować tak długo, jak wy będziecie przesyłać programy!

  • Otrzymujesz 3 punkty za bocznicę z większością podczas rundy.
  • Otrzymujesz n - 1 punktów za stronnictwo z mniejszością podczas rundy, gdzie n jest liczbą kolejnych stron, które opowiedziałeś się po stronie mniejszości.

Twój wynik będzie medianą z 5 prób. Każda próba składa się z 1000 rund.

Produkty

Zgłoszenia inne niż Java

Musisz przesłać unikalny tytuł, program i ciąg wiersza poleceń systemu Windows, który uruchomi Twój program. Pamiętaj, że do tego ciągu można dołączyć argument. Na przykład:

  • python Angel.py
    • Zauważ, że ten nie ma argumentów. To jest runda pierwsza! Przygotuj się na to.
  • python Angel.py 11011,00101,11101,11111,00001,11001,11001

Zgłoszenia Java

Musisz przesłać unikalny tytuł i klasę Java, która rozszerza abstrakcyjną klasę Human zapisaną poniżej.

public abstract class Human {
    public abstract String takeSides(String history) throws Exception;
}

Testowanie

Jeśli chcesz przetestować własne zgłoszenie, postępuj zgodnie z instrukcjami tutaj .

Dodatkowe uwagi

Możesz przesłać tyle różnych zgłoszeń, ile chcesz. Zgłoszenia, które wydają się być w zmowie, zostaną zdyskwalifikowane. Autor tego wyzwania będzie jedynym sędzią w tej sprawie.

Nowa instancja twojego programu lub klasy Java będzie tworzona za każdym razem, gdy zostanie wywołana. Możesz zachować informacje, pisząc do pliku. Nie możesz modyfikować struktury ani zachowania czegokolwiek poza własną klasą.

Gracze zostaną potasowani przed rozpoczęciem okresu próbnego. Demon i Anioł będą uczestniczyć w każdej próbie. Jeśli liczba graczy będzie parzysta, dołączy do nich Petyr Baelish. Demon walczy ze złem, anioł na dobre, a Petyr Baelish wybiera pseudolosową stronę.

Rainbolt
źródło
2
Komentarze zostały usunięte, ponieważ były przestarzałe i na prośbę OP. Powiadom mnie o wszelkich komentarzach, które należy usunąć.
Klamka
7
Woah, OP zmienia swoją nazwę użytkownika. Ok, więc kiedy zostanie wyświetlony wynik?
justhalf
6
@Rainbolt To musi być cholernie fajna robota, wykonując to wyzwanie! Powodem tej uwagi jest prostota protokołu i zasad, dzięki czemu jest on dostępny, a jednocześnie umożliwia proste, działające wpisy. TL; DR: Twoje wyzwanie jest zbyt dobre! : D
tomsmeding
3
@dgel Zamieszczę nieprzetworzone dane, górne, dolne, średnie, a może wykres liniowy, abyśmy mogli zobaczyć, kto spisał się lepiej, gdy trwała konkurencja.
Rainbolt
6
Jeden z strąków skończył z 10 wpisami, które głosowały w ten sam sposób za każdym razem. W rezultacie dwóch użytkowników uzyskało doskonałe wyniki lub „jedną rundę doskonałych” wyników wynoszącą około 450 000. Te same zgłoszenia zdobyły około 1900 r. W innych próbach. Średni wynik jest bliski 2000. Z powodu skrajnego braku równowagi wyników zdecydowałem, że bardziej znaczącą liczbą będzie mediana. Zredagowałem wyzwanie, aby po 5 próbach zwycięzca został zgłoszony z najwyższą medianą. Jeśli ktoś uważa, że ​​przejście od średniej do mediany jest niesprawiedliwe lub w inny sposób zły wybór, prosimy o komentarz.
Rainbolt

Odpowiedzi:

11

Najemnik

Zawsze po stronie tego, który zapłacił najwięcej pieniędzy w ostatniej rundzie.

Biorąc pod uwagę, że dobrzy ludzie zarabiają statystycznie więcej.

package Humans;
public class Mercenary extends Human {
    public String takeSides(String history) {
        // first round random!
        if (history.length() == 0) {
            return Math.random() >= 0.5 ? "good" : "evil";
        }

        String[] rounds = history.split(",");
        String lastRound = rounds[rounds.length - 1];

        double goodMoneyPaid = 0;
        double evilMoneyPaid = 0;
        for (char c : lastRound.toCharArray()) {
                switch (c) {
                case '0':
                    goodMoneyPaid = goodMoneyPaid + 0.2; //statistically proven: good people have more reliable incomes
                    break;
                case '1':
                    evilMoneyPaid++; 
                    break;
                default:
                    break;
                }
        }

        if (goodMoneyPaid > evilMoneyPaid)
        {
            return "good";
        } else {
            return "evil";
        }
    }
}
fabigler
źródło
2
To jest drugi post, który mówi coś o pieniądzach. Czy brakuje mi referencji lub czegoś takiego?
Rainbolt
To prawda, ale ten facet jest jeszcze bardziej złym sukinsynem. Opuszczając swoich kumpli za każdym razem, tylko ze względu na pieniądze.
fabigler
W instrukcji switch brakowało instrukcji return dla przypadku domyślnego, co spowodowało, że się nie skompilowała. Dodałem losowy.
Rainbolt
4
Gratulacje, królu wzgórza! Nie rozumiem, jak wygrywa ten wpis. Chcesz dodać wyjaśnienie, skoro ma przypisaną nagrodę za 300 reputacji?
Rainbolt
4
Być może błąd, lub źle zrozumiałem komentarze i opis, ale Najemnik tak naprawdę nie robi tego, co powinien. Z wyjątkiem pierwszej losowej rundy, będzie on zawsze po stronie zła, chyba że mniej niż 1/6 osób głosowało na zło w poprzedniej rundzie.
jaybz
39

Hipster, Ruby

if ARGV.length == 0
    puts ["good", "evil"].sample
else
    last_round = ARGV[0].split(',').last
    n_players = last_round.length
    puts last_round.count('1') > n_players/2 ? "evil" : "good"
end

Po prostu idzie w parze z mniejszością z ostatniej rundy, tylko dlatego, że wszystko inne jest w głównym nurcie.

Biegnij jak

ruby hipster.rb
Martin Ender
źródło
30

Petyr Baelish

Nigdy nie wiadomo, po czyjej stronie stoi Petyr Baelish.

package Humans;

/**
 * Always keep your foes confused. If they are never certain who you are or 
 * what you want, they cannot know what you are likely to do next.
 * @author Rusher
 */
public class PetyrBaelish extends Human {

    /**
     * Randomly take the side of good or evil.
     * @param history The past votes of every player
     * @return A String "good" or "evil
     */
    public String takeSides(String history) {
        return Math.random() < 0.5 ? "good" : "evil";
    }
}

Ten wpis zostanie uwzględniony tylko wtedy, gdy liczba graczy będzie parzysta. To gwarantuje, że zawsze będzie większość.

Rainbolt
źródło
28
Oczywiście ze strony Petyra Baelisha.
Cthulhu
2
@Kevin To konsekwentnie bije większość botów. Zwykle ma 27 punktów.
cjfaure
3
@Kevin Ten wpis został przesłany przez autora wyzwania. To nie miało być dobre. Istnieje, aby upewnić się, że zawsze będzie większość, ponieważ przy parzystej liczbie graczy może dojść do remisu.
Rainbolt,
4
Dlaczego, o Boże, dlaczego ten ma najwięcej głosów? To po prostu niesprawiedliwe .
tomsmeding
3
@tomsmeding Nie. To cytat z Game of Thrones lol.
Rainbolt
29

C ++, The Meta Scientist

Ten działa zasadniczo tak samo jak Naukowiec, ale nie działa na rundach jako całości, ale na pojedynczych graczach. Próbuje przyporządkować falę (lub stałą funkcję) każdemu graczowi osobno i przewiduje ich ruch w następnej rundzie. Z wynikowej prognozy rundy The Meta Scientist wybiera, która strona wygląda jak posiadanie większości.

#include <iostream>
#include <utility>
#include <cstdlib>
#include <cstring>
#if 0
#define DBG(st) {st}
#else
#define DBG(st)
#endif

#define WINDOW (200)

using namespace std;

int main(int argc,char **argv){
    if(argc==1){
        cout<<(rand()%2?"good":"evil")<<endl;
        return 0;
    }
    DBG(cerr<<"WINDOW="<<WINDOW<<endl;)
    int nump,numr;
    nump=strchr(argv[1],',')-argv[1];
    numr=(strlen(argv[1])+1)/(nump+1);
    int period,r,p;
    int score,*scores=new int[WINDOW];
    int max; //some score will always get above 0, because if some score<0, the inverted wave will be >0.
    int phase,phasemax;
    int predicted=0; //The predicted number of goods for the next round
    int fromround=numr-WINDOW;
    if(fromround<0)fromround=0;
    pair<int,int> maxat; //period, phase
    DBG(cerr<<"Players:"<<endl;)
    for(p=0;p<nump;p++){
        DBG(cerr<<" p"<<p<<": ";)
        for(r=fromround;r<numr;r++)if(argv[1][r*(nump+1)+p]!=argv[1][p])break;
        if(r==numr){
            DBG(cerr<<"All equal! prediction="<<argv[1][p]<<endl;)
            predicted+=argv[1][(numr-1)*(nump+1)+p]-'0';
            continue;
        }
        max=0;
        maxat={-1,-1};
        for(period=1;period<=WINDOW;period++){
            scores[period-1]=0;
            phasemax=-1;
            for(phase=0;phase<2*period;phase++){
                score=0;
                for(r=fromround;r<numr;r++){
                    if(argv[1][r*(nump+1)+p]-'0'==1-(r+phase)%(2*period)/period)score++;
                    else score--;
                }
                if(score>scores[period-1]){
                    scores[period-1]=score;
                    phasemax=phase;
                }
            }
            if(scores[period-1]>max){
                max=scores[period-1];
                maxat.first=period;
                maxat.second=phasemax;
            }
            DBG(cerr<<scores[period-1]<<" ";)
        }
        DBG(cerr<<"(max="<<max<<" at {"<<maxat.first<<","<<maxat.second<<"})"<<endl;)
        DBG(cerr<<"     prediction: 1-("<<numr<<"+"<<maxat.second<<")%(2*"<<maxat.first<<")/"<<maxat.first<<"="<<(1-(numr+maxat.second)%(2*maxat.first)/maxat.first)<<endl;)
        predicted+=(1-(numr+maxat.second)%(2*maxat.first)/maxat.first);
    }
    DBG(cerr<<"Predicted outcome: "<<predicted<<" good + "<<(nump-predicted)<<" evil"<<endl;)
    if(predicted>nump/2)cout<<"evil"<<endl; //pick minority
    else cout<<"good"<<endl;
    delete[] scores;
    return 0;
}

Jeśli chcesz włączyć instrukcje debugowania, zmień odczyt linii #if 0 na #if 1.

Połącz z g++ -O3 -std=c++0x -o MetaScientist MetaScientist.cpp (nie potrzebujesz ostrzeżeń, więc nie -Wall) i uruchom z MetaScientist.exe(prawdopodobnie włączając argument oczywiście). Jeśli naprawdę ładnie pytasz, mogę dostarczyć plik wykonywalny dla systemu Windows.

EDYCJA: Najwyraźniej poprzedniej wersji zabrakło czasu około 600 rund do gry. To nie powinno tego robić. Zużycie czasu jest kontrolowane przez #define WINDOW (...)linię, więcej jest wolniejsze, ale spogląda wstecz.

Tomsmeding
źródło
2
Pokornie sugeruję, abyś spróbował wybrać przegraną stronę. Jeśli konsekwentnie zgadniesz poprawnie, otrzymasz więcej niż 3 punkty na rundę.
Kevin
1
@Kevin To prawda, ale pomyślałem, że może dość szybko odgadnąć niewłaściwą stronę i musisz poprawnie odgadnąć przegraną stronę więcej niż siedem razy z rzędu, aby uzyskać poprawę w stosunku do uzyskania prawidłowej większości. Mogę to jednak zmienić.
tomsmeding
1
@Kevin Chciałbym też najpierw zobaczyć, jak sobie radzą (Scientist i Meta Scientist), kiedy Rusher dostaje nam tablicę wyników w ten weekend, jak wskazał w komentarzach do PO. Rusher, przepraszam, ale jestem zbyt leniwy, by samemu skompilować wszystkie rzeczy ... :)
tomsmeding
3
Bez obaw! Zresztą ich uruchomienie prawdopodobnie nie jest bezpieczne. Pozwól mi zepsuć moją maszynę kodem napisanym przez 50 nieznajomych w Internecie.
Rainbolt
1
@Kevin Ale to WIELE ! Rzeczywiście mogę, ale mi się nie podoba. Zobaczę, jak te opłaty.
tomsmeding
26

Anioł

Najczystszy gracz ze wszystkich.

Program

print "good"

Komenda

python Angel.py
Rainbolt
źródło
22
Python to dobry język. Wydaje się naturalne, że Anioł go użyje.
jpmc26,
23
Chciałbym przypomnieć ludziom, że Python jest wężem. Wąż
Pan Lister
3
@MrLister Czy mogę przypomnieć, że Lucyfer był wielkim aniołem, zanim Bóg go wyrzucił z nieba?
Zibbobz
1
@Zibbobz Tak ... szkoda, że ​​wypadli. Mogli wspólnie osiągnąć tak wiele.
Pan Lister,
24

Artemis Fowl

package Humans;

public class ArtemisFowl extends Human {
    public final String takeSides(String history) {
        int good = 0, evil = 0;
        for(int i = 0; i < history.length(); i++)   {
            switch(history.charAt(i))   {
                case '0': evil++; break;
                case '1': good++; break;
            }
        }
        if(good % 5 == 0){
           return "good";
        } else if (evil % 5 == 0){
           return "evil";
        } else {
           if(good > evil){
              return "good";
           } else if(evil > good){
              return "evil";
           } else {
              return Math.random() >= 0.5 ? "good" : "evil";
           }
        }
    }
}

W Książce 7, The Atlantis Complex , Artemis Fowl zachorował na chorobę psychiczną (zwaną kompleksem Atlantis), która zmusiła go do zrobienia wszystkiego w wielokrotności 5 (mówienie, działania itp.). Kiedy nie mógł tego zrobić kilkukrotnie, wpadł w panikę. Zasadniczo robię to: sprawdzam, czy dobro lub zło (celowe nastawienie) jest podzielne przez 5, jeśli nie, to panikuję i widzę, który był większy i biegnę z tym lub panikuję jeszcze bardziej i losowo wybieram.

Kyle Kanos
źródło
4
Kiedy czytałem Artemis Fowl w Junior High, istniały tylko dwie książki. Miło jest widzieć, że jest ich teraz siedem, a Disney robi z tego film.
Rainbolt
1
W rzeczywistości jest 8 książek.
Kyle Kanos
7
Im więcej, tym lepiej (chyba, że ​​czytasz The Wheel of Time)
Rainbolt
1
I zapomniałeś break;w swoim switch.
johnchen902
1
@ johnchen902, @ Manu: Nie mam zbyt dużego doświadczenia w Javie (używam Fortran90 + i widzę tu tylko Javę), stąd moje błędy. Naprawię je, kiedy za godzinę wejdę do biura.
Kyle Kanos
19

Disparnumerofobic

Dziwne liczby są przerażające.

package Humans;

public class Disparnumerophobic extends Human {
    public final String takeSides(String history) {
        int good = 0, evil = 0;
        for(int i = 0; i < history.length(); i++)   {
            switch(history.charAt(i))   {
                case '0': evil++; break;
                case '1': good++;
            }
        }
        if(good%2 == 1 && evil%2 == 0)  return "evil";
        if(evil%2 == 1 && good%2 == 0)  return "good";
        // well shit.... 
        return Math.random() >= 0.5 ? "good" : "evil";
    }
}
pseudonim117
źródło
17
Komentarz mnie rozśmieszył / parsknął.
phyrfox,
17

Linus, Ruby

Stara się wprowadzać analityków w błąd, zawsze przełamując wzorzec .

num_rounds = ARGV[0].to_s.count(',')
LINUS_SEQ = 0xcb13b2d3734ecb4dc8cb134b232c4d3b2dcd3b2d3734ec4d2c8cb134b234dcd3b2d3734ec4d2c8cb134b23734ecb4dcd3b2c4d232c4d2c8cb13b2d3734ecb4dcb232c4d2c8cb13b2d3734ecb4dc8cb134b232c4d3b2dcd3b2d3734ec4d2c8cb134b234dcd3b2d3734ec4d2c8cb134b23734ecb4dcd3b2c4d2c8cb134b2
puts %w[good evil][LINUS_SEQ[num_rounds]]

Zapisz jako linus.rbi biegnij zruby linus.rb

histocrat
źródło
16

BackPacker

Określa gracza, który jak dotąd najbardziej wybrał pasującą mniejszość i wybiera swój ostatni głos.

package Humans;

public class BackPacker extends Human {
    // toggles weather the BackPacker thinks majority is better vs. minority is better
    private static final boolean goWithMajority = false;

    @Override
    public final String takeSides(String history)  {
        if (history == null || history.equals(""))
            return "evil";
        String[] roundVotes = history.split(",");
        int players = roundVotes[0].length();
        int[] winningPlayers = new int[players];
        for (String nextRound : roundVotes) {
            boolean didGoodWin = didGoodWin(nextRound, players);
            for (int player = 0; player < nextRound.length(); player++) {
                boolean playerVotedGood = nextRound.charAt(player) == '1';
                winningPlayers[player] += didPlayerWin(didGoodWin, playerVotedGood);
            }
        }
        int bestScore = -1;
        for (int nextPlayer : winningPlayers)
            if (bestScore < nextPlayer)
                bestScore = nextPlayer;
        int bestPlayer = 0;
        for (int ii = 0; ii < players; ii++) {
            if (winningPlayers[ii] == bestScore) {
                bestPlayer = ii;
                break;
            }
        }
        if (roundVotes[roundVotes.length - 1].charAt(bestPlayer) == '1')
            return "good";
        return "evil";
    }

    private int didPlayerWin(boolean didGoodWin, boolean playerVotedGood) {
        if(goWithMajority) {
            return ((didGoodWin && playerVotedGood) || (!didGoodWin && !playerVotedGood)) ? 1 : 0;
        } else {
            return ((!didGoodWin && playerVotedGood) || (didGoodWin && !playerVotedGood)) ? 1 : 0;
        }
    }

    private boolean didGoodWin(String round, int players) {
        int good = 0;
        for (char next : round.toCharArray())
            good += next == '1' ? 1 : 0;
        return (good * 2) > players;
    }
}

The CrowdFollower

Określa gracza, który jak dotąd najbardziej wybrał pasującą większość i wybiera swój ostatni głos.

package Humans;

public class CrowdFollower extends Human {
    // toggles weather the FrontPacker thinks majority is better vs. minority is better
    private static final boolean goWithMajority = true;

    @Override
    public final String takeSides(String history)  {
        if (history == null || history.equals(""))
            return "evil";
        String[] roundVotes = history.split(",");
        int players = roundVotes[0].length();
        int[] winningPlayers = new int[players];
        for (String nextRound : roundVotes) {
            boolean didGoodWin = didGoodWin(nextRound, players);
            for (int player = 0; player < nextRound.length(); player++) {
                boolean playerVotedGood = nextRound.charAt(player) == '1';
                winningPlayers[player] += didPlayerWin(didGoodWin, playerVotedGood);
            }
        }
        int bestScore = -1;
        for (int nextPlayer : winningPlayers)
            if (bestScore < nextPlayer)
                bestScore = nextPlayer;
        int bestPlayer = 0;
        for (int ii = 0; ii < players; ii++) {
            if (winningPlayers[ii] == bestScore) {
                bestPlayer = ii;
                break;
            }
        }
        if (roundVotes[roundVotes.length - 1].charAt(bestPlayer) == '1')
            return "good";
        return "evil";
    }

    private int didPlayerWin(boolean didGoodWin, boolean playerVotedGood) {
        if(goWithMajority) {
            return ((didGoodWin && playerVotedGood) || (!didGoodWin && !playerVotedGood)) ? 1 : 0;
        } else playerVotedGood                return ((!didGoodWin && good) || (didGoodWin && !playerVotedGood)) ? 1 : 0;
        }
    }

    private boolean didGoodWin(String round, int players) {
        int good = 0;
        for (char next : round.toCharArray())
            good += next == '1' ? 1 : 0;
        return (good * 2) > players;
    }
}
Angelo Fuchs
źródło
Bardzo czysty program!
Rainbolt
Ups, myślę, że mogłem skopiować Twój program w innym języku.
PyRulez
@Rusher Zaktualizowałem kod i chciałbym dodać go jako dwa wpisy, jeden z goWithMajority = truei jeden tam, gdzie jest false. Czy to w porządku, czy muszę do tego dodać drugiego BackPackera?
Angelo Fuchs
@AngeloNeuschitzer Edytowałem ten post. W ten sposób nie zapomnę dodać obu zgłoszeń. Sugeruję zmianę naprawdę nieciekawej nazwy, którą mu nadałem, a jeśli chcesz, możesz dodać opis do obu.
Rainbolt
1
@Rainbolt Właściwie podoba mi się twój FrontPacker. Lol'd.
tomsmeding
15

Wróżka

To wciąż praca w toku. Jeszcze tego nie testowałem. Chciałem tylko sprawdzić, czy OP uważa, że ​​łamie zasady, czy nie.

Chodzi o to, aby symulować następną rundę, wykonując kilka razy wszystkich pozostałych uczestników, aby uzyskać prawdopodobieństwo wyniku i odpowiednio postępować.

package Humans;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import sun.net.www.protocol.file.FileURLConnection;

public class FortuneTeller extends Human {

/**
 * Code from http://stackoverflow.com/a/22462785 Private helper method
 *
 * @param directory The directory to start with
 * @param pckgname The package name to search for. Will be needed for
 * getting the Class object.
 * @param classes if a file isn't loaded but still is in the directory
 * @throws ClassNotFoundException
 */
private static void checkDirectory(File directory, String pckgname,
        ArrayList<Class<?>> classes) throws ClassNotFoundException {
    File tmpDirectory;

    if (directory.exists() && directory.isDirectory()) {
        final String[] files = directory.list();

        for (final String file : files) {
            if (file.endsWith(".class")) {
                try {
                    classes.add(Class.forName(pckgname + '.'
                            + file.substring(0, file.length() - 6)));
                } catch (final NoClassDefFoundError e) {
                // do nothing. this class hasn't been found by the
                    // loader, and we don't care.
                }
            } else if ((tmpDirectory = new File(directory, file))
                    .isDirectory()) {
                checkDirectory(tmpDirectory, pckgname + "." + file, classes);
            }
        }
    }
}

/**
 * Private helper method.
 *
 * @param connection the connection to the jar
 * @param pckgname the package name to search for
 * @param classes the current ArrayList of all classes. This method will
 * simply add new classes.
 * @throws ClassNotFoundException if a file isn't loaded but still is in the
 * jar file
 * @throws IOException if it can't correctly read from the jar file.
 */
private static void checkJarFile(JarURLConnection connection,
        String pckgname, ArrayList<Class<?>> classes)
        throws ClassNotFoundException, IOException {
    final JarFile jarFile = connection.getJarFile();
    final Enumeration<JarEntry> entries = jarFile.entries();
    String name;

    for (JarEntry jarEntry = null; entries.hasMoreElements()
            && ((jarEntry = entries.nextElement()) != null);) {
        name = jarEntry.getName();

        if (name.contains(".class")) {
            name = name.substring(0, name.length() - 6).replace('/', '.');

            if (name.contains(pckgname)) {
                classes.add(Class.forName(name));
            }
        }
    }
}

/**
 * Attempts to list all the classes in the specified package as determined
 * by the context class loader
 *
 * @param pckgname the package name to search
 * @return a list of classes that exist within that package
 * @throws ClassNotFoundException if something went wrong
 */
private static ArrayList<Class<?>> getClassesForPackage(String pckgname)
        throws ClassNotFoundException {
    final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();

    try {
        final ClassLoader cld = Thread.currentThread()
                .getContextClassLoader();

        if (cld == null) {
            throw new ClassNotFoundException("Can't get class loader.");
        }

        final Enumeration<URL> resources = cld.getResources(pckgname
                .replace('.', '/'));
        URLConnection connection;

        for (URL url = null; resources.hasMoreElements()
                && ((url = resources.nextElement()) != null);) {
            try {
                connection = url.openConnection();

                if (connection instanceof JarURLConnection) {
                    checkJarFile((JarURLConnection) connection, pckgname,
                            classes);
                } else if (connection instanceof FileURLConnection) {
                    try {
                        checkDirectory(
                                new File(URLDecoder.decode(url.getPath(),
                                                "UTF-8")), pckgname, classes);
                    } catch (final UnsupportedEncodingException ex) {
                        throw new ClassNotFoundException(
                                pckgname
                                + " does not appear to be a valid package (Unsupported encoding)",
                                ex);
                    }
                } else {
                    throw new ClassNotFoundException(pckgname + " ("
                            + url.getPath()
                            + ") does not appear to be a valid package");
                }
            } catch (final IOException ioex) {
                throw new ClassNotFoundException(
                        "IOException was thrown when trying to get all resources for "
                        + pckgname, ioex);
            }
        }
    } catch (final NullPointerException ex) {
        throw new ClassNotFoundException(
                pckgname
                + " does not appear to be a valid package (Null pointer exception)",
                ex);
    } catch (final IOException ioex) {
        throw new ClassNotFoundException(
                "IOException was thrown when trying to get all resources for "
                + pckgname, ioex);
    }

    return classes;
}

private static boolean isRecursiveCall = false;
private static ArrayList<Class<?>> classes;

static {
    if (classes == null) {
        try {
            classes = getClassesForPackage("Humans");
        } catch (ClassNotFoundException ex) {

        }
    }    
}

private String doThePetyrBaelish() {
    return Math.random() >= 0.5 ? "good" : "evil";
}

@Override
public String takeSides(String history) {
    if (isRecursiveCall) {
        return doThePetyrBaelish();
    }
    isRecursiveCall = true;

    int currentRoundGoodCount = 0;
    float probabilityOfGood = 0;
    int roundCount = 0;
    int voteCount = 0;



    do {
        for (int i = 0; i < classes.size(); i++) {
            try {
                if (classes.get(i).getName() == "Humans.FortuneTeller") {
                    continue;
                }

                Human human = (Human) classes.get(i).newInstance();
                String response = human.takeSides(history);
                switch (response) {
                    case "good":
                        currentRoundGoodCount++;
                        voteCount++;
                        break;
                    case "evil":
                        voteCount++;
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
            }
        }

        probabilityOfGood = (probabilityOfGood * roundCount
                + (float) currentRoundGoodCount / voteCount) / (roundCount + 1);

        roundCount++;
        currentRoundGoodCount = 0;
        voteCount = 0;

    } while (roundCount < 11);

    isRecursiveCall = false;
    if (probabilityOfGood > .7) {
        return "evil";
    }
    if (probabilityOfGood < .3) {
        return "good";
    }

    return doThePetyrBaelish();
}

}
Andris
źródło
Jeśli twój bot uruchamia wszystkie inne boty przed każdą odpowiedzią, czy odpowiedź nie zajmie więcej niż 1s?
plannapus
@plannapus Zgaduję, że z tym botem założę się, że wszyscy pozostaną w błędzie po stronie ostrożności i unikną czegoś bliskiego 1 sekundy oczekiwania. Myślę, że warto złożyć zgłoszenie i zgłoszenie, które składa się z 0,9 sekundy oczekiwania, zanim powróci „dobry”, po prostu z nim zadzierać. Właściwie SBoss mnie pobił: D
scragar
Jah! Następnie musiałbym umieścić czarną listę tego bota w moim kodzie. Byłoby to frustrujące ... Również przy różnych wpisach w różnych środowiskach, takich jak Python lub Perl, powtórne ładowanie interpretera może wystarczyć, aby ten kod przekroczył limit czasu.
Andris
16
Jeśli ktoś zrobi to samo, otrzymasz nieskończoną pętlę.
Brilliand
4
Upłynął limit czasu przesyłania. Podłączyłem profiler i prawie pół sekundy spędziłem na sprawdzaniu niektórych zgłoszeń. Przynajmniej działa, więc gratuluję tego.
Rainbolt
15

C ++, naukowiec

Ten próbuje, z historią tego, co większość wybrała na rundę wave( majority()daje większości wybór na rundę), dopasować falę do danych, długości fali 2*periodi fazy phase. Tak więc dane0,1,1,1,0,1,0,1,1,1,0,0,0,1,0 select period=3, phase=5( maxat=={3,5}): jego wyniki stają się 9 3 11 5 5 3 5 7 9 7 7 7 7 7 7. Zapętla się we wszystkich możliwych okresach, a jeśli dla tego okresu wynik jest wyższy niż dla bieżącego maksimum, zapamiętuje, {period,phase}dla którego to miało miejsce.

Następnie ekstrapoluje znalezioną falę do następnej rundy i przyjmuje przewidywaną większość.

#include <iostream>
#include <utility>
#include <cstdlib>
#include <cstring>
#if 0
#define DBG(st) {st}
#else
#define DBG(st)
#endif

#define WINDOW (700)

using namespace std;

int majority(const char *r){
    int p=0,a=0,b=0;
    while(true){
        if(r[p]=='1')a++;
        else if(r[p]=='0')b++;
        else break;
        p++;
    }
    return a>b;
}

int main(int argc,char **argv){
    if(argc==1){
        cout<<(rand()%2?"good":"evil")<<endl;
        return 0;
    }
    DBG(cerr<<"WINDOW="<<WINDOW<<endl;)
    int nump,numr;
    nump=strchr(argv[1],',')-argv[1];
    numr=(strlen(argv[1])+1)/(nump+1);
    int fromround=numr-30;
    if(fromround<0)fromround=0;
    int period,r;
    int *wave=new int[WINDOW];
    bool allequal=true;
    DBG(cerr<<"wave: ";)
    for(r=fromround;r<numr;r++){
        wave[r-fromround]=majority(argv[1]+r*(nump+1));
        if(wave[r-fromround]!=wave[0])allequal=false;
        DBG(cerr<<wave[r]<<" ";)
    }
    DBG(cerr<<endl;)
    if(allequal){
        DBG(cerr<<"All equal!"<<endl;)
        if(wave[numr-1]==1)cout<<"evil"<<endl; //choose for minority
        else cout<<"good"<<endl;
        return 0;
    }
    int score,*scores=new int[WINDOW];
    int max=0; //some score will always get above 0, because if some score<0, the inverted wave will be >0.
    int phase,phasemax;
    pair<int,int> maxat(-1,-1); //period, phase
    DBG(cerr<<"scores: ";)
    for(period=1;period<=WINDOW;period++){
        scores[period-1]=0;
        phasemax=-1;
        for(phase=0;phase<2*period;phase++){
            score=0;
            for(r=fromround;r<numr;r++){
                if(wave[r]==1-(r+phase)%(2*period)/period)score++;
                else score--;
            }
            if(score>scores[period-1]){
                scores[period-1]=score;
                phasemax=phase;
            }
        }
        if(scores[period-1]>max){
            max=scores[period-1];
            maxat.first=period;
            maxat.second=phasemax;
        }
        DBG(cerr<<scores[period-1]<<" ";)
    }
    DBG(cerr<<"(max="<<max<<" at {"<<maxat.first<<","<<maxat.second<<"})"<<endl;)
    DBG(cerr<<" max: ("<<numr<<"+"<<maxat.second<<")%(2*"<<maxat.first<<")/"<<maxat.first<<"=="<<((numr+maxat.second)%(2*maxat.first)/maxat.first)<<endl;)
    if(1-(numr+maxat.second)%(2*maxat.first)/maxat.first==1)cout<<"evil"<<endl; //choose for minority
    else cout<<"good"<<endl;
    delete[] wave;
    delete[] scores;
    return 0;
}

Połącz z g++ -O3 -std=c++0x -o Scientist Scientist.cpp (nie potrzebujesz ostrzeżeń, więc nie -Wall) i uruchom z Scientist.exe(prawdopodobnie włączając argument oczywiście). Jeśli naprawdę ładnie pytasz, mogę dostarczyć plik wykonywalny dla systemu Windows.

Aha, i nie waż się mieszać z formatem wejściowym. W przeciwnym razie zrobi dziwne rzeczy.

EDYCJA: Najwyraźniej poprzedniej wersji zabrakło czasu około 600 rund do gry. To nie powinno tego robić. Zużycie czasu jest kontrolowane przez #define WINDOW (...)linię, więcej jest wolniejsze, ale spogląda wstecz.

Tomsmeding
źródło
8
Pobieranie plików wykonywalnych napisanych przez ponad 60 nieznajomych w Internecie wydaje się złym pomysłem.
Rainbolt
@Rusher Całkowicie się zgadzam. Jeśli chcesz mieć problemy, to krok pierwszy w przewodniku „dla manekinów”. Moja oferta jest
ważna
2
Ten ma dobrze skompilować (i konkurować).
Rainbolt
14

Code Runner

Aby uczynić rzeczy interesującymi, stworzyłem skrypt do automatycznego pobierania kodu z każdej opublikowanej odpowiedzi, kompilowania go w razie potrzeby, a następnie uruchamiania wszystkich rozwiązań zgodnie z regułami. W ten sposób ludzie mogą sprawdzić, jak sobie radzą. Po prostu zapisz ten skrypt w run_all.py (wymaga BeautifulSoup), a następnie:

usage:
To get the latest code: 'python run_all.py get'
To run the submissions: 'python run_all.py run <optional num_runs>'

Kilka spraw:

  1. Jeśli chcesz dodać obsługę większej liczby języków lub alternatywnie usunąć obsługę niektórych, zobacz def submission_type(lang).
  2. Rozszerzenie skryptu powinno być dość łatwe, nawet w przypadku języków wymagających kompilacji (patrz CPPSubmission). Typ języka jest pobierany ze znacznika meta kodu < !-- language: lang-java -- >, więc pamiętaj o dodaniu go, jeśli chcesz, aby kod był uruchamiany (Usuń dodatkowe spacje przed i po <>). AKTUALIZACJA : Istnieje teraz bardzo podstawowe wnioskowanie, aby spróbować wykryć język, jeśli nie jest zdefiniowany.
  3. Jeśli Twój kod w ogóle nie uruchomi się lub nie zakończy w wyznaczonym czasie, zostanie dodany blacklist.texti automatycznie usunięty z przyszłych prób. Jeśli naprawisz kod, po prostu usuń wpis z czarnej listy i uruchom ponownie get,

Obecnie obsługiwane języki:

 submission_types =  {
    'lang-ruby': RubySubmission,
    'lang-python': PythonSubmission,
    'lang-py': PythonSubmission,
    'lang-java': JavaSubmission,
    'lang-Java': JavaSubmission,
    'lang-javascript': NodeSubmission,
    'lang-cpp': CPPSubmission,
    'lang-c': CSubmission,
    'lang-lua': LuaSubmission,
    'lang-r': RSubmission,
    'lang-fortran': FortranSubmission,
    'lang-bash': BashSubmission
}

Bez zbędnych ceregieli:

import urllib2
import hashlib
import os
import re
import subprocess
import shutil
import time
import multiprocessing
import tempfile
import sys
from bs4 import BeautifulSoup

__run_java__ = """
public class Run {
    public static void main(String[] args) {
        String input = "";
        Human h = new __REPLACE_ME__();
        if(args.length == 1)
            input = args[0];
        try {
            System.out.println(h.takeSides(input));
        }
        catch(Exception e) {
        }
    }
}
"""

__human_java__ = """
public abstract class Human {
    public abstract String takeSides(String history) throws Exception;
}
"""

class Submission():
    def __init__(self, name, code):
        self.name = name
        self.code = code

    def submissions_dir(self):
        return 'submission'

    def base_name(self):
        return 'run'

    def submission_path(self):
        return os.path.join(self.submissions_dir(), self.name)

    def extension(self):
        return ""

    def save_submission(self):
        self.save_code()

    def full_command(self, input):
        return []

    def full_path(self):
        file_name = "%s.%s" % (self.base_name(), self.extension())
        full_path = os.path.join(self.submission_path(), file_name)
        return full_path

    def save_code(self):    
        if not os.path.exists(self.submission_path()):
            os.makedirs(self.submission_path())

        with open(self.full_path(), 'w') as f:
            f.write(self.code)

    def write_err(self, err):
        with open(self.error_log(), 'w') as f:
            f.write(err)

    def error_log(self):
        return os.path.join(self.submission_path(), 'error.txt')

    def run_submission(self, input):
        command = self.full_command()
        if input is not None:
            command.append(input)
        try:
            output,err,exit_code = run(command,timeout=1)
            if len(err) > 0:
                self.write_err(err)
            return output
        except Exception as e:
            self.write_err(str(e))
            return ""

class CPPSubmission(Submission):
    def bin_path(self):
        return os.path.join(self.submission_path(), self.base_name())

    def save_submission(self):
        self.save_code()
        compile_cmd = ['g++', '-O3', '-std=c++0x', '-o', self.bin_path(), self.full_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'cpp'

    def full_command(self):
        return [self.bin_path()]

class CSubmission(Submission):
    def bin_path(self):
        return os.path.join(self.submission_path(), self.base_name())

    def save_submission(self):
        self.save_code()
        compile_cmd = ['gcc', '-o', self.bin_path(), self.full_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'c'

    def full_command(self):
        return [self.bin_path()]

class FortranSubmission(Submission):
    def bin_path(self):
        return os.path.join(self.submission_path(), self.base_name())

    def save_submission(self):
        self.save_code()
        compile_cmd = ['gfortran', '-fno-range-check', '-o', self.bin_path(), self.full_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'f90'

    def full_command(self):
        return [self.bin_path()]

class JavaSubmission(Submission):   
    def base_name(self):
        class_name = re.search(r'class (\w+) extends', self.code)
        file_name = class_name.group(1)
        return file_name

    def human_base_name(self):
        return 'Human'

    def run_base_name(self):
        return 'Run'

    def full_name(self, base_name):
        return '%s.%s' % (base_name, self.extension())

    def human_path(self):
        return os.path.join(self.submission_path(), self.full_name(self.human_base_name()))

    def run_path(self):
        return os.path.join(self.submission_path(), self.full_name(self.run_base_name()))

    def replace_in_file(self, file_name, str_orig, str_new):
        old_data = open(file_name).read()
        new_data = old_data.replace(str_orig, str_new)

        with open(file_name, 'w') as f:
            f.write(new_data)

    def write_code_to_file(self, code_str, file_name):
        with open(file_name, 'w') as f:
            f.write(code_str)

    def save_submission(self):
        self.save_code()
        self.write_code_to_file(__human_java__, self.human_path())
        self.write_code_to_file(__run_java__, self.run_path())

        self.replace_in_file(self.run_path(), '__REPLACE_ME__', self.base_name())
        self.replace_in_file(self.full_path(), 'package Humans;', '')

        compile_cmd = ['javac', '-cp', self.submission_path(), self.run_path()]
        errout = open(self.error_log(), 'w')
        subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)

    def extension(self):
        return 'java'

    def full_command(self):
        return ['java', '-cp', self.submission_path(), self.run_base_name()]

class PythonSubmission(Submission):
    def full_command(self):
        return ['python', self.full_path()]

    def extension(self):
        return 'py'

class RubySubmission(Submission):
    def full_command(self):
        return ['ruby', self.full_path()]

    def extension(self):
        return 'rb'

class NodeSubmission(Submission):
    def full_command(self):
        return ['node', self.full_path()]

    def extension(self):
        return 'js'

class LuaSubmission(Submission):
    def full_command(self):
        return ['lua', self.full_path()]

    def extension(self):
        return 'lua'

class RSubmission(Submission):
    def full_command(self):
        return ['Rscript', self.full_path()]

    def extension(self):
        return 'R'

class BashSubmission(Submission):
    def full_command(self):
        return [self.full_path()]

    def extension(self):
        return '.sh'

class Scraper():
    def download_page(self, url, use_cache = True, force_cache_update = False):
        file_name = hashlib.sha1(url).hexdigest()

        if not os.path.exists('cache'):
            os.makedirs('cache')

        full_path = os.path.join('cache', file_name)
        file_exists = os.path.isfile(full_path)

        if use_cache and file_exists and not force_cache_update:
            html = open(full_path, 'r').read()
            return html

        opener = urllib2.build_opener()
        opener.addheaders = [('User-agent', 'Mozilla/5.0')]
        response = opener.open(url)
        html = response.read()

        if use_cache:
            f = open(full_path, 'w')
            f.write(html)
            f.close()

        return html

    def parse_post(self, post):
        name = post.find(text=lambda t: len(t.strip()) > 0)
        pre = post.find('pre')
        lang = pre.attrs['class'][0] if pre.has_attr('class') else None
        code = pre.find('code').text
        user = post.find(class_='user-details').find(text=True)
        return {'name':name,'lang':lang,'code':code,'user':user}

    def parse_posts(self, html):
        soup = BeautifulSoup(html)
        # Skip the first post
        posts = soup.find_all(class_ = 'answercell')
        return [self.parse_post(post) for post in posts]

    def get_submissions(self,  page = 1, force_cache_update = False):
        url = "http://codegolf.stackexchange.com/questions/33137/good-versus-evil?page=%i&tab=votes#tab-top" % page
        html = self.download_page(url, use_cache = True, force_cache_update = force_cache_update)
        submissions = self.parse_posts(html)
        return submissions

class Timeout(Exception):
    pass

def run(command, timeout=10):
    proc = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    poll_seconds = .250
    deadline = time.time()+timeout
    while time.time() < deadline and proc.poll() == None:
        time.sleep(poll_seconds)

    if proc.poll() == None:
        if float(sys.version[:3]) >= 2.6:
            proc.terminate()
        raise Timeout()

    stdout, stderr = proc.communicate()
    return stdout, stderr, proc.returncode


def guess_lang(code):
    if re.search(r'class .* extends Human', code):
        return 'lang-java'
    if re.search(r'import sys', code):
        return 'lang-python'
    if re.search(r'puts', code) and (re.search(r'ARGV', code) or re.search(r'\%w', code)):
        return 'lang-ruby'
    if re.search(r'console\.log', code):
        return 'lang-javascript'
    if re.search(r'program', code) and re.search(r'subroutine', code):
        return 'lang-fortran'
    if re.search(r'@echo off', code):
        return 'lang-bash'
    return None


def submission_type(lang, code):
    submission_types =  {
        'lang-ruby': RubySubmission,
        'lang-python': PythonSubmission,
        'lang-py': PythonSubmission,
        'lang-java': JavaSubmission,
        'lang-Java': JavaSubmission,
        'lang-javascript': NodeSubmission,
        'lang-cpp': CPPSubmission,
        'lang-c': CSubmission,
        'lang-lua': LuaSubmission,
        'lang-r': RSubmission,
        'lang-fortran': FortranSubmission,
        'lang-bash': BashSubmission
    }

    klass = submission_types.get(lang)

    if klass is None:
        lang = guess_lang(code)
        klass = submission_types.get(lang)

    return klass

def instantiate(submission):
    lang = submission['lang']
    code = submission['code']
    name = submission['name']

    klass = submission_type(lang, code)
    if klass is not None:
        instance = klass(name, code)
        return instance
    print "Entry %s invalid - lang not supported: %s" % (name, lang)
    return None

def get_all_instances(force_update):
    scraper = Scraper()

    print 'Scraping Submissions..'

    pages = [1,2,3]
    submissions_by_page = [scraper.get_submissions(page=i, force_cache_update=force_update) for i in pages]
    submissions = [item for sublist in submissions_by_page for item in sublist]

    # Get instances
    raw_instances = [instantiate(s) for s in submissions]
    instances = [i for i in raw_instances if i]

    print "Using %i/%i Submissions" % (len(instances), len(submissions))

    return instances

def save_submissions(instances):
    print 'Saving Submissions..'

    for instance in instances:
        instance.save_submission()

def init_game(save=True, force_update=False):
    instances = get_all_instances(force_update)
    if save:
        save_submissions(instances)
    return instances

def one_run(instances, input):
    valid = {
        'good': 1,
        'evil': 0
    }

    disqualified = []
    results = []

    for instance in instances:
        out = instance.run_submission(input)
        res = out.strip().lower()
        if res not in valid:
            disqualified.append(instance)
        else:
            results.append(valid[res])

    return (results, disqualified)

def get_winner(scores, instances):
    max_value = max(scores)
    max_index = scores.index(max_value)
    instance = instances[max_index]
    return (instance.name, max_value)

def update_scores(results, scores, minority_counts, minority_num):
    for i in range(len(results)):
        if results[i] == minority_num:
            minority_counts[i] += 1
            scores[i] += (minority_counts[i] - 1)
        else:
            minority_counts[i] = 0
            scores[i] += 3

def try_run_game(instances, num_runs = 1000, blacklist = None):
    current_input = None
    minority_str = None
    num_instances = len(instances)
    scores = [0] * num_instances
    minority_counts = [0] * num_instances

    print "Running with %i instances..." % num_instances

    for i in range(num_runs):
        print "Round: %i - Last minority was %s" % (i, minority_str)
        results, disqualified = one_run(instances, current_input)

        if len(disqualified) > 0:
            for instance in disqualified:
                print "Removing %s!" % instance.name
                instances.remove(instance)

                if blacklist is not None:
                    with open(blacklist, 'a') as f:
                        f.write("%s\n" % instance.name)

            return False

        latest_result = "".join(map(str,results))
        current_input = "%s,%s" % (current_input, latest_result)

        minority_num = 1 if results.count(1) < results.count(0) else 0
        minority_str = 'good' if minority_num == 1 else 'evil'

        update_scores(results, scores, minority_counts, minority_num)
        name, score = get_winner(scores, instances)
        print "%s is currently winning with a score of %i" % (name, score)

    print "The winner is %s with a score of %i!!!" % (name, score)
    return True

def find_instance_by_name(instances, name):
    for instance in instances:
        if instance.name == name:
            return instance
    return None

def maybe_add_or_remove_baelish(instances, baelish):
    num_instances = len(instances)

    if num_instances % 2 == 0:
        print 'There are %i instances.' % num_instances
        try:
            instances.remove(baelish)
            print 'Baelish Removed!'
        except:
            instances.append(baelish)
            print 'Baelish Added!'

def remove_blacklisted(blacklist, instances):
    blacklisted = []

    try:
        blacklisted = open(blacklist).readlines()
    except:
        return

    print 'Removing blacklisted entries...'

    for name in blacklisted:
        name = name.strip()
        instance = find_instance_by_name(instances, name)
        if instance is not None:
            print 'Removing %s' % name
            instances.remove(instance)

def run_game(instances, num_runs):
    blacklist = 'blacklist.txt'
    remove_blacklisted(blacklist, instances)

    baelish = find_instance_by_name(instances, 'Petyr Baelish') 
    maybe_add_or_remove_baelish(instances, baelish)

    while not try_run_game(instances, num_runs = num_runs, blacklist = blacklist):
        print "Restarting!"
        maybe_add_or_remove_baelish(instances, baelish)

    print "Done!"

if __name__ == '__main__':
    param = sys.argv[1] if len(sys.argv) >= 2 else None

    if param == 'get':
        instances = init_game(save=True, force_update=True)
    elif param == 'run':
        instances = init_game(save=False, force_update=False)
        num_runs = 50
        if len(sys.argv) == 3:
            num_runs = int(sys.argv[2])
        run_game(instances, num_runs)
    else:
        self_name = os.path.basename(__file__)
        print "usage:"
        print "To get the latest code: 'python %s get'" % self_name
        print "To run the submissions: 'python %s run <optional num_runs>'" % self_name
WhatAWorld
źródło
Dlaczego nie ma języka Fortran ?
Kyle Kanos
@KyleKanos - dodałem wsparcie dla niego, wkrótce zaktualizuję kod.
WhatAWorld
Tak! Ja (trochę) ciężko pracowałem nad przesłaniem Fortrana i Rusher nie może go uruchomić, więc chciałbym, żeby ktoś go dostał :)
Kyle Kanos
1
@ Rusher: Zgadzam się z PeterTaylor w tej sprawie: podświetlanie składni, ponieważ jedyna sugerowana edycja powinna zostać odrzucona. Edycji należy używać do istotnych korekt , a nie drobnych rzeczy.
Kyle Kanos
1
Zasługujesz na to za przedstawiciela, ale ponieważ nie jest to dokładnie odpowiedź na pytanie (i prawdopodobnie mogłaby skorzystać z dodawania przez społeczność innych języków), myślę, że technicznie powinna to być wiki społeczności.
Martin Ender
13

Piękny umysł, Ruby

Podejmuje decyzję w oparciu o wzorce o wątpliwym znaczeniu w bitowej reprezentacji ostatniej rundy

require 'prime'

if ARGV.length == 0
    puts ["good", "evil"].sample
else
    last_round = ARGV[0].split(',').last
    puts Prime.prime?(last_round.to_i(2)) ? "good" : "evil"
end

Biegnij jak

ruby beautiful-mind.rb
Martin Ender
źródło
13

Piustitious, Lua

Przesądny program, który wierzy w Znaki i Cuda.

history = arg[1]

if history == nil then
    print("good")
else
    local EvilSigns, GoodSigns = 0,0
    local SoulSpace = ""

    for i in string.gmatch(history, "%d+") do
         SoulSpace = SoulSpace .. i 
    end

    if string.match(SoulSpace, "1010011010")  then -- THE NUBMER OF THE BEAST!
        local r = math.random(1000)
        if r <= 666 then print("evil") else print("good") end
    else
        for i in string.gmatch(SoulSpace, "10100") do -- "I'M COMING" - DEVIL
            EvilSigns = EvilSigns + 1
        end
        for i in string.gmatch(SoulSpace, "11010") do -- "ALL IS WELL" - GOD
            GoodSigns = GoodSigns + 1
        end

        if EvilSigns > GoodSigns then 
            print("evil")
        elseif GoodSigns > EvilSigns then
            print("good")
        elseif GoodSigns == EvilSigns then
            local r = math.random(1000)
            if r <= 666 then print("good") else print("evil") end
        end
    end
end

uruchom z:

lua Piustitious.lua

a następnie wejście.

AndoDaan
źródło
11

Winchesters

Sam i Dean są dobrzy (przez większość czasu).

package Humans;

public class TheWinchesters extends Human {

    @Override
    public String takeSides(String history) throws Exception {
        return Math.random() < 0.1 ? "evil" : "good";
    }

}
CommonGuy
źródło
Czy na pewno masz 9:1odpowiedni stosunek? Może powinniśmy przeprowadzić eksplorację danych i uzyskać bardziej precyzyjny stosunek?
recursion.ninja
1
@awashburn Zacząłem oglądać Supernatural 2 miesiące temu (teraz utknąłem w sezonie 9) i 9:1wydaje mi się to w porządku;)
CommonGuy
10

Statystyk

public class Statistician extends Human{
    public final String takeSides(String history) { 
        int side = 0;
        String[] hist = history.split(",");
        for(int i=0;i<hist.length;i++){
            for(char c:hist[i].toCharArray()){
                side += c == '1' ? (i + 1) : -(i + 1);
            }
        }
        if(side == 0) side += Math.round(Math.random());
        return side > 0 ? "good" : "evil";
    }
}
Niezasłużony
źródło
5
Ta ostatnia ostatnia linia jest niesamowita
cjfaure
5
@ Uneserved Zamiast Math.ceil(Math.random()-Math.random())ciebie możesz również zrobić tylko Math.round(Math.random()).
tomsmeding
10

R, nieco bayesowski bot

Użyj tabeli częstotliwości dla każdego użytkownika jako wcześniejszego prawdopodobieństwa wyników innych użytkowników.

args <- commandArgs(TRUE)
if(length(args)!=0){
    history <- do.call(rbind,strsplit(args,","))
    history <- do.call(rbind,strsplit(history,""))
    tabulated <- apply(history,2,function(x)table(factor(x,0:1)))
    result <- names(which.max(table(apply(tabulated, 2, function(x)sample(0:1,1, prob=x)))))
    if(result=="1"){cat("good")}else{cat("evil")}
}else{
    cat("good")
    }

Wywoływany za pomocą Rscript BayesianBot.Rnastępuje wejście.

Edycja : Aby wyjaśnić, co to robi, oto krok po kroku z przykładowym wejściem:

> args
[1] "11011,00101,11101,11111,00001,11001,11001"
> history #Each player is a column, each round a row
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    1    0    1    1
[2,]    0    0    1    0    1
[3,]    1    1    1    0    1
[4,]    1    1    1    1    1
[5,]    0    0    0    0    1
[6,]    1    1    0    0    1
[7,]    1    1    0    0    1

> tabulated #Tally of each player previous decisions.
  [,1] [,2] [,3] [,4] [,5]
0    2    2    4    5    0
1    5    5    3    2    7

Następnie linia rozpoczynająca się od result<-, dla każdego gracza, wybiera losowo 0 lub 1, używając tego ostatniego stołu jako wag (tj. Dla gracza 1 prawdopodobieństwo wybrania 0 wynosi 2/7, wybrania 1 5/7 itd.). Wybiera jeden wynik dla każdego gracza / kolumny i ostatecznie zwraca liczbę, która zakończyła się najczęściej.

plannapus
źródło
10

szwajcarski

Zawsze zachowuje neutralność. Skazany na to, że nigdy nie wygra.

package Humans;

/**
 * Never choosing a side, sustaining neutrality
 * @author Fabian
 */
public class Swiss extends Human {   
    public String takeSides(String history) {
        return "neutral"; // wtf, how boring is that?
    }
}
fabigler
źródło
Nie napisałem tego!
Rainbolt
To ironia. Neutralność nigdy nie wygrywa
bajeczny
2
@ Rusher ah mam go teraz: D
fabigler
1
Nawet się nie kompiluje - brakuje średnika.
Paŭlo Ebermann
9

HAL 9000

#!/usr/bin/env perl
print eval("evil")

Edycja: może jest to bardziej odpowiednie dla HAL 9000, ale bądź ostrożny! To jest bardzo złe. Zalecam cdopróżnienie katalogu przed jego uruchomieniem.

#!/usr/bin/env perl
print eval {
    ($_) = grep { -f and !/$0$/ } glob('./*');
    unlink;
    evil
}

Spowoduje to usunięcie jednego pliku z cwdkażdego wywołania!

Nie tak oczywiste wywołanie:

W M $

D:\>copy con hal_9000.pl
#!/usr/bin/env perl
print eval("evil")
^Z
        1 file(s) copied.

D:>hal_9000.pl
evil

W * nix

[core1024@testing_pc ~]$ tee hal_9000.pl
#!/usr/bin/env perl
print eval("evil")
# Press C-D here
[core1024@testing_pc ~]$ chmod +x $_
[core1024@testing_pc ~]$ ./$_
evil[core1024@testing_pc ~]$
rdzeń 1024
źródło
Musisz podać polecenie, którego można użyć do uruchomienia programu. Aby uzyskać więcej informacji, zobacz sekcję „Elementy dostawy”.
Rainbolt
@Rusher Done;)
core1024
9

Wola większości

import sys
import random

if len(sys.argv)==1:
    print(random.choice(['good','evil']))
else:
    rounds=sys.argv[1].split(',')
    last_round=rounds[-1]
    zeroes=last_round.count('0')
    ones=last_round.count('1')
    if ones>zeroes:
        print('good')
    elif zeroes>ones:
        print('evil')
    elif ones==zeroes:
        print(random.choice(['good','evil']))

Zapisz jako WotM.py, uruchom jakopython3 WotM.py z danymi wejściowymi.

Prosty program, aby zobaczyć, jak to zrobi. Idzie z tym, co większość powiedziała ostatnio, albo losowo.

isaacg
źródło
Musisz podać polecenie, którego można użyć do uruchomienia programu. Aby uzyskać więcej informacji, zobacz sekcję „Elementy dostawy”.
Rainbolt
Cholera, to czyni mój duplikat. : D Zmieniłem kopalnię na mniejszość.
Martin Ender
@ Russell Dodano polecenie. Tego właśnie szukałeś?
isaacg
@isaacg Perfect!
Rainbolt
1
Obliczyłem średni ranking na podstawie wyników w tabeli wyników i ten wpis wygrywa według tej miary.
Brilliand
9

Alan Shearer

Powtarza to, co właśnie powiedziała osoba, obok której siedzi. Jeśli dana osoba się myli, przechodzi do następnej osoby i zamiast tego powtarza to, co mówi.

package Humans;

/**
 * Alan Shearer copies someone whilst they're right; if they get predict
 * wrongly then he moves to the next person and copies whatever they say.
 *
 * @author Algy
 * @url http://codegolf.stackexchange.com/questions/33137/good-versus-evil
 */
public class AlanShearer extends Human {

    private char calculateWinner(String round) {
        int good = 0, evil = 0;

        for (int i = 0, L = round.length(); i < L; i++) {
            if (round.charAt(i) == '1') {
                good++;
            } else {
                evil++;
            }
        }

        return (good >= evil) ? '1' : '0';
    }

    /**
     * Take the side of good or evil.
     * @param history The past votes of every player
     * @return A String "good" or "evil
     */
    public String takeSides(String history) {
        String[] parts = history.split(",");
        String lastRound = parts[parts.length() - 1];

        if (parts.length() == 0 || lastRound.length() == 0) {
            return "good";
        } else {
            if (parts.length() == 1) {
                return lastRound.charAt(0) == '1' ? "good" : "evil";
            } else {
                int personToCopy = 0;

                for (int i = 0, L = parts.length(); i < L; i++) {
                    if (parts[i].charAt(personToCopy) != calculateWinner(parts[i])) {
                        personToCopy++;

                        if (personToCopy >= L) {
                            personToCopy = 0;
                        }
                    }
                }
            }

            return lastRound.charAt(personToCopy) == '1' ? "good" : "evil";
        }
    }
}
Algy Taylor
źródło
Odwołujesz się do zmiennej wywoływanej, lastRoundzanim jeszcze ją zadeklarujesz. Ponadto dodałeś nawiasy do wszystkich, String.lengthale to nie jest funkcja. Czy uda ci się przesłać zgłoszenie do punktu, w którym zostanie ono skompilowane?
Rainbolt
@Rusher - gotowe :)
Algy Taylor
@Algy: lastRound.lengthjest nadal dostępny (w pierwszym przypadku) przed lastRoundzadeklarowanym (w tym przypadku, jeśli jest inny). Spróbuj go skompilować (i być może uruchomić) przed przesłaniem go tutaj.
Paŭlo Ebermann
@ PaŭloEbermann - przepraszam, nie jestem w środowisku, w którym mogę to uruchomić - wprowadzona poprawka
Algy Taylor
Teraz odwołujesz się do zmiennej o nazwie „personToCopy”, gdy jest poza zakresem. Właśnie przeniosłem go do bloku else, żeby się skompilował, ale nie wiem, czy tego właśnie chciałeś.
Rainbolt
8

Później jest zło, JavaScript ( node.js )

Mierzy czas między wykonaniami. Jeśli różnica czasu jest większa niż poprzednio, musi być zła. W przeciwnym razie dobrze.

var fs = require('fs'),
currentTime = (new Date).getTime();

try {
    var data = fs.readFileSync('./laterisevil.txt', 'utf8');
} catch (e) { data = '0 0'; } // no file? no problem, let's start out evil at epoch

var parsed = data.match(/(\d+) (\d+)/),
lastTime = +parsed[1],
lastDifference = +parsed[2],
currentDifference = currentTime - lastTime;

fs.writeFileSync('./laterisevil.txt', currentTime + ' ' + currentDifference, 'utf8');
console.log(currentDifference > lastDifference? 'evil' : 'good');

Biegnij z: node laterisevil.js

nderscore
źródło
8

Wyszukiwarka wzorców, Python

Szuka powtarzającego się wzoru, a jeśli nie może go znaleźć, po prostu idzie z większością.

import sys

if len(sys.argv) == 1: 
    print('good')
    quit()

wins = ''.join(
    map(lambda s: str(int(s.count('1') > s.count('0'))),
        sys.argv[1].split(',')
    )
)

# look for a repeating pattern
accuracy = []

for n in range(1, len(wins)//2+1):
    predicted = wins[:n]*(len(wins)//n)
    actual    = wins[:len(predicted)]
    n_right = 0
    for p, a in zip(predicted, actual):
        n_right += (p == a)
    accuracy.append(n_right/len(predicted))

# if there's a good repeating pattern, use it
if accuracy:
    best = max(accuracy)
    if best > 0.8:
        n = accuracy.index(best)+1
        prediction = wins[:n][(len(wins))%n]
        # good chance of success by going with minority
        if prediction == '1':
            print('evil')
        else:
            print('good')
        quit()

# if there's no good pattern, just go with the majority
if wins.count('1') > wins.count('0'):
    print('good')
else:
    print('evil')

Biegnij z

python3 pattern_finder.py
CesiumLifeJacket
źródło
1
Uwielbiam ten kod, kiedy go uruchamiam, zawsze dostaje jakoś 3000 pkt.
Realdeo,
8

Kamizelka

Kamizelka wierzy, że ze względu na innych dotychczasowych walczących większość po każdej rundzie zmienia się między dobrem a złem częściej, niż pozostaje po tej samej stronie. W ten sposób zaczyna pierwszą rundę od arbitralnego stronnictwa z dobrem, a następnie naprzemiennie co rundę, próbując częściej pozostać w drużynie wygranej lub przegranej.

package Humans;

public class Turncoat extends Human {
    public final String takeSides(String history) {
        String[] hist = history.split(",");

        return (hist.length % 2) == 0 ? "good" : "evil";
    }
}

Po napisaniu tego, zdałem sobie sprawę, że z powodu wpisów opartych na analizie statystycznej pęd spowodowałby, że większość zamieniłaby strony mniej, ponieważ ukończono więcej rund. Stąd leniwy kamizelka.

Leniwy kamizelka

Leniwy kamizelka zaczyna się jak kamizelka, ale gdy mija runda, staje się coraz bardziej leniwy, aby przejść na drugą stronę.

package Humans;

public class LazyTurncoat extends Human {
    public final String takeSides(String history) {
        int round = history.length() == 0 ? 0 : history.split(",").length;
        int momentum = 2 + ((round / 100) * 6);
        int choice = round % momentum;
        int between = momentum / 2;

        return choice < between ? "good" : "evil";
    }
}
jaybz
źródło
2
Leniwy kamizelka jest świetna!
Angelo Fuchs,
Obejmuję oba, jeśli nie masz nic przeciwko.
Rainbolt
Śmiało. Jestem ciekawy, jak sobie poradzą, szczególnie te, które kompilują statystyki głosowania.
jaybz
@Rainbolt Właśnie zauważyłem głupi błąd w kamizelce. Nie trzeba jednak tego poprawiać. Nadal działa, ale nie do końca zgodnie z przeznaczeniem, a nawet jeśli nie jest za późno, aby to naprawić, naprawienie go sprawi, że zachowa się dokładnie tak, jak jeden z nowszych wpisów. Jeśli chcesz, możesz dołączyć / wykluczyć.
jaybz
8

Biograf, Ruby

rounds = ARGV[0].split(',') rescue []

if rounds.length < 10
  choice = 1
else
  outcome_history = ['x',*rounds.map{|r|['0','1'].max_by{|s|r.count s}.tr('01','ab')}]
  player_histories = rounds.map{|r|r.chars.to_a}.transpose.map{ |hist| outcome_history.zip(hist).join }
  predictions = player_histories.map do |history|
    (10).downto(0) do |i|
      i*=2
      lookbehind = history[-i,i]
      @identical_previous_behavior = history.scan(/(?<=#{lookbehind})[10]/)
      break if @identical_previous_behavior.any?
    end
    if @identical_previous_behavior.any?
      (@identical_previous_behavior.count('1')+1).fdiv(@identical_previous_behavior.size+2)
    else
      0.5
    end
  end
  simulations = (1..1000).map do
    votes = predictions.map{ |chance| rand < chance ? 1 : 0 }
    [0,1].max_by { |i| votes.count(i) }
  end
  choice = case simulations.count(1)/10
    when 0..15
      1
    when 16..50
      0
    when 51..84
      1
    when 85..100
      0
  end
end

puts %w[evil good][choice]

Moja próba prawie inteligentnego wejścia (właściwie inteligentna wymagałaby testowania w terenie). Napisane w Ruby, więc jest szansa, że ​​będzie to zbyt wolne, ale na mojej maszynie i tak zajmuje to .11 sekund, aby obliczyć ostatnią rundę, gdy jest 40 losowych graczy, więc mam nadzieję, że zadziała wystarczająco dobrze.

zapisz jako biographer.rb, uruchom jakoruby biographer.rb

Chodzi o to, że dla każdego gracza, ocenia on szanse na wybranie „dobrego”, patrząc zarówno na własne wybory w ciągu ostatnich dziesięciu rund, jak i na ogólne wyniki, i znajdując w przeszłości przypadki, w których identyczne okoliczności (ich głosy + ogółem wyniki). Wybiera najdłuższy wygląd (do 10 rund), tak aby istniał jakikolwiek precedens, i używa go do stworzenia częstotliwości (dostosowanej zgodnie z prawem sukcesji Laplace'a, aby nigdy nie mieć 100% pewności co do nikogo).

Następnie przeprowadza symulacje i sprawdza, jak często Dobry wygrywa. Jeśli symulacje zakończyły się w większości w ten sam sposób, to prawdopodobnie dobrze się powiedzie, więc wybierze przewidywaną mniejszość. Jeśli nie jest pewny, wybiera przewidywaną większość.

histocrat
źródło
8

Judasz

Judasz jest naprawdę dobrą osobą. Szkoda, że ​​zdradzi dobrych facetów za kilka groszy.

package Humans;

public class Judas extends Human {

    private static final String MONEY = ".*?0100110101101111011011100110010101111001.*?";

    public String takeSides(String history) {
       return history != null && history.replace(",","").matches(MONEY) ? "evil" : "good";
    }
}
William Barbosa
źródło
1
To tylko kiedykolwiek głosów zło jeśli istnieje wystarczająca liczba uczestników, może chcesz usunąć ,OUT history, nawet bardziej tak Rusher ma zamiar podzielić się z gry w grupach.
Angelo Fuchs
Nie wiedziałem, że podzieli grę na grupy. Rzeczywiście czekałem, aż to pytanie będzie mieć wystarczającą liczbę zgłoszeń, zanim opublikowałem swoją odpowiedź z powodu rozmiaru ciągu. Dzięki, że dałeś mi znać.
William Barbosa
Jeśli wiesz, jak przekazać argument 60000 znaków do procesu w systemie Windows, daj mi znać. W przeciwnym razie przepraszam za zepsucie wpisu i dziękuję za jego naprawienie! Nie spodziewałem się, że otrzymam tyle zgłoszeń.
Rainbolt
7

Fallacious Gambler (Python)

Jeśli jedna ze stron wygrała większość razy z rzędu, gracz zdaje sobie sprawę, że druga strona ma większą szansę na przewagę w następnej rundzie (prawda?) I to wpływa na jego głos. Dąży do mniejszości, ponieważ jeśli uda jej się wejść do mniejszości, prawdopodobnie uda jej się tam kilka razy (prawda?) I zdobędzie wiele punktów.

import sys
import random

def whoWon(round):
    return "good" if round.count("1") > round.count("0") else "evil"

if len(sys.argv) == 1:
    print random.choice(["good", "evil"])
else:
    history = sys.argv[1]
    rounds = history.split(",")
    lastWin = whoWon(rounds[-1])
    streakLength = 1
    while streakLength < len(rounds) and whoWon(rounds[-streakLength]) == lastWin:
        streakLength += 1
    lastLoss = ["good", "evil"]
    lastLoss.remove(lastWin)
    lastLoss = lastLoss[0] 
    print lastWin if random.randint(0, streakLength) > 1 else lastLoss  

Stosowanie

W pierwszej rundzie:

python gambler.py

a następnie:

python gambler.py 101,100,001 etc.
komandos
źródło
4
Podoba mi się to, jak jesteś pewien swojego kodu, prawda? : P
IEatBagels
7

Automat komórkowy

W tej grze używa się konwencjonalnych zasad gry Conwaya. Najpierw tworzona jest siatka 2D z poprzednich głosów. Następnie „świat” przesuwa się o jeden krok do przodu i obliczana jest łączna liczba pozostałych żywych komórek. Jeśli liczba ta jest większa niż połowa całkowitej liczby komórek, wybierana jest wartość „dobra”. W przeciwnym razie wybierane jest „zło”.

Proszę wybaczyć błędy, które zostały rozbite podczas mojej przerwy na lunch. ;)

package Humans;

public class CellularAutomaton extends Human {

    private static final String GOOD_TEXT = "good";

    private static final String EVIL_TEXT = "evil";

    private int numRows;

    private int numColumns;

    private int[][] world;

    @Override
    public String takeSides(String history) {
        String side = GOOD_TEXT;

        if (history.isEmpty()) {
            side = Math.random() <= 0.5 ? GOOD_TEXT : EVIL_TEXT;
        }

        else {
            String[] prevVotes = history.split(",");

            numRows = prevVotes.length;

            numColumns = prevVotes[0].length();

            world = new int[numRows][numColumns];

            for (int i = 0; i < numColumns; i++) {
                for (int j = 0; j < numRows; j++) {
                    world[j][i] =
                        Integer.parseInt(Character.toString(prevVotes[j].charAt(i)));
                }
            }

            int totalAlive = 0;
            int total = numRows * numColumns;
            for (int i = 0; i < numColumns; i++) {
                for (int j = 0; j < numRows; j++) {
                    totalAlive += getAlive(world, i, j);
                }
            }
            if (totalAlive < total / 2) {
                side = EVIL_TEXT;
            }
        }

        return side;
    }

    private int getAlive(int[][] world, int i, int j) {
        int livingNeighbors = 0;

        if (i - 1 >= 0) {
            if (j - 1 >= 0) {
                livingNeighbors += world[j - 1][i - 1];
            }
            livingNeighbors += world[j][i - 1];
            if (j + 1 < numRows) {
                livingNeighbors += world[j + 1][i - 1];
            }
        }
        if (j - 1 >= 0) {
            livingNeighbors += world[j - 1][i];
        }
        if (j + 1 < numRows) {
            livingNeighbors += world[j + 1][i];
        }
        if (i + 1 < numColumns) {
            if (j - 1 >= 0) {
                livingNeighbors += world[j - 1][i + 1];
            }
            livingNeighbors += world[j][i + 1];
            if (j + 1 < numRows) {
                livingNeighbors += world[j + 1][i + 1];
            }
        }

        return livingNeighbors > 1 && livingNeighbors < 4 ? 1 : 0;
    }
}
Teoria grafów
źródło
1
Do testowania usunąłem wiersz wydruku. Wpisy Java muszą tylko zwracać dobro lub zło, a nie drukować.
Rainbolt
7

Profesor Ridge

Mam nadzieję, że korzystanie z bibliotek jest dozwolone, nie mam ochoty robić tego bez jednego =)

Podstawową ideą jest trenowanie klasyfikatora regresji grzbietu dla każdego uczestnika w ostatnich rundach, wykorzystując 30 wyników przed każdą rundą jako cechy. Pierwotnie obejmowała ostatnią rundę wyników dla wszystkich graczy, aby przewidzieć wynik również dla każdego gracza, ale to było raczej zbliżone do czasu, gdy liczba uczestników będzie większa (powiedzmy 50 lub więcej).

#include <iostream>
#include <string>
#include <algorithm>
#include "Eigen/Dense"

using Eigen::MatrixXf;
using Eigen::VectorXf;
using Eigen::IOFormat;
using std::max;

void regress(MatrixXf &feats, VectorXf &classes, VectorXf &out, float alpha = 1) {
    MatrixXf featstrans = feats.transpose();
    MatrixXf AtA = featstrans * feats;

    out = (AtA + (MatrixXf::Identity(feats.cols(), feats.cols()) * alpha)).inverse() * featstrans * classes;
}

float classify(VectorXf &weights, VectorXf &feats) {
    return weights.transpose() * feats;
}

size_t predict(MatrixXf &train_data, VectorXf &labels, VectorXf &testitem) {
    VectorXf weights;
    regress(train_data, labels, weights);
    return (classify(weights, testitem) > 0 ? 1 : 0);
}

static const int N = 30;
static const int M = 10;
// use up to N previous rounds worth of data to predict next round
// train on all previous rounds available
size_t predict(MatrixXf &data, size_t prev_iters, size_t n_participants) {
    MatrixXf newdata(data.rows(), data.cols() + max(N, M));
    newdata << MatrixXf::Zero(data.rows(), max(N, M)), data;

    size_t n_samples = std::min(500ul, prev_iters);
    if (n_samples > (8 * max(N, M))) {
        n_samples -= max(N,M);
    }
    size_t oldest_sample = prev_iters - n_samples;
    MatrixXf train_data(n_samples, N + M + 1);
    VectorXf testitem(N + M + 1);
    VectorXf labels(n_samples);
    VectorXf averages = newdata.colwise().mean();
    size_t n_expected_good = 0;
    for (size_t i = 0; i < n_participants; ++i) {
        for (size_t iter = oldest_sample; iter < prev_iters; ++iter) {
            train_data.row(iter - oldest_sample) << newdata.row(i).segment<N>(iter + max(N, M) - N)
                                  , averages.segment<M>(iter + max(N, M) - M).transpose()
                                  , 1; 
        }
        testitem.transpose() << newdata.row(i).segment<N>(prev_iters + max(N, M) - N)
                  , averages.segment<M>(prev_iters + max(N, M) - M).transpose()
                  , 1;
        labels = data.row(i).segment(oldest_sample, n_samples);
        n_expected_good += predict(train_data, labels, testitem);
    }
    return n_expected_good;
}


void fill(MatrixXf &data, std::string &params) {
    size_t pos = 0, end = params.size();
    size_t i = 0, j = 0;
    while (pos < end) {
        switch (params[pos]) {
            case ',':
                i = 0;
                ++j;
                break;
            case '1':
                data(i,j) = 1;
                ++i;
                break;
            case '0':
                data(i,j) = -1;
                ++i;
                break;
            default:
                std::cerr << "Error in input string, unexpected " << params[pos] << " found." << std::endl;
                std::exit(1);
                break;
        }
        ++pos;
    }
}

int main(int argc, char **argv) {
    using namespace std;

    if (argc == 1) {
        cout << "evil" << endl;
        std::exit(0);
    }

    string params(argv[1]);
    size_t n_prev_iters = count(params.begin(), params.end(), ',') + 1;
    size_t n_participants = find(params.begin(), params.end(), ',') - params.begin();

    MatrixXf data(n_participants, n_prev_iters);
    fill(data, params);

    size_t n_expected_good = predict(data, n_prev_iters, n_participants);

    if (n_expected_good > n_participants/2) {
        cout << "evil" << endl;
    } else {
        cout << "good" << endl;
    }
}

Kompilować

Zapisz kod źródłowy w pliku o nazwie ridge_professor.cc, pobierz bibliotekę Eigen i rozpakuj znajdujący się w niej folder Eigen do tego samego folderu, co plik źródłowy. Połącz zg++ -I. -O3 -ffast-math -o ridge_professor ridge_professor.cc .

Biegać

wywołaj ridge_professor.exe i w razie potrzeby podaj argument.

Pytanie

Ponieważ nie mogę jeszcze skomentować, zapytam tutaj: czy ograniczenie wielkości argumentów w oknach nie uniemożliwia wywołania wynikowych plików binarnych z całą historią na kilkaset obrotów? Myślałem, że nie możesz mieć więcej niż ~ 9000 znaków w argumencie ...

dgel
źródło
Dziękuję za zwrócenie mojej uwagi to . Wymyślę sposób, aby to działało, jeśli nie działa dobrze w Javie. Jeśli Java nie może tego zrobić, badania mówią mi, że C ++ może, i skorzystam z okazji, aby ponownie nauczyć się C ++. Wrócę wkrótce z wynikami testów.
Rainbolt
Jak się okazuje, Java nie podlega ograniczeniom wiersza poleceń. Wygląda na to, że problem powodują tylko polecenia większe niż 32k. Oto mój dowód (sam go napisałem): docs.google.com/document/d/… . Ponownie, naprawdę doceniam to, że poruszyłeś to, zanim jutro rozpoczną się próby.
Rainbolt
@Rusher Istnieje już 57 botów i planujesz, aby każdy bieg składał się z 1000 rund. To spowodowałoby, że Twój ciąg znaków ma 57k znaków (a więc> 32k), prawda?
plannapus
1
@Rusher Myślę, że lepiej jest przedłużyć oś czasu o kolejny tydzień i poprosić uczestników, aby zmienili swoje programy tak, aby czytali standard, zamiast używać ciągu argumentów. Byłoby banalne dla większości programów do zmiany
dgel
@dgel Oś czasu dla wyzwania jest nieskończenie długa, ale nie chcę zmieniać zasad w taki sposób, aby każdy musiał przepisać swoją odpowiedź. Jestem prawie pewien, że reguła, którą dodałem ostatniej nocy, przekręci tylko jedno zgłoszenie i planuję pomóc tej osobie, jeśli kiedykolwiek doprowadzi swój program do punktu, w którym się skompiluje.
Rainbolt
6

Crowley

Ponieważ Winchesterowie są znacznie mniej interesujący bez tego faceta. On oczywiście popiera zło ... chyba że jest potrzebne, aby zająć się większym złem.

package Humans;

public class Crowley extends Human {
public String takeSides(String history) {
    int gd = 0, j=history.length(), comma=0, c=0, z=0;
    while(comma < 2 && j>0)   {
        j--;
        z++;
        if (history.charAt(j) == ',') {
            comma++;
            if(c> z/2) {gd++;}
            z=0;
            c=0;
        } else if (history.charAt(j)=='1') {
            c++;
        } else {
        }
    }
    if(gd == 0){
        return "good";
    } else {
        return "evil";
    }
}}

Patrzę na dwie ostatnie tury (do tej pory 0 przecinków i 1 przecinek) i jeśli obie pozwolą wygrać złu, głosuję dobrze. W przeciwnym razie głosuję na zło.

kaine
źródło
Czy mam rację? Patrzysz na ostatnią turę i jeśli mniej niż 50% to „dobre” głosy, to popierasz „dobre” jeszcze ze złem? (Z ciekawości: Wolisz tajemnicze nazwy zmiennych, czy to przypadek?)
Angelo Fuchs
1
@AngeloNeuschitzer Patrzę na dwie ostatnie tury (do tej pory 0 przecinków i 1 przecinek) i jeśli obie pozwolą wygrać złu, głosuję dobrze. W przeciwnym razie głosuję na zło. Wolę nazwy zmiennych, które są krótkie do wpisania, jeśli kod jest wystarczająco krótki, cel kodu nie zostanie pomylony. Nie jestem profesjonalnym programistą i po raz pierwszy programowałem w Javie lub coś innego, kto widział kod od 6,5 roku. Napisałem to, by odświeżyć moją pamięć. (TLDR nie są dla mnie tajemnicze i jestem jedynym, dla którego zwykle
koduję
Dla jasności ... Crowley zaczynał jako człowiek, więc było celowe, że zaczyna dobrze ... Nie spodziewał się jednak, że pozostanie dobry na wszystkie rundy ... cholera
kaine