最小唯一数KoTH


27

创建一个机器人以选择最小的唯一编号。

(基于我多年前听说的一项心理学实验,但无法再次进行追踪。)

规则

  • 每个游戏将由10个随机选择的机器人组成,每个机器人进行1000回合。
  • 每回合,所有漫游器都会从1到10(包括10)之间选择一个整数。选择相同值的所有漫游器都将被排除,剩下的拥有最小值的漫游器将获得一个分数。
  • 如果没有机器人选择唯一的值,则不会获得任何积分。
  • 在1000回合结束时,得分最高的机器人(或所有得分最高的机器人)将赢得比赛。
  • 比赛将持续200 *(玩家人数)游戏。
  • 获胜百分比最高的机器人将赢得比赛。

技术指标

Bots必须是Python 3类,并且必须实现两个方法:selectupdate
机器人将使用索引构建。
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]以外的值等)的漫游器都将被取消比赛资格,并且没有它们的比赛将重新进行。
  • 没有回合的时间限制,但是如果机器人花费的时间太长,我可以实施一个回合。
  • 每个用户的提交数量没有限制。
  • 提交截止日期为9月28日(星期五)UTC时间23:59:59

结果

                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, ...]

Answers:


10

贝叶斯

尝试使用简单的统计模型做出最佳选择。

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将购买多少重量?
Roman Odaisky

set([…for…in…])≡{…for…in…},顺便说一句
Roman Odaisky

@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
hidefromkgb

@Mnemonic没问题!老实说,这是我的第一个KoTH,也是我在Python中的第一个东西,因此我只是跟踪了前两个海报,尽管怀疑我应该这样做,但并没有对其进行更改。我也不确定您是否要在测试中包含Lowball,或者它实际上只是该帖子的一个示例。
工程师敬酒

别担心。欢迎来到KoTH的美好世界!

2
:您thew的“王牌手榴弹” puzzling.stackexchange.com/questions/45299/...
凯恩

4

历史记录

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

实现user2390246的评论:

那呢?从1开始。在第一轮之后,跟踪获胜的值,并以等于发生次数的概率从中随机选取。例如,如果前三轮的获胜值为[2,3,2],则在第四轮中,选择[2](p = 2/3)和[3](p = 1/3)。


4

上层

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

每个人的机器人都针对1或随机,那么为什么不仅仅针对2?


4

像水一样流动

通过将每个数字加倍来避免基本的恒定bot检测算法,如果不占用它们,则缓慢地朝较低的值前进。

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

我很好奇,您的漫游器与我的Fountain有某种联系吗?两者都是“面向水的”,哈哈。
RedClover

老实说,我的最初计划是制造一个固定猜测机器人,使某些数字加倍猜测,这是我进行机器人决策过程的动力。当我看到它时,我想到的是一条缓慢移动的溪流,这启发了这个名字。虽然喊出了水的主题:)
TCFP

因此,在我进行的每项测试中,这都获得第三或第四(通常是第三)。这样简单的策略真是太神奇了。
罗伯特·弗雷泽

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

在线尝试!

前100轮返回10,下100轮返回9,依此类推。


4

机会机器人

该漫游器会跟踪每轮没有其他漫游器选择的最低号码(最低可用号码或机会),并最频繁地播放一直是该号码的号码。

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

我本来打算发布一个随机漫游器,但是hidefromkgb在我之前发布了(通过发布它们使自己成为KGB的简单目标,而不是隐藏的好方法)。这是我的第一个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

一个劲

该机器人会随机选择一个数字并坚持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

取消选择

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

呜呜呜呜呜

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

便便

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,这是非Python的吗?


1
<!-- language: lang-python -->在代码块之前添加一行以启用语法突出显示
Herman L

@HermanL我python在问题上使标签变幻了,以为它将是自动的,但我写了一些不好的东西。
Mihail Malostanidis

1
至于pythonicity,代码非常好,除了它可能被认为是pythonic others = [c for i, c in enumerate(choices) if i != self.index],或者因为随后您仅将该变量用于成员资格测试,{ }而不是[ ]构造a set而不是a list
Roman Odaisky

if (self.guess)也是非常不可思议的
乔纳森·弗雷希

我不知道周围那些人是怎么self.guess进入那里的!一定是格式化者之一。
Mihail Malostanidis

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

按照目前的形式,如果其他漫游器选择了从1到8的所有数字,您的漫游器将陷入while循环中。您是要设置target为10吗?
埃米尔(Emil)

@Emil True,它原来是这样的,改变了
RedClover

2

泊松

从Poisson分布中选择偏低的数字。如果我们处于平局,则向上调整分布的平均参数;如果我们下方有猜测,则向下调整。随着游戏的进行,步长逐渐变小。

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

敏运

保持中奖值和最小未选中值的连续计数(其中,只有当最小未选中值小于中奖值时才考虑最小未选中值)。它从这些获胜和最小值中随机选择。

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

玩家循环

在玩家之间循环。当前玩家(可能是自己)的选择现在是该机器人的选择。开始打印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之间循环,而您不必进行增量或检查- 在线尝试!
Triggernometry '18年

2

浣熊

选择上一轮未选择的最低号码,但我们自己的先前选择除外,这一次可以再次选择。在第一轮中,选择1。(给9个对手和10个选择,保证有一个可用值。)

我独立提出了这个建议,但现在至少看到了两个以前基本相同的机器人。

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

一般

将军始终战斗在最后一战(S)

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

Bot随机选择,但避免选择与上一轮相同的数字。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.