Najmniejszy unikalny numer KoTH

27

Utwórz bota, aby wybrać najmniejszy unikalny numer.

(Na podstawie eksperymentu psychologicznego, o którym słyszałem wiele lat temu, ale nie byłem w stanie go ponownie wyśledzić.)

Zasady

  • Każda gra będzie się składać z 10 losowo wybranych botów grających w 1000 rund.
  • W każdej rundzie wszystkie boty wybierają liczbę całkowitą od 1 do 10 (włącznie). Wszelkie boty, które wybiorą tę samą wartość, zostaną wykluczone, a pozostały bot o najmniejszej wartości otrzyma punkt.
  • W przypadku, gdy żaden bot nie wybierze unikalnej wartości, nie zostaną przyznane żadne punkty.
  • Pod koniec 1000 rund wygrywa bot z największą liczbą punktów (lub wszystkie boty związane z największą liczbą punktów).
  • Turniej potrwa 200 * (liczba graczy) gier.
  • Bot z najwyższym procentem wygranych wygrywa turniej.

Dane techniczne

Boty muszą być klasami Python 3 i muszą implementować dwie metody: selecti update.
Boty będą budowane z indeksem.
selectnie przekazano żadnych argumentów i zwraca wybór bota dla bieżącej rundy.
updateprzekazywana jest lista wyborów dokonanych przez każdego bota w poprzedniej rundzie.

Przykład

class Lowball(object):
    def __init__(self, index):
        # Initial setup happens here.
        self.index = index
    def select(self):
        # Decision-making happens here.
        return 1
    def update(self, choices):
        # Learning about opponents happens here.
        # Note that choices[self.index] will be this bot's choice.
        pass

Kontroler

import numpy as np

from bots import allBotConstructors
allIndices = range(len(allBotConstructors))
games = {i: 0 for i in allIndices}
wins = {i: 0 for i in allIndices}

for _ in range(200 * len(allBotConstructors)):
    # Choose players.
    playerIndices = np.random.choice(allIndices, 10, replace=False)
    players = [allBotConstructors[j](i) for i, j in enumerate(playerIndices)]

    scores = [0] * 10
    for _ in range(1000):
        # Let everyone choose a value.
        choices = [bot.select() for bot in players]
        for bot in players:
            bot.update(choices[:])

        # Find who picked the best.
        unique = [x for x in choices if choices.count(x) == 1]
        if unique:
            scores[choices.index(min(unique))] += 1

    # Update stats.
    for i in playerIndices:
        games[i] += 1
    bestScore = max(scores)
    for i, s in enumerate(scores):
        if s == bestScore:
            wins[playerIndices[i]] += 1

winRates = {i: wins[i] / games[i] for i in allIndices}
for i in sorted(winRates, key=lambda i: winRates[i], reverse=True):
    print('{:>40}: {:.4f} ({}/{})'.format(allBotConstructors[i], winRates[i], wins[i], games[i]))

Dodatkowe informacje

  • Żaden bot nie będzie grał przeciwko sobie.
  • W mało prawdopodobnym przypadku włączenia bota w mniej niż 100 gier turniej zostanie wznowiony.
  • Boty mogą przechowywać stan między rundami, ale nie między grami.
  • Dostęp do kontrolera lub innych botów jest niedozwolony.
  • Liczba gier i liczba rund na grę mogą ulec zwiększeniu, jeśli wyniki będą zbyt zmienne.
  • Wszelkie boty, które zgłaszają błędy lub udzielają nieprawidłowych odpowiedzi (nie-int, wartości spoza [1, 10] itd.) Zostaną zdyskwalifikowane, a turniej zostanie ponownie uruchomiony bez nich.
  • Nie ma limitu czasowego na rundy, ale mogę go wprowadzić, jeśli boty zajmują zbyt dużo czasu na myślenie.
  • Nie ma ograniczenia liczby zgłoszeń na użytkownika.
  • Termin przesyłania zgłoszeń upływa o 23:59:59 UTC w piątek, 28 września . Turniej jest teraz zamknięty dla zgłoszeń.

