Zagrajmy w Reaper - Closed for Submissions

13

UWAGA : Zwycięzcą tego konkursu jest Jack !!!. Żadne kolejne zgłoszenia nie będą akceptowane.

Oto czat tego wyzwania . To mój pierwszy, więc jestem otwarty na sugestie!

Reaper to koncepcja gry opracowana przez Art of Problem Solving, która wymaga cierpliwości i chciwości. Po zmodyfikowaniu gry, aby pasowała do konkursu w stylu KOTH (dzięki @NathanMerrill i @dzaima za sugestie i ulepszenia), oto wyzwanie.

Gra działa w następujący sposób: mamy wartość znaną jako Reap, która mnoży się przez określoną stałą przy każdym tiku. Po każdym tyknięciu każdy bot ma opcję „zbierania”, co oznacza dodanie bieżącej wartości Reap do swojego wyniku i zmniejszenie Reap do 1.

Istnieje jednak ustalona liczba tyknięć, które bot musi odczekać między „zbiorem”, oraz stała liczba punktów niezbędnych do wygrania gry.

Wystarczająco proste? Oto twoje dane wejściowe:

I / O

Masz napisać funkcję w Pythonie 3, która pobiera 3 dane wejściowe. Pierwszy selfsłuży do odwoływania się do obiektów klasy (pokazano później). Drugi to Reapaktualna wartość Żniwa, którą zarobiłbyś, gdybyś „Żył”. Trzecia to prevReaplista botów, które zbierały podczas poprzedniego tyknięcia.

Inne obiekty, do których masz dostęp w swojej funkcji:

self.obj: An object for your use to store information between ticks.
self.mult: The multiplier that Reap is multiplied by each tick
self.win: The score you need to win
self.points: Your current set of points
self.waittime: The amount of ticks that you must wait between reaps during the game
self.time: The number of ticks since your last reap
self.lenBots: The number of bots (including you) in the game.
self.getRandom(): Use to produce a random number between 0 and 1.

Ty MUSI nie zmieniać żadnego z treścią tych obiektów, z wyjątkiem self.obj.

Musisz generować, 1aby zbierać, i cokolwiek innego (lub nic), aby nie zbierać. Zauważ, że jeśli zbierzesz, gdy nie czekałeś wystarczająco dużo tyknięć, zignoruję fakt, że postanowiłeś zbierać.

Zasady

Parametry będę używał to winning_score=10000, multiplier=1.6-(1.2/(1+sqrt(x))), waittime = floor(1.5*x)gdzie xjest liczbą botów w KOTH.

  • Gra kończy się, gdy gracz (lub wielu) osiągnie zwycięski wynik.
  • Kiedy wiele botów prosi o zbieranie na raz, pierwszeństwo mają boty, które czekały dłużej (w przypadku remisów, boty, które czekały maksymalny czas wszystkie mogą zbierać i zdobywać punkty w Reap)
  • Twój bot nie może zająć średnio więcej niż 100 ms przez 5 tyknięć.
  • Jeśli chcesz zaimportować biblioteki, zapytaj! Spróbuję dodać dowolne biblioteki, które mogę uruchomić na mojej stacjonarnej wersji Pythona (matematyka jest już zaimportowana: możesz ją swobodnie używać)
  • Wszystkie standardowe luki dla KoTH, takie jak zduplikowane boty, boty 1-up itp., Są podobnie zbanowane.
  • Wszelkie boty korzystające z dowolnej losowości muszą korzystać z getRandomfunkcji, którą podałem.

Kontroler można znaleźć w linku TIO poniżej. Aby go użyć, dodaj nazwę swojej funkcji BotListjako ciąg, a następnie dodaj funkcję do kodu. Zmodyfikuj, multiplieraby zmienić to, co Reap jest mnożone przez każdy tik, zmodyfikuj, winning_scoreaby zmienić wynik niezbędny do zakończenia gry, i zmodyfikuj, waittimeaby zmienić liczbę tyknięć, które będą czekać między zbiorami.

