Kleinste eindeutige Nummer KoTH


27

Erstellen Sie einen Bot, um die kleinste eindeutige Nummer auszuwählen.

(Basierend auf einem Psychologieexperiment, von dem ich vor vielen Jahren gehört habe, das ich aber nicht wieder finden konnte.)

Regeln

  • Jedes Spiel besteht aus 10 zufällig ausgewählten Bots, die 1000 Runden spielen.
  • In jeder Runde wählen alle Bots eine ganze Zahl von 1 bis 10 (einschließlich). Alle Bots, die denselben Wert wählen, werden ausgeschlossen und der verbleibende Bot mit dem kleinsten Wert erhält einen Punkt.
  • Für den Fall, dass kein Bot einen eindeutigen Wert auswählt, werden keine Punkte vergeben.
  • Am Ende von 1000 Runden gewinnt der Bot mit den meisten Punkten (oder alle Bots mit den meisten Punkten) das Spiel.
  • Das Turnier wird 200 * (Anzahl der Spieler) Spiele dauern.
  • Der Bot mit dem höchsten Gewinnanteil gewinnt das Turnier.

Spezifikationen

Bots müssen Python 3-Klassen sein und zwei Methoden implementieren: selectund update.
Bots werden mit einem Index erstellt.
selectEs werden keine Argumente übergeben und die Auswahl des Bots für die aktuelle Runde zurückgegeben.
updateEs wird eine Liste der von jedem Bot in der vorherigen Runde getroffenen Entscheidungen übergeben.

Beispiel

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

Regler

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]))

Zusätzliche Information

  • Kein Bot wird in einem Spiel gegen sich selbst spielen.
  • In dem unwahrscheinlichen Fall, dass ein Bot in weniger als 100 Spielen enthalten ist, wird das Turnier wiederholt.
  • Bots können den Status zwischen Runden speichern, jedoch nicht zwischen Spielen.
  • Der Zugriff auf den Controller oder andere Bots ist nicht gestattet.
  • Die Anzahl der Spiele und die Anzahl der Runden pro Spiel können sich erhöhen, wenn die Ergebnisse zu unterschiedlich sind.
  • Alle Bots, die Fehler auslösen oder ungültige Antworten geben (Nicht-Eingaben, Werte außerhalb von [1, 10] usw.), werden disqualifiziert und das Turnier wird ohne sie wiederholt.
  • Es gibt keine zeitliche Begrenzung für Runden, aber ich kann eine implementieren, wenn Bots zu lange zum Nachdenken brauchen.
  • Die Anzahl der Einsendungen pro Benutzer ist nicht begrenzt.
  • Einsendeschluss ist Freitag, 28. September, 23:59:59 UTC. Das Turnier ist nun für Einsendungen geschlossen.

Ergebnisse

                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)

2
@Mnemonic Irgendwelche Neuigkeiten?
user1502040

4
@Herohtar Ich habe es laufen lassen, bevor ich zur Arbeit gegangen bin. Mit etwas Glück sollte es getan werden, wenn ich nach Hause komme.

1
@Mnemonic Ist es schon fertig?
user1502040

2
@Justin Es läuft gerade und scheint nicht abzustürzen, aber ich hätte definitiv nichts dagegen, wenn die Hilfe fehlschlägt.

1
@MihailMalostanidis Erstellt eine Datei, die bots.pyim selben Verzeichnis aufgerufen wird und alle Bots enthält. Erstellen Sie am Ende eine Liste der Konstruktoren:allBotConstructors = [Lowball, BayesBot, ...]

Antworten:


10

BayesBot

Versucht, mithilfe eines einfachen statistischen Modells die optimale Auswahl zu treffen.

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

10

Vermeiden Sie konstante Bots

Verfolgen Sie, welche Bots immer den gleichen Wert zurückgegeben haben, und überspringen Sie diese Werte. Wählen Sie die verbleibenden Werte nach dem Zufallsprinzip aus, jedoch mit einer deutlichen Tendenz zu niedrigeren Werten.

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

10

WaitWhatBot

Nicht der konkurrenzfähigste Bot und definitiv nicht GTO , aber erdrosselt die Punktzahl eines "immer 1" - oder "fast immer 1" -Objekts im selben Spiel wie in einem solchen Szenario. WaitWhatBot wird auch so ein Bot.

Verwendet sich entwickelnde Wahrscheinlichkeiten mit gewichteten Gewichten sowohl in Bezug auf die Zeit (neuere -> größere Gewichtung) als auch den Auswahlwert (unterer Punkt -> größere Gewichtung).