Wyniki

                BayesBot: 0.3998 (796/1991)
      WhoopDiScoopDiPoop: 0.3913 (752/1922)
           PoopDiScoopty: 0.3216 (649/2018)
                   Water: 0.3213 (660/2054)
                 Lowball: 0.2743 (564/2056)
                Saboteur: 0.2730 (553/2026)
                OneUpper: 0.2640 (532/2015)
         StupidGreedyOne: 0.2610 (516/1977)
          SecondSaboteur: 0.2492 (492/1974)
                    T42T: 0.2407 (488/2027)
                     T4T: 0.2368 (476/2010)
          OpportunityBot: 0.2322 (454/1955)
              TheGeneral: 0.1932 (374/1936)
             FindRepeats: 0.1433 (280/1954)
                  MinWin: 0.1398 (283/2025)
             LazyStalker: 0.1130 (226/2000)
               FollowBot: 0.1112 (229/2060)
                Assassin: 0.1096 (219/1999)
           MostlyAverage: 0.0958 (194/2024)
             UnchosenBot: 0.0890 (174/1955)
                 Raccoon: 0.0868 (175/2015)
               Equalizer: 0.0831 (166/1997)
       AvoidConstantBots: 0.0798 (158/1980)
WeightedPreviousUnchosen: 0.0599 (122/2038)
               BitterBot: 0.0581 (116/1996)
               Profiteur: 0.0564 (114/2023)
              HistoryBot: 0.0425 (84/1978)
            ThreeFourSix: 0.0328 (65/1984)
                 Stalker: 0.0306 (61/1994)
             Psychadelic: 0.0278 (54/1943)
              Unpopulist: 0.0186 (37/1994)
             PoissonsBot: 0.0177 (35/1978)
         RaccoonTriangle: 0.0168 (33/1964)
              LowHalfRNG: 0.0134 (27/2022)
              VictoryPM1: 0.0109 (22/2016)
            TimeWeighted: 0.0079 (16/2021)
             TotallyLost: 0.0077 (15/1945)
            OneTrackMind: 0.0065 (13/1985)
              LuckySeven: 0.0053 (11/2063)
          FinalCountdown: 0.0045 (9/2000)
                Triangle: 0.0039 (8/2052)
           LeastFrequent: 0.0019 (4/2067)
                Fountain: 0.0015 (3/1951)
             PlayerCycle: 0.0015 (3/1995)
                  Cycler: 0.0010 (2/1986)
               SecureRNG: 0.0010 (2/2032)
             SneakyNiner: 0.0005 (1/2030)
            I_Like_Nines: 0.0000 (0/1973)

źródło
2
@Mnemonic Wszelkie wiadomości?
user1502040,
4
@Herohtar Uruchomiłem go, zanim wyszedłem do pracy. Przy odrobinie szczęścia należy to zrobić, kiedy wrócę do domu.
1
@Mnemonic Czy to już koniec?
user1502040,
2
@Justin Działa teraz i wydaje się, że nie ulega awarii, ale zdecydowanie nie miałbym nic przeciwko pomocy, jeśli ten test się nie powiedzie.
1
@MihailMalostanidis Utwórz plik wywoływany bots.pyw tym samym katalogu, zawierający wszystkie boty. Na koniec utwórz listę konstruktorów:allBotConstructors = [Lowball, BayesBot, ...]

Odpowiedzi:

10

BayesBot

Próbuje dokonać optymalnego wyboru za pomocą prostego modelu statystycznego.

import random

def dirichlet(counts):
    counts = [random.gammavariate(n, 1) for n in counts]
    k = 1. / sum(counts)
    return [n * k for n in counts]

class BayesBot(object):
    def __init__(self, index):
        self.index = index
        self.counts = [[0.2 * (10 - i) for i in range(10)] for _ in range(10)]
    def select(self):
        player_distributions = []
        for i, counts in enumerate(self.counts):
            if i == self.index:
                continue
            player_distributions.append(dirichlet(counts))
        cumulative_unique = 0.
        scores = [0.] * 10
        for i in range(10):
            p_unpicked = 1.
            for d in player_distributions:
                p_unpicked *= (1. - d[i])
            p_unique = p_unpicked * sum(d[i] / (1. - d[i]) for d in player_distributions)
            scores[i] = p_unpicked * (1. - cumulative_unique)
            cumulative_unique += p_unique * (1. - cumulative_unique)
        return scores.index(max(scores)) + 1
    def update(self, choices):
        for i, n in enumerate(choices):
            self.counts[i][n - 1] += 1
użytkownik1502040
źródło
10

Unikaj stałych botów

Śledź, które boty zawsze zwracały tę samą wartość, i pomiń te wartości. Z pozostałych wartości wybierz je losowo, ale znacznie spychasz w stronę niższych wartości.

import numpy as np

class AvoidConstantBots(object):
    all_values = range(1, 11)
    def __init__(self, index):
        self.index = index
        self.constant_choices = None

    def select(self):
        available = set(self.all_values)
        if self.constant_choices is not None:
            available -= set(self.constant_choices)
        if len(available) == 0:
            available = set(self.all_values)
        values = np.array(sorted(available))
        weights = 1. / (np.arange(1, len(values) + 1)) ** 1.5
        weights /= sum(weights)
        return np.random.choice(sorted(available), p=weights)

    def update(self, choices):
        if self.constant_choices is None:
            self.constant_choices = choices[:]
            self.constant_choices[self.index] = None
        else:
            for i, choice in enumerate(choices):
                if self.constant_choices[i] != choice:
                    self.constant_choices[i] = None