Dla Twojej wygody, oto kilka przykładowych (i raczej głupich) botów. Przesyłanie botów podobnych do tych nie będzie dozwolone. Pokazują jednak, jak działa kontroler.

def Greedybot(self,Reap, prevReap):
    return 1
def Randombot(self,Reap, prevReap):
    if self.obj == None:
        self.obj=[]
    self.obj.append(prevReap)
    if self.getRandom()>0.5:
        return 1

Dla zainteresowanych, oto Kontroler z wbudowanymi 15 zgłoszeniami: Wypróbuj online

OSTATECZNE REZULTATY

WOO SĄ OSTATECZNIE TUTAJ! Sprawdź powyższy link TIO, aby zobaczyć, jakiego kodu użyłem do wygenerowania końcowej klasyfikacji. Wyniki nie są wyjątkowo interesujące. Wyniki ponad 1000 przebiegów, które wykonałem z różnymi losowymi nasionami

1000 wins - Jack
0 wins - everyone else

Gratulacje dla zwycięzcy nagrody Bount Jack! (alias @Renzeee)

Don Thousand
źródło
Powiedzmy, że dwa boty zbierają w tym samym czasie, a wygrywa ten o najdłuższym czasie oczekiwania. Czy drugi bot również będzie miał włączony czas oczekiwania, mimo że nie był w stanie czerpać korzyści z tej rundy, w zasadzie marnując swoje „żniwo”? Co dzieje się, gdy dwa boty zbierają w tym samym czasie, z tym samym czasem oczekiwania?
Kevin Cruijssen
1
Czy wolno używać len(BotList)?
Renzeee,
1
@Renzeee Ooo o tym nie myślał! Dokonam szybkiej modyfikacji.
Don Thousand
1
@Renzeee Oh, to z pewnością coś użytecznego do rozważenia. Może zrobić drugiego bota podobnego do mojego Co 50, ale z faktycznymi obliczeniami w samym bocie, zamiast tego, co zrobiłem w moim opisie na podstawie 25botów w grze. Najpierw jednak trochę poczekam, by zobaczyć boty innych ludzi. Rushabh Mehta , czy będzie ostateczny termin / data, kiedy wszystkie boty zostaną uruchomione i zostanie wyłoniony zwycięzca?
Kevin Cruijssen
1
@Rushabh Mehta Gotcha, powstrzymam się. Właśnie zapytałem b / c, że niezależnie śledzę wyniki innych botów i czasy oczekiwania w celu ich snajperowania, i jestem leniwy. :)
Triggernometry

Odpowiedzi:

9

Niezdecydowany Twitchy Mess

def mess(self, Reap, prevReap):
    if not hasattr(self.obj, "start"):
            self.obj.start = False
    if self.time < self.waittime:
        return 0
    if self.points + Reap >= self.win:
            return 1
    if Reap >= self.waittime / (self.lenBots + 2):
        self.obj.start = True
    if self.obj.start:
        return 1 if self.getRandom() > 0.2 else 0
    return 1 if self.getRandom() > 0.8 else 0

Ten bot wykonuje najpierw zwykłe kontrole (czy mogę zbierać, czy mogę wygrać?), A następnie szuka wartości docelowej, zanim zacznie zbierać. Jest jednak niezdecydowany, więc po osiągnięciu celu zastanawia się, jak długo może poczekać i nie zbiera natychmiast. Ponadto jest niepewny, więc może przypadkowo „nacisnąć przycisk” i zebrać się przed celem.

Ciekawostka: w zasadzie gram jako żniwiarz jako człowiek.

Quintec
źródło
Niezły bot +1. Za chwilę przyjrzę się temu bliżej. Dołącz do czatu, jeśli jeszcze tego nie zrobiłeś
Don Thousand
@RushabhMehta Teraz z mniej niezdecydowaniem; p
Quintec
Dodam twoje zmiany, kiedy będę mógł!
Don Thousand
9

Snajper