Verwendet etwas verschleierten Code, um ein bisschen zu kichern.

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

9
Wie viel Gewicht hätte WaitWhatBot gekauft, wenn WaitWhatBot nur Gewicht gekauft hätte?
Roman Odaisky

set ([… für… in…]) ≡ {… für… in…} übrigens
Roman Odaisky

@RomanOdaisky Ich habe kürzlich jemandem davon geraten, um Golf zu spielen!
Jonathan Allan

5

Stalker

Zu Beginn des Spiels wählt dieser Bot zufällig einen bestimmten Index als Ziel. Es pirscht sich dann an das Ziel des gesamten Spiels und kopiert die Nummer, die es in der vorherigen Runde gewählt hat.

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]

4

Dumme Gierige

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

Dieser Bot geht davon aus, dass andere Bots nicht binden wollen.

Mir ist klar, dass dies dasselbe ist wie das angegebene Beispiel, aber ich hatte den Gedanken, bevor ich so weit gelesen hatte. Wenn dies nicht mit dem Ablauf der KoTH-Herausforderungen übereinstimmt, lassen Sie es mich wissen.


Im Allgemeinen bevorzuge ich es, keine doppelten Bots zu haben, aber es macht mir nichts aus, sie zu verlassen.

1
@Mnemonic Nun, technisch gesehen ist es kein Betrug, da es nicht initialisiert wird self.index.
Hidefromkgb

@Mnemonic Kein Problem! Um ehrlich zu sein, dies ist mein erstes KoTH und mein erstes Element in Python, also bin ich nur den ersten beiden Postern gefolgt und habe es trotz meines Verdachts nicht geändert. Ich war mir auch nicht sicher, ob Sie Lowball in Ihre Tests einbeziehen würden, oder es war wirklich nur ein Beispiel für den Beitrag.
Ingenieur Toast

Keine Bange. Willkommen in der wundervollen Welt von KoTH!


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))

Implementierung des Kommentars von user2390246:

Was ist dann damit? Beginnen Sie mit 1. Verfolgen Sie nach der ersten Runde die Gewinnwerte und wählen Sie sie zufällig mit einer Wahrscheinlichkeit aus, die der Anzahl der Vorkommen entspricht. Wenn die Gewinnwerte in den ersten drei Runden beispielsweise [2, 3, 2] sind, wählen Sie in Runde vier [2] mit p = 2/3 und [3] mit p = 1/3.


4

OneUpper

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

Die Bots aller anderen zielen entweder auf 1 oder zufällig, warum also nicht einfach auf 2?


4

Fließen wie Wasser

Vermeidet grundlegende Algorithmen zur Erkennung konstanter Bots, indem jede Zahl verdoppelt und langsam auf niedrigere Werte umgeschaltet wird, wenn sie nicht belegt sind.

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

Ich bin neugierig, hat dein Bot etwas mit meinem Brunnen zu tun ? Beide sind "wasserorientiert", haha.
RedClover

Mein ursprünglicher Plan war es, einen festen Ratgeber zu erstellen, der bestimmte Zahlen doppelt erraten hat, was meine Motivation für den Entscheidungsprozess des Bot war. Als ich es mir vorstellte, dachte ich an einen langsamen Strom, der den Namen inspirierte. Schreien Sie jedoch zum Wasserthema :)
TCFP

Das ist also der 3. oder 4. Platz (normalerweise der 3. Platz) in jedem Test, den ich durchführe. Das ist ziemlich erstaunlich für eine so einfache Strategie.
Robert Fraser

4

Total verloren

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

4

Der finale Countdown

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

Probieren Sie es online!

Gibt 10 für die ersten 100 Runden, 9 für die nächsten 100 usw. zurück.


4

Opportunitybot

Dieser Bot verfolgt in jeder Runde die niedrigste Zahl, die nicht von anderen Bots ausgewählt wurde (die niedrigste verfügbare Zahl oder Chance), und spielt die Zahl, die diese Zahl am häufigsten war.

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

4

PatterMatcher

Sucht nach sich wiederholenden Abschnitten in den Einsendungen der Bots, versucht dort Zahlen vorherzusagen und zu vermeiden.

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

Dreieck