Przywróć Monikę
źródło
10

WaitWhatBot

Nie najbardziej konkurencyjny bot i zdecydowanie nie GTO , ale zdusi wynik każdego „zawsze 1” lub „prawie zawsze 1” przeciwnika w tej samej grze, jak w takim scenariuszu, WaitWhatBot również staje się takim botem.

Wykorzystuje zmieniające się prawdopodobieństwa z ważonymi wagami zarówno w czasie (nowsze -> większa waga), jak i wartości wyboru (niższy punkt -> większa waga).

Używa nieco zaciemnionego kodu dla odrobiny chichotu.

from random import choices as weightWeight
class WaitWhatBot(object):
    def __init__(wait,what):
        weight,weightWhat=5,2
        wait.what,wait.weight=what,(weight**(weight/weight/weightWhat)+weightWhat/weightWhat)/weightWhat
        wait.whatWeight,wait.weightWeight=[wait.what==wait.weight]*int(wait.weight**weight),wait.weight
        wait.whatWhat=wait.whatWeight.pop()#wait, when we pop weight off whatWeight what weight will pop?
        wait.waitWait=tuple(zip(*enumerate(wait.whatWeight,wait.weightWeight!=wait.whatWeight)))[weightWeight==wait.weight]
    def select(what):return int(what.weight**what.whatWhat if all(not waitWait for waitWait in what.whatWeight)else weightWeight(what.waitWait,what.whatWeight)[what.weight==what.what])
    def update(waitWhat,whatWait):
        what,wait,weightWhat=set(wait for wait in whatWait[:waitWhat.what]+whatWait[waitWhat.what+1:]if wait in waitWhat.waitWait),-~waitWhat.whatWhat,waitWhat.weightWeight
        while wait not in what:
            waitWhat.whatWeight[wait+~waitWhat.whatWhat]+=weightWhat
            weightWhat/=waitWhat.weight
            wait-=~waitWhat.whatWhat
        if not wait!=(what!=weightWhat):waitWhat.whatWeight[waitWhat.whatWhat]+=weightWhat
        waitWhat.weightWeight*=waitWhat.weight
Jonathan Allan
źródło
9
Ile wagi kupiłby WaitWhatBot, gdyby WaitWhatBot kupiłby tylko wagę?
Roman Odaisky,
set ([… dla… w…]) ≡ {… dla… w…}, nawiasem mówiąc
Roman Odaisky
@RomanOdaisky Właściwie to doradziłem komuś tego innego podczas golfa!
Jonathan Allan
5

Prześladowca

Na początku gry bot ten losowo wybiera określony indeks jako cel. Następnie śledzi, które są ukierunkowane na całą grę, kopiując liczbę wybraną w poprzedniej rundzie.

import random

class Stalker(object):
  def __init__(self, index):
    # choose a random target to stalk that isn't ourself
    self.targetIndex = random.choice([x for x in range(10) if x != index])
    # get a random number to start with since we haven't seen our target's value yet
    self.targetValue = random.randint(1, 10)
  def select(self):
    return self.targetValue
  def update(self, choices):
    # look at what our target chose last time and do that
    self.targetValue = choices[self.targetIndex]
Herohtar
źródło
4

Głupi chciwy

class StupidGreedyOne(object):
    def __init__(self, index):
        pass
    def select(self):
        return 1
    def update(self, choices):
        pass

Ten bot zakłada, że ​​inne boty nie chcą wiązać.

Zdaję sobie sprawę, że jest to to samo co podany przykład, ale pomyślałem o tym, zanim przeczytałem tak daleko. Jeśli jest to niezgodne ze sposobem uruchamiania wyzwań KoTH, daj mi znać.

Inżynier Toast
źródło
Ogólnie wolę nie mieć zduplikowanych botów, ale nie mam nic przeciwko temu.
1
@Monemoniczny, technicznie rzecz biorąc, nie jest to duplikat, ponieważ nie inicjuje się self.index.
hidefromkgb
@Mnemonic Nie ma problemu! Szczerze mówiąc, jest to mój pierwszy KoTH i mój pierwszy cokolwiek w Pythonie, więc po prostu śledziłem pierwsze dwa plakaty i nie zmieniłem go, mimo że podejrzewam, że powinienem to zrobić. Nie byłem również pewien, czy zamierzasz uwzględnić Lowball w swoich testach, czy był to naprawdę tylko przykład postu.
Engineer Toast
Bez obaw. Witamy w cudownym świecie KoTH!
2
Ci thew się "asa granat": puzzling.stackexchange.com/questions/45299/...
Kaine
4