Bot napędzany złośliwością. Śledzi czasy odnowienia i wyniki przeciwnika. Próby powstrzymania innych przed wygraną. Prawie nigdy tak naprawdę nie wygrywa, ale sprawia, że ​​gra jest frustrująca dla innych.

EDYTOWAĆ:

  • Jeśli zbieranie sprawi, że wygra, zbierz.
  • Jeśli nikt nie jest> = 70% zwycięskiego wyniku:

    • Jeśli wszyscy są w trakcie odnawiania, poczekaj do ostatniej możliwej chwili, aby skorzystać.
    • Jeśli ktokolwiek wygrałby, zbierając aktualną wartość, i są oni aktywni teraz lub byliby aktywni w następnej turze, czerp.
    • Jeśli co najmniej połowa innych użytkowników jest w trakcie odnawiania, spróbuj zebrać. Utrudnia to atakowanie określonych przeciwników, dlatego został usunięty.
    • W przeciwnym razie wykorzystaj 25% czasu (zasadniczo, aby zagwarantować, że ten bot zbierze CZASAMI, na wypadek, gdyby stało się coś dziwnego, tak jak wszyscy czekają kilka tur).
  • Jeśli ktoś JEST> = 70% zwycięskiego wyniku:

    • Jeśli Sniper może wygrać remis, a następna runda byłaby powyżej średniej wartości Reap dla przeciwnika o najwyższym wyniku, zbiera
    • Jeśli przeciwnik z największą liczbą punktów opuści swój czas odnowienia w następnej turze, czerp.
def Sniper(self, Reap, prevReap):
    # initialize opponents array
    if not hasattr(self.obj, "opponents"):
        self.obj.opponents = {}

    # initialize previous Reap value
    if not hasattr(self.obj, "lastReap"):
        self.obj.lastReap = 0

    # increment all stored wait times to see who will be "active" this turn
    for opponent in self.obj.opponents:
        self.obj.opponents[opponent]["time"] += 1

    # update opponents array
    for opponent in prevReap:
        # don't track yourself, since you're not an opponent
        if opponent != "Sniper":
            # initialize opponent
            if opponent not in self.obj.opponents:
                self.obj.opponents[opponent] = {"time": 0, "points": 0, "num_reaps": 0, "avg": 0}
            self.obj.opponents[opponent]["time"] = 0
            self.obj.opponents[opponent]["points"] += self.obj.lastReap
            self.obj.opponents[opponent]["num_reaps"] += 1
            self.obj.opponents[opponent]["avg"] = self.obj.opponents[opponent]["points"] / self.obj.opponents[opponent]["num_reaps"]

    # done "assigning" points for last round, update lastReap
    self.obj.lastReap = Reap

    # get current 1st place(s) (excluding yourself)
    winner = "" if len(self.obj.opponents) == 0 else max(self.obj.opponents, key=lambda opponent:self.obj.opponents[opponent]["points"])

    # you are ready now
    if self.time >= self.waittime:
        # current Reap is sufficient for you to win
        if self.points + Reap >= self.win:
            return 1

        if (
                # a 1st place exists
                winner != ''
                # if current 1st place is close to winning
                and self.obj.opponents[winner]["points"] / self.win >= .7
        ):
            if (
                    # next round's Reap value will be above opponent's average Reap
                    (Reap * self.mult >= self.obj.opponents[winner]["avg"])
                    # we have been waiting at least as long as our opponent (tiebreaker)
                    and self.time >= self.obj.opponents[winner]["time"]
            ):
                return 1

                # current 1st place opponent will be active next round
            if self.obj.opponents[winner]["time"] + 1 >= self.waittime:
                return 1

        else:
            if (
                    # everyone is waiting for their cooldown
                    all(values["time"] < self.waittime for key, values in self.obj.opponents.items())
                    # and we're tracking ALL opponents
                    and len(self.obj.opponents) == self.lenBots - 1
                    # at least one person will be ready next turn
                    and any(values["time"] + 1 >= self.waittime for key, values in self.obj.opponents.items())
            ):
                return 1

            if (
                    # opponent will be active next round
                    any( (values["time"] + 1 >= self.waittime)
                         # current Reap value would allow opponent to win
                         and (values["points"] + Reap >= self.win) for key, values in self.obj.opponents.items())
            ):
                return 1

            if (
                    # a 1st place exists
                    winner != ''
                    # current 1st place opponent will be active next round
                    and (self.obj.opponents[winner]["time"] + 1 >= self.waittime)
                    # next round's Reap value will be above their average Reap
                    and (Reap * self.mult >= self.obj.opponents[winner]["avg"])

            ):
                return 1

            # # at least half of opponents are waiting for their cooldown
            # if sum(values["time"] < self.waittime for key, values in self.obj.opponents.items()) >= (self.lenBots - 1) / 2:
            #     return 1

            # 25% of the time
            if self.getRandom() <= .25:
                return 1

    # default return: do not snipe
    return 0