Die Chance n zu wählen ist (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

Zeitgewichtet

Die Wahrscheinlichkeit, mit der ein Bot eine Zahl auswählt, ist proportional zu (10-n)*Δt. Die erste Runde ist identisch mit Dreieck.

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

Am wenigsten häufig

Sendet die am seltensten vorkommende Zahl, wenn sie gleich ist, nimmt die niedrigste.

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

Längste Zeit

Wie bei den Frequenzen, jedoch mit der längsten Zeit zwischen den Einsendungen.

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

Saboteur

Sendet die niedrigste Nummer, die zuletzt gesendet wurde.

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

Zweiter Saboteur

Sendet die zweitniedrigste Nummer, die zuletzt gesendet wurde

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

Sendet die niedrigste Nummer, die beim letzten Mal nicht gesendet wurde

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)

Es tut mir leid, dass ich ein bisschen mitgerissen wurde, als ich die Idee für neue Bots bekam, während ich die vorherige einmal implementierte. Ich war mir nicht sicher, welches das Beste sein würde und bin gespannt auf die Leistung jedes einzelnen. Sie finden sie alle hier: https://repl.it/@Fejfo/Lowest-Unique-Number


Nett. Sie können Saboteur so ändern, dass die letzte Auswahl ignoriert wird (es sei denn, dies ist beabsichtigt). Ich denke auch, dass Sie einige Sonderfälle behandeln müssen: Was sollte SecondSaboteur tun, wenn jeder Bot in einer Runde den gleichen Wert wählt, und was sollte Profiteur tun, wenn jeder Bot einen anderen Wert wählt? Möglicherweise benötigen Sie eine abschließende Klammer in Profiteur set(range(10).
Setzen Sie Monica

PatternMatcher scheint eine Art Endlosschleife oder eine Stelle zu haben, an der es hängen bleibt.
Robert Fraser

3

Der Top 50% RNG Bot

import random

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

Ich wollte gerade einen zufälligen Bot posten, aber hidefromkgb hat vor mir gepostet (durch das Posten machen sie sich ein einfaches Ziel für den KGB, kein guter Weg, sich zu verstecken). Dies ist meine erste KOTH-Antwort in der Hoffnung, den rng-Bot schlagen zu können.


3

Der Cycler

Dieser Bot durchläuft einfach jede der Zahlen in seinem Zug. Nur zum Spaß initialisiert es den Zähler mit seinem Index.

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

3

OneTrackMind

Dieser Bot wählt zufällig eine Zahl und bleibt 50 Runden lang dabei. Dann wählt er eine andere und wiederholt sie.

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)

3

Glückliche sieben

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

Ich fühle mich heute glücklich! Ich werfe alles auf 7 raus!


3

Meine Idee ist, dass die Strategie mehr von der Anzahl der Bots abhängt als von der tatsächlichen Bewertung der Strategien.

Bei einer erheblichen Anzahl von Bots stehen folgende Optionen zur Verfügung:

  • "Gierige" Roboter, die auf die unteren 1-3 Zahlen abzielen 10 Bots sind "schlau" und streben die unteren 1-3 Zahlen an. Am besten lassen Sie diese Bots einfach dazwischen stören.

  • "Intelligente" Roboter, die, sobald sie feststellen, dass 4 immer aufgehoben sind, woanders hingehen.

  • "Zufällige" und "konstante" Roboter. Hier gibt es nicht viel zu tun.

Also wette ich auf # 4.

class LazyStalker(object):
    def __init__(self, index):
        pass
    def select(self):
        return 4
    def update(self, choices):
        pass

2

Der unentbehrliche RNG-Bot

import secrets

class SecureRNG(object):
    def __init__(self, index):
        pass
    def select(self):
        return secrets.randbelow(10) + 1
    def update(self, choices):
        pass

2

Attentäter

Bleibt im Schatten, zielt dann auf die aktuell niedrigste Schätzung. Lauf.

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

2

FollowBot

Kopieren Sie den Gewinner aus der letzten Runde oder zumindest die beste Auswahl, wenn es keinen Gewinner gibt.

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

2

Psychadelic

Der einzige Weg, einen Atomkrieg zu gewinnen, besteht darin, sich wahnsinnig zu machen. Also werde ich jeden Predictive Bot im Turnier verrückt machen.

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

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

Nimmt die Auswahl der letzten Runde und wählt die niedrigste nicht ausgewählte Zahl (wobei die Auswahl von UnchosenBot natürlich ignoriert wird).


2

Whoop-di-Scoop-di-Poop

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

Ich habe Python noch nie gesehen oder berührt, ist das unpythonisch?