HistoryBot

import random

class HistoryBot(object):
    def __init__(self, index):
        self.pastWins = []
    def select(self):
        if not self.pastWins:
            return 1
        return random.choice(self.pastWins)
    def update(self, choices):
        unique = [x for x in choices if choices.count(x) == 1]
        if unique:
            self.pastWins.append(min(unique))

Realizacja komentarza użytkownika 2390246:

A co z tym? Zacznij od 1. Po pierwszej rundzie śledź zwycięskie wartości i wybieraj losowo z prawdopodobieństwem równym liczbie wystąpień. Np. Jeśli zwycięskie wartości w pierwszych trzech rundach wynoszą [2, 3, 2], to w czwartej rundzie wybierz [2] przy p = 2/3 i [3] przy p = 1/3.

użytkownik48543
źródło
4

OneUpper

class OneUpper(object):
    def __init__(self, index):
        self.index = index
    def select(self):
        return 2
    def update(self, choices):
        pass

Boty wszystkich innych celują w 1 lub losowo, więc dlaczego nie po prostu dążyć do 2?

Zackary Murphy
źródło
4

Płyń jak woda

Unika podstawowych algorytmów ciągłego wykrywania botów, podwajając każdą liczbę, powoli przechodząc do niższych wartości, jeśli nie są zajęte.

class Water(object):
    def __init__(self, index):
        self.index = index
        self.round = 0
        self.play = 4
        self.choices = [0]*10

    def select(self):
        if self.round > 0 and self.round%2 == 0:
            if not max([1, self.play - 1]) in self.choices:
                self.play -= 1
        return self.play

    def update(self, choices):
        self.round += 1
        self.choices = choices
TCFP
źródło
Jestem ciekawy, czy twój bot jest w jakiś sposób związany z moją fontanną ? Oba są „zorientowane na wodę”, haha.
RedClover,
Szczerze mówiąc, mój pierwotny plan polegał na stworzeniu stałego bota zgadującego, który podwójnie odgaduje określone liczby, co było moją motywacją do podjęcia decyzji przez bota. Kiedy go wizualizowałem, myślałem o powolnym ruchu, który zainspirował nazwę.
Krzyczcie
To zajmuje 3. lub 4. (zwykle 3.) w każdym teście, który przeprowadzam. To dość niesamowite w przypadku tak prostej strategii.
Robert Fraser
4

Całkiem zagubiona

class TotallyLost(object):
    def __init__(self, index):
        self.index = index
        self.round = 0
        self.numbers = [4,8,1,5,1,6,2,3,4,2]
    def select(self):
        return self.numbers[self.round % len(self.numbers)]
    def update(self, choices):
        self.round = self.round + 1
Robert Fraser
źródło
4

Ostateczne odliczanie