Znudzony

Dla zabawy ten bot został przywieziony przez przyjaciela i tak naprawdę nie chce tu być. Rzucają k16, dopóki nie otrzymają liczby od 1 do 9, a następnie próbują zbierać za każdym razem, gdy liczba zawiera wybraną cyfrę. (Poszukiwanie d10 zakłóciłoby grę, która jest niegrzeczna, a 0 jest po prostu zbyt łatwe!)

def Bored(self, Reap, prevReap):
    # if this is the first round, determine your fav number
    if not hasattr(self.obj, "fav_int"):
        r = 0

        while r == 0:
            # 4 bits are required to code 1-9 (0b1001)
            for i in range(0, 4):
                # flip a coin. Puts a 1 in this bit place 50% of the time
                if self.getRandom() >= .50:
                    r += 2**i
            # if your random bit assigning has produced a number outside the range 1-9, try again
            if not (0 < r < 10):
                r = 0

        self.obj.fav_int = r

    # you are ready now
    if self.time >= self.waittime:
        # current Reap is sufficient for you to win
        if self.points + Reap >= self.win:
            return 1
        # do you like this value?
        if str(self.obj.fav_int) in str(Reap):
            return 1
        # do you like your wait time?
        if self.time % int(self.obj.fav_int) == 0:
            return 1

    # default return: do not reap
    return 0
Triggernometria
źródło
Niezły bot! +1. Ciekawie będzie zobaczyć, jak to działa.
Don Thousand
1
Myślę, że powinieneś użyć self.obj.opponents[opponent]["time"] += 1w pierwszej pętli for i self.obj.lastReapna końcu drugiej pętli for. Poza tym fajne pomysły. Jestem ciekawy, jak to zadziałałoby z wieloma innymi botami. Kiedy używam wielu chciwych i losowych botów, po prostu zbierze jak najszybciej, ponieważ przez większość czasu połowa botów nie może czerpać. Ale oczywiście nie są to realistyczni konkurenci.
Renzeee
@Triggernometry Powinieneś dołączyć do czatu. Sprawdź także opublikowane przeze mnie zmiany. Upewnij się, że zmiany, które wprowadziłem w twoim bocie są prawidłowe.
Don Thousand
7

Jacek

Jest to prosty bot z 4 zasadami:

  • Nie zbieraj, gdy nic nie robi
  • Zawsze zbieraj, gdy zbieranie pozwala nam wygrać
  • Żnij także, gdy nie został zebrany za 3 tiki
  • W przeciwnym razie nic nie rób

Zoptymalizowałem 3 tiki w porównaniu do obecnych istniejących botów (Sniper, grim_reaper, Every50, mess, BetterRandom, Averager, trochę więcej).

def Jack(self, Reap, prevReap):
    if self.time < self.waittime:
        return 0
    if self.win - self.points < Reap:
        return 1
    if self.mult ** 3 <= Reap:
        return 1
    return 0

Próbowałem pozostać przy moim starym rozwiązaniu (5 tyknięć), ale także czerpać, jeśli nie czerpiesz więcej niż X tyknięć, a następnie czerpać po przejściu mniejszej liczby tyknięć podczas braku zbierania (tj. 5, jeśli czekasz dłużej niż ja .waittime + 5, również zbiera, jeśli nie został zebrany za 4 tiki). Ale to nie poprawiło się po prostu zawsze zbierając po 4 tyknięciach zamiast 5.

