Найменше унікальне число KoTH


27

Створіть бота, щоб вибрати найменший унікальний номер.

(На основі психологічного експерименту, про який я чув багато років тому, але його не вдалося віднайти знову.)

Правила

  • Кожна гра буде складатися з 10 випадково вибраних ботів, що грають 1000 раундів.
  • Кожен раунд усі боти вибирають ціле число від 1 до 10 (включно). Будь-які боти, які виберуть однакове значення, будуть виключені, а решта бота з найменшим значенням отримає бал.
  • У випадку, якщо жоден бот не вибере унікальне значення, бали не присвоюються.
  • В кінці 1000 раундів виграє бот з найбільшою кількістю очок (або всіх ботів, пов'язаних з найбільшою кількістю очок).
  • Турнір триватиме 200 * (кількість гравців) ігор.
  • Бот з найвищим відсотком виграшів виграє турнір.

Технічні умови

Боти повинні бути класами Python 3 і повинні реалізовувати два методи: selectі update.
Боти будуватимуться з індексом.
selectне передається аргументів і повертає вибір бота для поточного раунду.
updateпередається список варіантів, зроблених кожним ботом у попередньому раунді.

Приклад

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

Контролер

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

Додаткова інформація

  • Жоден бот не буде грати в гру проти себе.
  • У випадку, якщо бот включений у менше ніж 100 ігор, турнір буде повторно.
  • Боти можуть зберігати стан між раундами, але не між іграми.
  • Доступ до контролера чи інших ботів заборонено.
  • Кількість ігор та кількість раундів у грі можуть збільшуватися, якщо результати занадто різні.
  • Будь-які боти, які викликають помилки або дають недійсні відповіді (не-введення, значення поза [1, 10] тощо), будуть дискваліфіковані, і турнір буде повторено без них.
  • Немає обмеження в часі для раундів, але я можу реалізувати його, якщо ботам потрібно буде занадто багато часу, щоб думати.
  • Немає обмежень у кількості подань на кожного користувача.
  • Кінцевий термін подання заявок - 23:59:59 UTC в п’ятницю, 28 вересня. Турнір зараз закритий для подання заявок.

Результати

                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 Будь-які новини?
user1502040

4
@Herohtar Я встановив, що він працює перед тим, як піти на роботу. При будь-якій удачі це слід зробити, коли я повернуся додому.

1
@Mnemonic Чи закінчилось це ще?
user1502040

2
@Justin Він працює зараз і, здається, не виходить з ладу, але я точно не заперечував би за допомогу, якщо цей запуск не вдасться.

1
@MihailMalostanidis Створіть файл, викликаний bots.pyу тій самій директорії, що містить усі боти. Наприкінці створіть список конструкторів:allBotConstructors = [Lowball, BayesBot, ...]

Відповіді:


10

BayesBot

Намагається зробити оптимальний вибір, використовуючи просту статистичну модель.

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

Уникайте постійних ботів

Слідкуйте за тим, які боти завжди повертали однакове значення, і пропускайте ці значення. З решти значень виберіть їх випадковим чином, але суттєво упередженими до нижчих.

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

Зачекайте, що

Не найконкурентніший бот і, безумовно, не GTO , але загрожує забиття будь-якого суперника "завжди 1" або "майже завжди 1" в тій же грі, що і в такому сценарії. WaitWhatBot теж стає таким ботом.

Використовує ймовірності, що розвиваються, із зваженими вагами як за часом (більш недавній -> більша вага), так і значенням вибору (нижня точка -> більша вага).

Використовує дещо затуманений код для трохи хихикання.

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
Скільки б вага придбав WaitWhatBot, якби WaitWhatBot придбав би вагу?
Роман Одайський

встановити ([… за… в…]) ≡ {… за… в…}, до речі
Роман Одайський

@RomanOdaisky я насправді порадив когось із цього дня просто для гольфу!
Джонатан Аллан

5

Сталкер

На початку гри цей бот випадковим чином вибирає конкретний індекс як свою ціль. Потім він перетворює цілі на всю гру, копіюючи номер, який вона обрала в попередньому раунді.

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

Дурний жадібний

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

Цей бот передбачає, що інші боти не хочуть зв'язати.

Я усвідомлюю, що це те саме, що і приклад, але я думав, перш ніж читати так далеко. Якщо це не суперечить тому, як виконуються виклики KoTH, дайте мені знати.


Взагалі я вважаю за краще не мати дублікатів ботів, але не проти залишати це.

1
@Mnemonic добре технічно це не дур, оскільки це не ініціалізується self.index.
hidfromkgb

@Mnemonic Немає проблем! Чесно кажучи, це мій перший KoTH і мій перший що-небудь на Python, тому я просто дотримувався перших двох плакатів і не міняв його, незважаючи на мою підозру, що я повинен був. Я також не був впевнений, чи збираєтесь ви включити Lowball у свої тести чи це був справді лише приклад для посади.
Інженер Тост

Не хвилюйтесь. Ласкаво просимо у чудовий світ KoTH!

2
Ви танув в «аса гранату»: 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))