1
Fügen Sie die Zeile <!-- language: lang-python -->vor dem Codeblock hinzu, um die Syntaxhervorhebung zu aktivieren
Herman L

@HermanL Ich habe ein pythonTag zu der Frage halluziniert und dachte, es wäre automatisch, aber ich habe etwas Schlechtes geschrieben.
Mihail Malostanidis

1
Was pythonicity, ist der Code recht gut, außer es pythonicer zu sagen , in Betracht gezogen werden könnte others = [c for i, c in enumerate(choices) if i != self.index], oder, weil danach nur Sie diese Variable für die Mitgliedschaft Tests verwenden, { }anstatt [ ]eine konstruieren würde , setanstatt ein list.
Roman Odaisky

if (self.guess)ist auch sehr unpythonisch.
Jonathan Frech

Ich habe keine Ahnung, wie diese Eltern self.guessda reingekommen sind! Muss einer der Formatierer gewesen sein.
Mihail Malostanidis

2

Brunnen

Ein einfacher Bot wählt zuerst die niedrigste Zahl und wenn ein anderer Bot sie ebenfalls wählt, erhöht er den Zähler - der Boden wird gefüllt und das Wasser fließt nach unten. Wenn es 11 erreicht, startet es wieder auf 1 - das Wasser wird wieder nach oben gepumpt.

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

In der aktuellen Form bleibt Ihr Bot in der while-Schleife stecken, wenn die anderen Bots alle Zahlen von 1 bis 8 ausgewählt haben. Wollten Sie den Wert targetauf 10 setzen?
Emil

@Emil Richtig, es war ursprünglich so, geändert
RedClover

2

PoissonsBot

Wählen Sie Zahlen aus einer Poisson-Verteilung aus, die auf niedrigere Werte eingestellt ist. Passen Sie den Mittelwert der Verteilung nach oben an, wenn wir gleichauf sind, und nach unten, wenn unter uns Vermutungen liegen. Die Schrittgröße wird mit fortschreitendem Spiel immer kleiner.

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)

2

MinWin

Es wird eine fortlaufende Zählung der Gewinnwerte und der minimalen nicht ausgewählten Werte durchgeführt (wobei der minimale nicht ausgewählte Wert nur berücksichtigt wird, wenn er kleiner als der Gewinnwert ist). Es wählt zufällig zwischen diesen Gewinn- und Mindestwerten.

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)

2

Spielerzyklus

Wechselt durch die Spieler. Der aktuelle Spieler (könnte selbst sein) hat nun die Wahl dieses Bots. Beginnt mit dem Drucken von 8, weil warum nicht. Entschuldigung, ich kann nicht python, das ist wahrscheinlich schlechter Code.

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)]

Edit: Danke an Triggernometry für die Verbesserung meines Codes mit itertools


Ihr Code funktioniert einwandfrei, aber Sie können ein intertools.cycle () für a hinzufügen, damit es automatisch zwischen 0 und 9 wechselt und Sie keine Inkrementierung oder Prüfung durchführen müssen - Probieren Sie es online aus!
Triggernometrie

2

Waschbär

Wählen Sie die niedrigste Zahl, die in der vorherigen Runde nicht ausgewählt wurde, mit Ausnahme unserer eigenen vorherigen Auswahl, die dieses Mal erneut ausgewählt werden könnte. Wählen Sie in der ersten Runde 1 aus. (Bei 9 Gegnern und 10 Auswahlmöglichkeiten gibt es garantiert einen verfügbaren Wert.)

Das habe ich mir unabhängig ausgedacht, aber jetzt sehen wir mindestens zwei vorhergehende Bots, die im Wesentlichen gleich sind.

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))

Waschbär-Dreieck

Kombiniert Waschbär und Dreieck: Wählen Sie aus den nicht gewählten Werten einen aus, der auf der Wahrscheinlichkeit des umgekehrten Dreiecks basiert.

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)

Fehler:AttributeError: 'RaccoonTriangle' object has no attribute 'boundaries'
Renzeee

1
Ja Entschuldigung. Ich glaube, ich habe es behoben. Ich war mitten im Schreiben von Tests, als ich aufhörte.
Quantenmechaniker

1

Die allgemeine

Der General kämpft immer den letzten Krieg (n) .

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

1

No-Repeat Random

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 pickt zufällig, vermeidet jedoch, die gleiche Zahl zu wählen, die er in der vorherigen Runde gespielt hat.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.