Renzeee
źródło
5

Co 50

Te boty będą zbierać za każdym razem, gdy ich Reapilość przekroczy 50.

Dlaczego 50

Jeśli założę, że w grze będzie 25 botów, oznacza to, że multiplier = 1.6-(1.2/(1+sqrt(25))) = 1.4i waittime = floor(1.5*25) = 37. Od początku Reapzaczyna 1się tak:

Round: 1  2    3     4      5      6      7      8       9       10      11      12      13      14      15       16       17       18       19       20       etc.
Reap:  1  1.4  1.96  2.744  ~3.84  ~5.39  ~7.53  ~10.54  ~14.76  ~20.66  ~28.92  ~40.50  ~56.69  ~79.37  ~111.12  ~155.57  ~217.79  ~304.91  ~426.88  ~597.63  etc.

Jak widać, osiąga ponad 50 po 13 tyknięciach. Ponieważ Reapbędzie zresetować do 1 za każdym razem, gdy zbiera bot, a waittimedla bota, który zbiera wynosi 37, prawdopodobieństwo a zbiera bot prędzej niż później jest dość wysokie, zwłaszcza z botami podobnej do przykładu GreedyBot, który będzie czerpać jak najszybciej ich waittimeIs dostępne ponownie. Na początku chciałem zrobić 200, czyli 17 tyknięcie, nieco w połowie z 37 tyknięć w czasie oczekiwania, ale przy założeniu, że w grze jest 25 botów, istnieje duża szansa, że ​​ktoś inny Reapmnie porwie . Więc obniżyłem go do 50. To wciąż ładna zaokrąglona liczba, ale szczególnie dlatego, że jest to 13. tyknięcie (z 25 botami), a 13 i „zbieranie” również pasują trochę do tego samego „złego” gatunku.

Kod:

Kod jest śmiesznie trywialny ..

def Every50(self, Reap, prevReap):
  return int(Reap > 50)

Uwagi:

Ten bot jest dość zły z niską liczbą botów w grze. Na razie zostawię to, a może będę lepszym botem obliczającym najlepszy czas Reap. Przy wyjątkowo niskiej liczbie botów w grze waittimejest również znacznie niższa, więc nawet GreedyBotmoże dość łatwo wygrać z tego bota, jeśli waittimejest wystarczająco niski.

Mam nadzieję, że więcej osób doda o wiele więcej botów. ; p

Kevin Cruijssen
źródło
def Every49(self, Reap, prevReap): return Reap > 49 Twój ruch.
Quintec
@Quintec Hehe. Z 25 botami w grze oznaczałoby to, że wciąż jest to 13 tyknięcie i oboje wygrywamy Reap, więc nie mam nic przeciwko dzieleniu się zwycięstwem z tobą, lol. ; p
Kevin Cruijssen
Być może zechcesz intominąć nierówność, ponieważ 1 to prawdziwe polecenie
Don Thousand
@Quintec Jestem świadomy, że żartujesz, ale nie pozwolę na boty 1-up lub duplikaty
Don Thousand
@RushabhMehta Nie programuję zbyt często w Pythonie, więc naprawdę już wątpiłem, czy powinienem dodać obsadę, aby Truejawnie to jawić 1. Wyobrażałem sobie, że True == 1czek nadal będzie wracał Truedla mojego bota dodającego go do list Reapersw twojej nextfunkcji, ale dodałem rzutowanie do int tak jak sugerowałeś.
Kevin Cruijssen
5

Uśrednianie

def Averager(self,Reap,prevReap):
    returner = 0
    if not hasattr(self.obj,"last"):
        self.obj.last = Reap
        self.obj.total = 0
        self.obj.count = 0
        returner = 1
    else:
        if len(prevReap) > 0:
            self.obj.total += self.obj.last
            self.obj.count += 1
        self.obj.last = Reap
    if self.obj.count > 0 and Reap > self.obj.total / self.obj.count:
        returner = 1
    return returner