Реалізація коментаря користувача2390246:

Що з цим тоді? Почніть з 1. Після першого раунду слідкуйте за виграшними значеннями та вибирайте їх випадковим чином з вірогідністю, що дорівнює кількості подій. Наприклад, якщо виграшні значення у перших трьох раундах [2, 3, 2], то у четвертому раунді виберіть [2] з р = 2/3 та [3] з р = 1/3.


4

OneUpper

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

Всі інші боти націлені на 1 або випадкові, то чому б не просто націлитись на 2?


4

Тече, як вода

Уникайте основних алгоритмів виявлення постійних ботів шляхом подвоєння на кожне число, повільно просуваючись до нижчих значень, якщо вони незайняті.

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

Мені цікаво, чи пов’язаний ваш бот якось із моїм Фонтаном ? Обидва "орієнтовані на воду", ха-ха.
RedClover

Чесно кажучи, мій початковий план полягав у тому, щоб створити фіксований бот, який би вдвічі здогадувався про певні числа, що було моєю мотивацією для процесу прийняття рішення. Коли я його візуалізував, я думав про повільний рухомий потік, який надихнув назву. Хоч кричати на тему води :)
TCFP

Отже, це стає третім чи четвертим (зазвичай 3-м) у кожному тесті, який я проводжу. Це досить дивовижно для такої простої стратегії.
Роберт Фрейзер

4

Повністю загублений

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

Фінальний відлік

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

Спробуйте в Інтернеті!

Повертає 10 за перші 100 раундів, 9 для наступних 100 і так далі.


4

Opportunitybot

Цей бот відслідковує найменше число, яке не обирають будь-які інші боти в кожному раунді (найменше доступне число чи можливість) та відтворює число, яке найчастіше було цим номером.

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

Шукає повторення розділів у поданнях ботів, намагається передбачити та уникати чисел.

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

Трикутник

Шанс вибору n є (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

Зважений часом

Ймовірність того, що бот обирає число, пропорційна (10-n)*Δt. Перший раунд це ідентичний трикутнику.

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

Найменш часто

Подайте найменш часто зустрічається число, якщо вони рівні, візьміть найменше.

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

Найдовший час

Те саме, що частота відвідувань, але найдовший час між поданнями.

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

Диверсант

Подає найменшу кількість, яка була подана останній раз.

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

Другий диверсант

Подає друге найменше число, яке було подано останній раз

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

Профітер

Подає найменше число, не подане останній раз

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)

Вибачте, що я трохи захопився, отримуючи ідею для нових ботів під час впровадження попереднього. Я не був впевнений, хто з них найкращий, і мені цікаво про виконання кожного з них. Ви можете знайти їх усіх тут: https://repl.it/@Fejfo/Lowest-Unique-Number


