Самый маленький уникальный номер 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
@ Мнемоник Есть новости?
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

WaitWhatBot

Не самый конкурентоспособный бот и, определенно, не 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
@ Мнемоник, технически это не обман, так как он не инициализируется self.index.
hidefromkgb

@ Мнемоник Нет проблем! Честно говоря, это мой первый 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] с p = 2/3 и [3] с 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

Все остальные боты либо стремятся к 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

Честно говоря, мой первоначальный план состоял в том, чтобы сделать бота с фиксированным предположением, который бы угадал определенные числа, что было моей мотивацией для процесса принятия решения бота. Когда я визуализировал это, я думал о медленно движущемся потоке, который вдохновил название.
Приветствую

Так что это становится 3-м или 4-м (обычно 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

TimeWeighted

Вероятность, что бот выберет число, пропорциональна (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

LeastFrequent

Представляет наименее часто встречающееся число, если они равны, берут самое низкое.

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

SecondSaboteur

Представляет второй самый низкий номер, который был представлен в последний раз

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

Представляет наименьшее число, не представленное в прошлый раз

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

Я собирался опубликовать случайного бота, но hidefromkgb разместил до меня (публикуя, что они делают себя легкой целью для КГБ, а не хорошим способом скрыться). Это мой первый ответ на KOTH, просто в надежде победить rng бота.


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

Psychadelic

Единственный способ выиграть ядерную войну - сделать себя безумным. Поэтому я собираюсь сделать каждого прогнозирующего бота на турнире безумным.

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

Принимает выбор последнего раунда и выбирает наименьшее число без выбора (конечно, игнорируя выбор UnchosenBot).


2

Whoop-ди-совок-ди-какашки

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

Полуют-ди-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а не 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. Вы хотели установить targetзначение 10?
Эмиль

@Emil Правда, изначально это было так, изменилось
RedClover

2

PoissonsBot

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

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

Енот Треугольник

Объединяет Raccoon и Triangle: из не выбранных значений выберите одно на основе вероятности обратного треугольника.

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'
Рензее

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.