Ten bot próbuje zbierać za każdym razem, gdy aktualna wartość Reap jest wyższa niż średnia wartość Reapped.

Jo.
źródło
Bardzo fajny bot! +1
Don Thousand
Jestem zarówno bardzo zirytowany, jak i pod wrażeniem, że tak prosty algorytm bije wszystkich tak łatwo. Dobra robota!
Triggernometry
3

Ponury Żniwiarz

Bot utrzymuje bieżącą średnią wartości wszystkich poprzednich zbiorów, a także czas oczekiwania każdego bota. Zbiera, gdy czeka dłużej niż 3/4 innych botów, a żniwa są co najmniej 3/4 wielkości przeciętnego dotychczasowego żniwa. Celem jest zebranie dużej ilości żniw o niskim ryzyku w rozsądnej wielkości.

def grim_reaper(self, Reap, prevReap):
    if self.obj == None:
        self.obj = {}
        self.obj["reaps"] = []
        self.obj["prev"] = 1
        self.obj["players"] = {i:0 for i in range(math.ceil(self.waittime / 1.5))}
    if Reap == 1 and len(prevReap) > 0:
        self.obj["reaps"].append(self.obj["prev"])
        for player in prevReap:
            self.obj["players"][player] = 0

    retvalue = 0
    if (len(self.obj["reaps"]) > 0 
         and Reap > sum(self.obj["reaps"]) / len(self.obj["reaps"]) * 3. / 4.
         and sum([self.time >= i for i in self.obj["players"].values()]) >= len(self.obj["players"].values()) * 3 / 4):
        retvalue = 1

    for player in self.obj["players"]:
        self.obj["players"][player] += 1
    self.obj["prev"] = Reap
    return retvalue

Edycja: Naprawiono niektóre żenujące błędy składniowe.

Wypróbuj online

Zachary Cotton
źródło
1
Powinieneś użyć self.obj.reapszamiast self.reapsi self.objzamiast self.objecti prevReapzamiast prevLeapi add () po self.obj.players.valuesdwa razy. I myślę, że self.obj.reaps = []nie zadziała, chyba że self.objjest przedmiotem. Nie jestem do końca pewien, czy wszystko działa nadal zgodnie z przeznaczeniem i czy wszystko, co powiedziałem, jest prawdą, ale po tych zmianach i użyciu fałszywego obiektu, self.objgdy jeszcze nie istnieje, kod kompiluje się dla mnie.
Renzeee,
@ZacharyColton Nie musisz importować matematyki. Jest już zaimportowany
Don Thousand
@RushabhMehta Dodałem class Object(object):[newline] passna górze i użyłem self.obj = Object()w if not hasattr(..)(jeśli dobrze pamiętam).
Renzeee,
@Renzeee aha ic
Don Thousand
@ZacharyCotton Powinieneś dołączyć do czatu.
Don Thousand
3

BetterRandom

def BetterRandom(self,reap,prevReap):
    return self.getRandom()>(reap/self.mult**self.waittime)**-0.810192835

Bot opiera się na założeniu, że szansa na zbieranie powinna być proporcjonalna do wielkości zbieranych plonów, ponieważ punkt jest punktem, bez względu na to, kiedy zostanie zdobyty. Zawsze istnieje bardzo niewielka szansa na czerpanie korzyści, dzięki czemu zachowanie można wykorzystać. Najpierw pomyślałem, że będzie to wprost proporcjonalne i założyłem, że stała proporcjonalności będzie w pobliżu 1/mult^waittime(maksymalne czerpanie przy założeniu, że przynajmniej jeden bot gra zachłanny) po uruchomieniu niektórych symulacji stwierdziłem, że była to rzeczywiście optymalna stała. Ale bot wciąż był lepszy od Randoma, więc doszedłem do wniosku, że relacja nie była wprost proporcjonalna i dodałem stałą, aby obliczyć, jaka była relacja. Po kilku symulacjach stwierdziłem, że w porównaniu z moim testem zestaw botów -1.5był optymalny. W rzeczywistości odpowiada to odwrotnie proporcjonalnemu stosunkowi między szansą zbierania areap*sqrt(reap)co jest zaskakujące. Podejrzewam, że jest to w dużej mierze zależne od konkretnych botów, więc wersja tego bota, która oblicza K podczas gry, byłaby lepsza. (Ale nie wiem, czy możesz korzystać z danych z poprzednich rund).