class FinalCountdown(object):
    def __init__(self, index):
        self.round = -1
    def select(self):
        self.round += 1
        return (10 - self.round // 100)
    def update(self, choices):
        pass

Wypróbuj online!

Zwraca 10 za pierwsze 100 rund, 9 za następne 100 i tak dalej.

Laikoni
źródło
4

Opportunitybot

Ten bot śledzi najniższą liczbę niewybraną przez inne boty w każdej rundzie (najniższa dostępna liczba lub okazja) i odtwarza liczbę, która była tą liczbą najczęściej.

class OpportunityBot(object):
    def __init__(self, index):
        self.index = index
        self.winOccasions = [0,0,0,0,0,0,0,0,0,0]

    def select(self):
        return self.winOccasions.index(max(self.winOccasions))+1

    def update(self, choices):
        choices.pop(self.index)
        succeeded = [choices.count(i)==0 for i in range(1,11)]
        self.winOccasions[succeeded.index(True)] += 1
Mattermonkey
źródło
4

PatterMatcher

Szuka powtarzających się sekcji w zgłoszeniach botów, próbuje przewidzieć i unikać liczb.

class PatternMatcher(object):
    def __init__(self, index):
        self.bots=[[]]*9
        self.index=index
    def select(self):
        minVisible=3    #increase these if this bot is to slow
        minOccurences=2
        predictions=set()
        for bot in self.bots:     
            #match patters of the form A+(B+C)*minOccurences+B and use C[0] as a prediction      
            for lenB in range(minVisible,len(bot)//(minVisible+1)+1):
                subBot=bot[:-lenB]
                patterns=[] 
                for lenBC in range(lenB,len(subBot)//minOccurences+1):
                    BC=subBot[-lenBC:]
                    for i in range(1,minOccurences):
                        if BC!=subBot[-lenBC*i-lenBC:-lenBC*i]:
                            break
                    else:
                        patterns.append(BC)
                predictions|={pattern[lenB%len(pattern)] for pattern in patterns}
        other=set(range(1,11))-predictions
        if other: return min(other)
        else: return 1                

    def update(self, choices):
        j = 0
        for i,choice in enumerate(choices):
            if i == self.index:
                continue
            self.bots[j].append(choice)
            j += 1

Trójkąt

Szansa na wybranie n to (10-n)/45

import random
class Triangle(object):
    def __init__(self, index):pass
    def select(self):return random.choice([x for x in range(1, 11) for _ in range(10 - x)])
    def update(self, choices):pass

Ważony czasowo

Prawdopodobieństwo, że bot wybierze liczbę, jest proporcjonalne (10-n)*Δt. Pierwsza runda jest identyczna z trójkątem.

import random
class TimeWeighted(object):
    def __init__(self, index):
        self.last=[0]*10
        self.round=1 
    def select(self):
        weights=[(self.round-self.last[i])*(10-i) for i in range(10)]
        return 1+random.choice([x for x in range(10) for _ in range(weights[x])])

    def update(self, choices):
        for c in choices:
            self.last[c-1]=self.round
        self.round+=1

Najmniej często

Podaje najmniej występującą liczbę, jeśli są równe, weź najniższą.

class LeastFrequent(object):
    def __init__(self, index):self.frequenties=[0]*10
    def select(self):return 1+self.frequenties.index(min(self.frequenties))
    def update(self, choices):
        for c in choices:
            self.frequenties[c-1]+=1

Najdłuższy czas

Tak samo jak w przypadku częstości, ale najdłużej między zgłoszeniami.

class LongestTime(object):
    def __init__(self, index):
        self.frequencies=[0]*10
        self.round=1
    def select(self):return 1+self.frequencies.index(min(self.frequencies))
    def update(self, choices):
        for c in choices:
            self.frequencies[c-1]=self.round
        self.round+=1

Sabotażysta

Podaje najniższą liczbę, która została przesłana ostatnim razem.

class Saboteur(object):
    def __init__(self, index):self.last=[1]
    def select(self):return min(self.last)
    def update(self, choices):self.last=choices

SecondSaboteur

Przesyła drugą najniższą liczbę, która została przesłana ostatnim razem

class SecondSaboteur(object):
    def __init__(self, index):self.last=[1,2]
    def select(self):return min({i for i in self.last if i!=min(self.last)})
    def update(self, choices):self.last=choices

Profiteur

Przesyła najniższą liczbę, która nie została przesłana ostatnim razem

class Profiteur(object):
    def __init__(self, index):self.last=set()
    def select(self):return min(set(range(1, 11))-self.last, default=1)
    def update(self, choices):self.last=set(choices)

Przepraszam, że trochę mnie poniosło, gdy wpadłem na pomysł nowych botów podczas implementacji poprzedniego. Nie byłem pewien, który z nich byłby najlepszy i jestem ciekawy występu każdego z nich. Możesz je znaleźć tutaj: https://repl.it/@Fejfo/Lowest-Unique-Number

fejfo
źródło
Miły. Możesz rozważyć zmodyfikowanie Sabotażysty, aby zignorował swój ostatni wybór (chyba że jest to celowe). Myślę też, że być może będziesz musiał poradzić sobie z niektórymi specjalnymi przypadkami: co powinien zrobić SecondSaboteur, jeśli każdy bot wybierze tę samą wartość w danej rundzie, i co powinien zrobić Profiteur, jeśli każdy bot wybierze inną wartość? Później może być potrzebny końcowy nawias w Profiteur set(range(10).
Przywróć Monikę
PatternMatcher wydaje się mieć jakąś nieskończoną pętlę lub miejsce, w którym się zacina.
Robert Fraser
3

Najlepszy 50% bot RNG

import random

class LowHalfRNG(object):
    def __init__(self, index):
        pass
    def select(self):
        return random.randint(1, 5)
    def update(self, choices):
        pass

Właśnie miałem opublikować losowego bota, ale hidefromkgb wysłał przede mną (przez wysłanie robią się łatwym celem dla KGB, a nie dobrym sposobem na ukrycie). To jest moja pierwsza odpowiedź KOTH, mając nadzieję na pokonanie bota rng.

maxb
źródło
3

Cykler

Ten bot po prostu cyklicznie przechodzi przez każdą z kolejnych liczb. Dla zabawy inicjuje licznik z jego indeksem.

class Cycler(object):
  def __init__(self, index):
    self.counter = index # Start the count at our index
  def select(self):
    return self.counter + 1 # Add 1 since we need a number between 1-10
  def update(self, choices):
    self.counter = (self.counter + 1) % 10
Herohtar
źródło
3

OneTrackMind

Ten bot losowo wybiera liczbę i trzyma ją przez 50 rund, a następnie wybiera kolejną i powtarza.

import random

class OneTrackMind(object):
    def __init__(self, index):
        self.round = 0;
        self.target = random.randint(1,10)
    def select(self):
        return self.target
    def update(self, choices):
        self.round += 1;
        if self.round % 50 == 0:
            self.target = random.randint(1,10)
Jo.
źródło
3

Szczęśliwa siódemka

class LuckySeven(object):
    def __init__(self, index):
        pass
    def select(self):
        return 7
    def update(self, choices):
        pass

Mam dziś szczęście! Wyrzucam wszystko na 7!

AlexRacer
źródło
3

Mój pomysł jest taki, że strategia jest bardziej zależna od liczby botów niż od faktycznej oceny strategii.

Przy znacznej liczbie botów dostępne są następujące opcje:

  • „Chciwe” roboty celujące w niższe 1-3 numery 10 botów są „sprytne” i dążą do uzyskania niższych 1-3 liczb, najlepsze jest po prostu pozwolenie tym botom interweniować między nimi.

  • „Inteligentne” roboty, które po uświadomieniu sobie, że 4 są zawsze odbierane, pójdą gdzie indziej.

  • „Losowe” i „stałe” roboty. Nie ma tu wiele do zrobienia.

Więc stawiam na # 4.

class LazyStalker(object):
    def __init__(self, index):
        pass
    def select(self):
        return 4
    def update(self, choices):
        pass
SJuan76
źródło
2

Niezbędny bot RNG

import secrets

class SecureRNG(object):
    def __init__(self, index):
        pass
    def select(self):
        return secrets.randbelow(10) + 1
    def update(self, choices):
        pass
hidefromkgb
źródło
2

Morderca

Pozostaje w cieniu, a następnie dąży do bieżącej najniższej wartości. Biegać.

class Assassin(object):
    def __init__(self, index):
        self.index = index
        self.round = 0
        self.choices = [0]*10

    def select(self):
        if self.round == 0:
            return 10
        else:
            return min(self.choices)

    def update(self, choices):
        self.round += 1
        self.choices = choices
        self.choices[self.index] = 10
TCFP
źródło
2

FollowBot

Skopiuj zwycięzcę z ostatniej rundy lub przynajmniej najlepszy wybór z minimalną liczbą remisów, jeśli nie było zwycięzcy.

import collections

class FollowBot(object):
    def __init__(self, index):
        self.lastround = []

    def select(self):
        counter = collections.Counter(self.lastround)
        counts = [(count,value) for (value,count) in counter.items()]
        counts.sort()
        if len(counts) >= 1:
            return counts[0][1]
        else:
            return 1

    def update(self, choices):
        self.lastround = choices
RM
źródło
2

Psychadelic

Jedynym sposobem na zwycięstwo w wojnie nuklearnej jest oszalenie. Więc sprawię, że każdy bot predykcyjny w turnieju będzie szalony.

class Psychadelic(object):
    def __init__(self, index):
        self.index = index
    def select(self):
        return random.randint(1, self.index + 1)
    def update(self, choices):
        pass
Jozuego
źródło
2

UnchosenBot

class UnchosenBot(object):
    def __init__(self, index):
        self.index = index
        self.answer = 0
    def select(self):
        if self.answer == 0:
            return 1
        return self.answer
    def update(self, choices):
        self.answer = 0
        del choices[self.index]
        for x in range(1, 11):
            if x not in choices:
                self.answer = x
                return

Podejmuje decyzje z ostatniej rundy i wybiera najniższą niezbraną liczbę (oczywiście ignorując wybór UnchosenBot).

Mistrz Spitemaster
źródło
2

Whoop-di-scoop-di-kupa

class WhoopDiScoopDiPoop(object):
    def __init__(self, index):
        self.index = index
        self.guess = 1
        self.tenure = 0
        self.perseverance = 4

    def select(self):
        return self.guess

    def update(self, choices):
        others = {c for i, c in enumerate(choices) if i != self.index}
        for i in range(1, self.guess):
            if i not in others:
                self.guess = i
                self.tenure = 0
                self.perseverance += 1
                return
        if self.guess not in others:
            self.tenure = 0
            return
        self.tenure += 1
        if self.tenure > self.perseverance:
            if self.guess == 10:
                return
            self.guess += 1
            self.tenure = 0

Poop-di-scoopty

class PoopDiScoopty(object):
    def __init__(self, index):
        self.index = index
        self.guess = 1
        self.tenure = 0
        self.perseverance = 4

    def select(self):
        return self.guess

    def update(self, choices):
        others = [c for i, c in enumerate(choices) if i != self.index]
        for i in range(1, self.guess):
            if i not in others:
                self.guess = i
                self.tenure = 0
                self.perseverance += 1
                return
        if self.guess not in others:
            self.tenure = 0
            return
        self.tenure += others.count(self.guess) # this is the change
        if self.tenure > self.perseverance:
            if self.guess == 10:
                return
            self.guess += 1
            self.tenure = 0

Nigdy nie widziałem ani nie dotykałem Pythona, czy to nie jest mityczne?

Mihail Malostanidis
źródło
1
Dodaj wiersz <!-- language: lang-python -->przed blokiem kodu, aby włączyć wyróżnianie składni
Herman L
@HermanL Halucynowałem pythontag w pytaniu i myślałem, że to będzie automatyczne, ale napisałem coś złego.
Mihail Malostanidis,
1
Jeśli chodzi o pythonicity, kod jest całkiem dobry, z wyjątkiem tego, że można powiedzieć others = [c for i, c in enumerate(choices) if i != self.index], że można go uznać za pythononic , lub, ponieważ później używasz tej zmiennej tylko do testów członkostwa, { }zamiast [ ]budować setraczej niż a list.
Roman Odaisky,
if (self.guess)jest również bardzo mało mityczny.
Jonathan Frech,
Nie mam pojęcia, jak self.guesssię tam dostały te pareny ! Musiał być jednym z formaterów.
Mihail Malostanidis,
2

Fontanna

Prosty bot najpierw wybiera najniższą liczbę, a jeśli inny też go wybierze, zwiększy licznik - podłoga się wypełni i woda spłynie. Kiedy osiągnie 11, restartuje się do 1 - woda jest pompowana z powrotem na szczyt.

class Fountain:

    def __init__(self, index, target=10):

        # Set data
        self.index = index
        self.pick  = 1
        self.target = target+1

    def select(self):

        # Select the number
        return self.pick

    def update(self, choices: list):

        # Remove self from the list
        choices.pop(self.index)  # I hope `choices[:]` is passed, not `choices`.

        # While the selected number is occupied
        while self.pick in choices:

            # Pick next number
            self.pick += 1

            # If target was reached
            if self.pick == self.target:

                # Reset to 1
                self.pick = 1
Czerwona koniczyna
źródło
W obecnej formie twój bot utknie w pętli while, jeśli inne boty wybiorą wszystkie liczby od 1 do 8. Czy chciałeś ustawić target10?
Emil
@Emil Prawda, pierwotnie tak było, zmieniono
RedClover
2

PoissonsBot

Wybierz liczby z rozkładu Poissona, który jest tendencyjny do niższych wartości. Dostosuj średni parametr rozkładu w górę, jeśli jesteśmy w remisie, i w dół, jeśli są domysły pod nami. Rozmiar kroku stopniowo maleje wraz z postępem gry.

from numpy.random import poisson
import math

class PoissonsBot(object):
    def __init__(self, index):
        self.index = index
        self.mean = 2
        self.roundsleft = 1000

    def select(self):
        self.roundsleft = max(self.roundsleft-1, 2)
        return max(min(poisson(self.mean),10),1)

    def update(self, choices):
        myval = choices[self.index]
        nequal = len([c for c in choices if c==myval])
        nless = len([c for c in choices if c<myval])
        step = math.log10(self.roundsleft)
        if nequal > 1:
            self.mean += nequal/step
        self.mean -= nless/step
        self.mean = max(self.mean, 0.3)
thegreatemu
źródło
2

MinWin

Prowadzi bieżącą liczbę zwycięskich wartości i minimalne niezaznaczone wartości (gdzie minimalna niezaznaczona wartość jest brana pod uwagę tylko wtedy, gdy jest mniejsza niż wartość wygrywająca). Losowo wybiera spośród wygranych i wartości minimalnych.

import random

class MinWin:

    def __init__(self, index):
        self.index = index
        self.mins = list(range(1, 11))
        self.wins = list(range(1, 11))

    def select(self):
        return min(random.choice(self.mins), random.choice(self.wins))

    def update(self, choices):
        counts = [0] * 10
        for x in choices:
            counts[x - 1] += 1

        if 0 in counts and (1 not in counts or counts.index(0) < counts.index(1)):
            self.mins.append(counts.index(0) + 1)
        if 1 in counts:
            self.wins.append(counts.index(1) + 1)
Curtis Bechtel
źródło
2

PlayerCycle

Przechodzi między graczami. Wybór bieżącego gracza (może być własnym) jest teraz wyborem tego bota. Zaczyna drukować 8, bo dlaczego nie. Przepraszam, że nie mogę pytona, to prawdopodobnie zły kod.

import itertools
class PlayerCycle(object):
    def __init__(self, index):
        self.a = itertools.cycle(range(10))
        self.b = 8
    def select(self):
        return self.b
    def update(self, choices):
        self.b = choices[next(self.a)]

Edycja: Dzięki Triggernometry za ulepszenie mojego kodu za pomocą itertools

ev3commander
źródło
Twój kod działa dobrze, ale możesz dodać intertools.cycle (), aby automatycznie przełączał się między 0-9 i nie musiałeś zwiększać ani sprawdzać - Wypróbuj online!
Triggernometry
2

szop pracz

Wybierz najniższą liczbę, która nie została wybrana w poprzedniej rundzie, z wyjątkiem naszego własnego poprzedniego wyboru, który tym razem może zostać ponownie wybrany. W pierwszej rundzie wybierz 1. (Biorąc pod uwagę 9 przeciwników i 10 wyborów, na pewno jest jedna dostępna wartość).

Wymyśliłem to niezależnie, ale teraz widzę co najmniej 2 poprzednie boty, które są zasadniczo takie same.

class Raccoon(object):
    def __init__(self, index):
        self.index = index
        self.last_round = None
        self.domain = None
    def select(self):
        # Return the lowest number not chosen last time.
        if self.domain is None:
            return 1
        else:
            # This finds the smallest element of domain, not present in last_round
            return min(self.domain-self.last_round)
    def update(self, choices):
        last_round = choices[:]
        last_round[self.index] = 0 # don't include our own choice
        self.last_round = set(last_round)
        if self.domain is None:
            self.domain = set(range(1,len(choices)+1))

Trójkąt szopa pracza

Łączy szop pracz i trójkąt: spośród niezaznaczonych wartości wybierz jedną na podstawie prawdopodobieństwa odwrotnego trójkąta.

import random
class RaccoonTriangle(object):
    def __init__(self, index):
        self.index = index
        self.unchosen = set([1,])
        self.domain = None
    def select(self):
        # Return the lowest number not chosen last time.
        if self.domain is None:
            return random.randint(1,self.index+1)
        else:
            # Reverse triangle weights for unchosen values
            weighted_choices = [u for i,u in enumerate(sorted(self.unchosen),0) for _ in range(len(self.unchosen)-i)]
            return random.choice(weighted_choices)
    def update(self, choices):
        last_round = choices[:] # make a copy
        last_round[self.index] = 0 # don't include our own choice
        if self.domain is None:
            self.domain = set(range(1,len(choices)+1))
        self.unchosen = self.domain - set(last_round)
Mechanika kwantowa
źródło
Błąd:AttributeError: 'RaccoonTriangle' object has no attribute 'boundaries'
Renzeee,
1
Tak, przepraszam. Myślę, że to naprawiłem. Byłem w trakcie pisania testów, kiedy skończyłem.
Mechanika kwantowa,
1

Generał

Generał zawsze walczy do ostatniej wojny (y) .

import numpy
import random

class TheGeneral:
    def __init__(self, index):
        self.round = 0
        self.index = index
        self.would_have_won = [0] * 10

    def select(self):
        if self.round <= 100:
            return random.choice((list(numpy.nonzero(self.would_have_won)[0]) + [0, 1])[:2]) + 1

        return random.choice(numpy.argsort(self.would_have_won)[-2:]) + 1

    def update(self, choices):
        for i, s in enumerate(numpy.bincount([c - 1 for i, c in enumerate(choices)
            if i != self.index], minlength=10)):

            if s == 0:
                self.would_have_won[i] += 1
            elif s == 1:
                break

        self.round += 1
Roman Odaisky
źródło
1

Bez powtarzania losowo

import secrets

class NoRepeats(object):
    def __init__(self, index):
        self.lastround = secrets.randbelow(10) + 1

    def select(self):
        i = secrets.randbelow(10) + 1
        while i == self.lastround:
             i = secrets.randbelow(10) + 1
        self.lastround = i
        return self.lastround

    def update(self, choices):
        pass

Bot wybiera losowo, ale unika wybierania tego samego numeru co poprzednia runda.

Draco18s
źródło