Приємно. Ви можете розглянути можливість зміни Saboteur для ігнорування його останнього вибору (якщо це не навмисно). Крім того, я думаю, вам може знадобитися вирішити деякі особливі випадки: що робити SecondSaboteur, якщо кожен бот вибирає однакове значення в якомусь раунді, і що повинен робити Profiteur, якщо кожен бот обирає інше значення? Можливо, вам знадобиться кінцева дужка в Profiteur після set(range(10).
Відновіть Моніку

Схоже, що у PatternMatcher є якась нескінченна петля або місце, де вона застрягає.
Роберт Фрейзер

3

Топ 50% RNG бота

import random

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

Я збирався опублікувати випадкового бота, але hidfromkgb розмістив переді мною (публікуючи, що вони роблять себе легкою ціллю для КДБ, не гарним способом приховувати). Це моя перша відповідь KOTH, просто сподіваючись перемогти бота.


3

Цикл

Цей бот просто перебирає кожне з чисел по черзі. Для задоволення він ініціалізує лічильник своїм індексом.

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

Цей бот випадковим чином вибирає номер і тримається з ним протягом 50 раундів, потім вибирає інший і повторює.

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

Пощастило сім

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

Мені сьогодні щастить! Я викидаю все на 7!


3

Моя ідея полягає в тому, що стратегія більше залежить від кількості ботів, ніж від фактичної оцінки стратегій.

З великою кількістю ботів можливі варіанти:

  • "Жадібні" роботи, спрямовані на те, щоб нижчі 1-3 числа 10 ботів були "розумними" і прагнули отримати нижчі 1-3 числа, найкраще - просто дозволити тим ботам втручатися між ними.

  • "Розумні" роботи, коли вони зрозуміють, що 4 завжди підхоплені, поїдуть в інше місце.

  • "Випадкові" та "постійні" роботи. Тут мало що робити.

Отже, я ставлю на №4.

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

2

Найважливіший RNG-бот

import secrets

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

2

Вбивця

Залишається в тіні, потім націлений на нинішню здогадку. Біжи.

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

Скопіюйте переможця з останнього раунду або, принаймні, найкращого відбору, мінімально зв'язаного, якщо переможця не було.

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

Психаделічний

Єдиний спосіб виграти ядерну війну - це зробити божевільним. Тож я збираюся зробити кожного прогнозного бота на турнірі божевільним.

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

UnsensenBot

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

Обирає вибір в останньому раунді і вибирає найнижчу незрешенну кількість (звичайно, ігноруючи вибір UnchosenBot).


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

Я ніколи не бачив і не торкався до Python, це непіфонічно?


1
Додайте рядок <!-- language: lang-python -->перед блоком коду, щоб увімкнути підсвічування синтаксису
Herman L

@HermanL Я галюцинізував pythonтег на запитання і подумав, що це буде автоматично, але я написав щось погане.
Михайло Малостанідіс

1
Що стосується pythonicity, код є досить хорошим, за винятком того, що можна вважати pythonicer others = [c for i, c in enumerate(choices) if i != self.index], або, оскільки згодом ви використовуєте цю змінну лише для тестів на членство, { }а не [ ]будували б, setа не a list.
Роман Одайський

if (self.guess)також є дуже непітонічним.
Джонатан Фрех

Я поняття не маю, як ті парені self.guessпотрапили туди! Повинно бути одним із форматів.
Михайло Малостанідіс

2

Фонтан

Простий бот, підбирає спочатку найменше число, і якщо будь-який інший бот теж вибере його, він збільшить лічильник - підлога заповнюється і вода стікає вниз. Коли вона досягає 11, вона перезавантажується до 1 - вода відкачується назад до верху.

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

У поточній формі ваш бот застрягне в циклі while, якщо інші боти обрали всі числа від 1 до 8. Ви мали намір встановити target10?
Еміль

@Emil Щоправда, спочатку так було, змінили
RedClover

2

ОтрутиБот

Виберіть числа з розподілу Пуассона, який ухиляється до менших значень. Відрегулюйте середній параметр розподілу вгору, якщо ми перебуваємо внизу і внизу, якщо під нами є здогадки. Розмір кроку стає поступово меншим за час гри.

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

Зберігає підрахунок переможних значень та мінімальних невибраних значень (де мінімальне невибране значення враховується лише у тому випадку, якщо воно менше переможного значення). Він випадковим чином вибирає серед цих виграшних і мінімальних значень.

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

PlayerCycle

Цикли через плеєри. Вибір поточного гравця (міг бути сам) - це вибір цього бота. Починає друкувати 8, бо чому б ні. Вибачте, що не можу python, це, мабуть, поганий код.

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

Редагувати: Завдяки Triggernometry за вдосконалення коду за допомогою itertools


Ваш код працює чудово, але ви можете додати intertools.cycle () для того, щоб він автоматично проходив через 0-9, і вам не доведеться робити збільшення чи перевірку - Спробуйте в Інтернеті!
Тригернометрія

2

Єнот

Виберіть найменше число, яке не було обрано в попередньому раунді, за винятком нашого попереднього вибору, який цього разу можна було вибрати знову. У першому раунді виберіть 1. (Враховуючи 9 супротивників та 10 варіантів, гарантовано буде одне доступне значення.)

Я придумав це самостійно, але тепер побачу принаймні 2 попередніх бота, які по суті однакові.

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

Трикутник єнота

Поєднує єнот і трикутник: з незбережених значень виберіть одне на основі ймовірності зворотного трикутника.

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)

Помилка:AttributeError: 'RaccoonTriangle' object has no attribute 'boundaries'
Renzeee

1
Так, вибачте. Я думаю, що я це виправив. Я був у середині написання тестів, коли я перестав.
Квантовий механік

1

Генерал

Генеральний завжди бореться за останню війну (и) .

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

Не повторення випадкових

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

Бот вибирає випадковим чином, але уникає вибору того ж числа, що і попередній раунд.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.