EDYCJA: Zrobiłem program, aby automatycznie znaleźć rodzaj proporcjonalności. Na zestawie testowym ["myBot("+str(k)+")","Randombot","Greedybot","Every50","Jack","grim_reaper","Averager","mess"]znalazłem nową wartość.

fejfo
źródło
Niedługo dodam nowe statystyki za pomocą twojego bota
Don Thousand
1
Wygląda na to, że (reap/self.mult**self.waittime)**-0.810192835jest zawsze powyżej 1, tzn. Self.getRandom () nigdy nie jest wyższy.
Renzeee
@ interfejsfo możesz również korzystać z danych z poprzednich rund. Po to self.objjest. Aby zobaczyć kilka przykładów, jak go używać, spójrz na niektóre inne boty, które go używają.
Don Thousand
3

Cel

def target(self,Reap,prevReap):
    if not hasattr(self.obj, "target_time"):
        self.obj.target_time = -1
        self.obj.targeting = False
        self.obj.target = None
    if self.obj.target_time >= 0:
        self.obj.target_time += 1

    if self.time < self.waittime:
            return 0
    if self.points + Reap >= self.win:
        return 1
    if len(prevReap) > 0:
        if not self.obj.targeting:
            self.obj.target_time = 0
            self.obj.target = prevReap[int(self.getRandom() * len(prevReap))]
            self.obj.targeting = True
    if self.waittime <= self.obj.target_time + 1:
        self.obj.targeting = False
        self.obj.target = None
        self.obj.target_time = -1
        return 1
    return 0

Moje szanse na wygraną z bałaganem są prawie zerowe, więc czas zepsuć wszystkie inne boty na jak najwięcej sposobów! :)

Ten bot działa podobnie jak snajper. Ilekroć ktoś zbiera, wybiera losowy cel od tego, kto zbierze. Następnie po prostu czeka, aż ten cel prawie znów zbierze żniwo i go wycina. Nie zmienia to jednak ostrości - po wybraniu i zablokowaniu nie możesz uciec :)

Quintec
źródło
2

EveryN

Chyba czas na mojego drugiego bota tuż przed terminem.

Ten bot:

  • Pomiń, gdy jest jeszcze czas oczekiwania na ostatnie żniwo
  • Zbieraj, kiedy może wygrać
  • Żnij, gdy nikt nie zbierze przynajmniej nrund, gdzie noblicza się za pomocąn = 3 + ceil(self.waittime / self.lenBots)

Kod:

def every_n(self, Reap, prevReap):
    # Initialize obj fields
    if not hasattr(self.obj, "roundsWithoutReaps"):
        self.obj.roundsWithoutReaps = 0

    # Increase the roundsWithoutReaps if no bots reaped last round
    if len(prevReap) < 1:
        self.obj.roundsWithoutReaps += 1
    else
        self.obj.roundsWithoutReaps = 0

    # Skip if you're still in your waiting time
    if self.time < self.waittime:
        return 0
    # Reap if you can win
    if self.win - self.points < Reap:
        return 1

    # i.e. 25 bots: 3 + ceil(37 / 25) = 5
    n = 3 + math.ceil(self.waittime / self.lenBots)

    # Only reap when no bots have reaped for at least `n` rounds
    if self.obj.roundsWithoutReaps >= n:
        self.obj.roundsWithoutReaps = 0
        return 1

    return 0

Nie programuję zbyt często w Pythonie, więc jeśli zauważysz jakieś błędy, daj mi znać.

Kevin Cruijssen
źródło
Święta długa zmienna nazwa. (Również PEP: python.org/dev/peps/pep-0008 )
Quintec
@Quintec Zmieniłem wcięcie 2 spacji na 4; skrócił subsequentRoundsWithoutReapsdo roundsWithoutReaps; użyto małych liter ze znakami podkreślenia dla nazwy metody; i usunąłem nawias w instrukcjach if. Dzięki.
Kevin Cruijssen
Nie ma problemu! (Technicznie powinno to być rounds_without_reaps, ale to nie jest tak naprawdę problem, ponieważ w tym wyzwaniu wykorzystuje się także mixedCamelCase, więc to nie ma znaczenia)
Quintec
@Quintec Ah ok. Spojrzałem na prevReapa lenBotsi takie i zakładane zmienne są CamelCase jak w Javie. ;) Ach cóż, bez względu na to, jakiego przypadku użyjemy, i tak powinno działać. 2 zamiast 4 wciętych spacji prawdopodobnie spowodowałoby jednak pewne problemy, więc dziękuję w obu przypadkach.
Kevin Cruijssen
2

W toku: Mój projekt rozszerzenia T4T na każdy otwarty KOTH.

Wet za wet

def t4t(self, r, p):
    if(not hasattr(self.obj,"last")): self.obj.last = self.win
    if(p):
        self.obj.last = r
        return 0

    # The usual checks
    if self.time < self.waittime:
        return 0
    if self.points + r >= self.win:
        return 1

    if(r >= self.obj.last):
        return 1

Tit for n Tats

def t4nt(self, r, p):
    n = 5 # Subject to change
    if(not hasattr(self.obj,"last")): self.obj.last = [self.win]*n

    if(p):
        self.obj.last.append(r)
        self.obj.last.pop(0)
        return 0

    # The usual checks
    if(self.time < self.waittime):
        return 0
    if(self.points + r >= self.win):
        return 1

    if(r >= self.obj.last[0]):
        return 1

Kevin

Tylko po to, abyś był na nogach.

def kevin(just, a, joke):
    return 0
SIGSTACKFAULT
źródło
Pamiętaj, że self.lastnie jest rzeczą, ale możesz self.obj.lastcoś zrobić ! W każdym razie dodam wszystkie trzy twoje boty do memów +1
Don Thousand
Tak, jestem idiotą. Naprawiony.
SIGSTACKFAULT,
@RushabhMehta Właśnie przeszedł i sprawił, że faktycznie działały. proszę edytować.
SIGSTACKFAULT,
Brzmi dobrze! Dołącz do GC, opublikuję tam częściowe wyniki
Don Thousand
1

Przeciętny Joe

Zainspirował mnie Averager i stworzyłem bota, który oblicza średnią liczbę obrotów, zanim ktoś zbierze, i spróbuje zebrać jedną turę przed tym.

def average_joe(self, Reap, prevReap):

    if not hasattr(self.obj, "average_turns"):
        self.obj.turns_since_reap = 1
        self.obj.total_turns = 0
        self.obj.total_reaps = 0
        return 1

    if len(prevReap) > 0:
        self.obj.total_turns = self.obj.total_turns + self.obj.turns_since_reap
        self.obj.total_reaps += 1
        self.obj.turns_since_reap = 0
    else:
        self.obj.turns_since_reap += 1

    # Don't reap if you are in cooldown
    if self.time < self.waittime:
        return 0

    # Reap if you are going to win
    if self.win - self.points < Reap:
        return 1

    # Reap if it is one turn before average
    average_turns = self.obj.total_turns / self.obj.total_reaps

    if average_turns - 1 >= self.obj.turns_since_reap:
        return 1
    else:
        return 0
DobromirM
źródło
Dodam to jutro.
Don Thousand
1

Mocno zakodowane

Tak to jest.

def HardCo(self,reap,prevReap):
    return reap > 2

Zamiast uśredniania wyników z przeszłości, należy użyć wstępnie obliczonej średniej dla typowego przebiegu. Z czasem i tak nie będzie lepiej.

GB
źródło