最小の固有番号KoTH


27

最小の一意の番号を選択するボットを作成します。

(私が何年も前に聞いた心理実験に基づきますが、再び追跡することはできませんでした。)

ルール

  • 各ゲームは、1000ラウンドをプレイする10個のランダムに選択されたボットで構成されます。
  • 各ラウンドでは、すべてのボットが1〜10(両端を含む)の整数を選択します。同じ値を選択したボットは除外され、最小の値を持つ残りのボットはポイントを受け取ります。
  • ボットが一意の値を選択しない場合、ポイントは付与されません。
  • 1000ラウンドの終了時に、最もポイントの多いボット(または最もポイントの多いボットすべて)がゲームに勝ちます。
  • トーナメントは200 *(プレーヤー数)ゲーム続きます。
  • 勝率が最も高いボットがトーナメントに勝ちます。

仕様書

ボットはPython 3クラスでなければならず、2つのメソッドを実装する必要があります: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日金曜日の23:59:59 UTCです。トーナメントの提出は現在締め切られています。

結果

                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」の対戦相手のスコアを抑えます。

時間(より最近->より大きな重み)と選択値(より低いポイント->より大きな重み)の両方で重み付けされた重みを持つ進化確率を使用します。

少し複雑にするために、多少難読化されたコードを使用します。

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はどのくらいの重量を購入しますか?
ロマンオダイスキー

set([…for…in…])≡{…for…in…}、ところで
ロマンオダイスキー

@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

@ニーモニック問題ありません!正直なところ、これは私の最初のKoTHであり、Pythonでの最初の何かなので、最初の2つのポスターをたどっただけで、私が持っているはずの疑いにもかかわらず変更しませんでした。また、Lowballをテストに含めるのか、それとも投稿の単なる例に過ぎないのかもわかりませんでした。
エンジニアトースト

心配ない。KoTHの素晴らしい世界へようこそ!

2
あなたは「エース手ren弾」を持ちます: 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))

user2390246のコメントの実装:

では、これはどうですか?1から開始します。最初のラウンドの後、勝ち値を追跡し、発生回数に等しい確率でそれらからランダムに選びます。たとえば、最初の3ラウンドの勝ち値が[2、3、2]の場合、ラウンド4では、[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

水のような流れ

各数値を2倍にして、使用されていない場合はゆっくりと低い値に向かって進むことにより、基本的な一定のボット検出アルゴリズムを回避します。

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番目(通常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

オンラインでお試しください!

最初の100ラウンドで10、次の100ラウンドで9などを返します。


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

最小頻度

発生頻度が最も低い番号を送信します。等しい場合は、最も小さい番号を取得します。

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

前回送信された2番目に低い番号を送信します

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)

申し訳ありませんが、少し夢中になって、以前の1回の実装中に新しいボットのアイデアを得ました。どれがベストかはわかりませんでしたが、それぞれのパフォーマンスに興味があります。それらはすべてここで見つけることができます:https : //repl.it/@Fejfo/Lowest-Unique-Number


いいね Saboteurを変更して、最後の選択を無視することを検討してください(意図的な場合を除く)。また、いくつかの特別なケースを処理する必要があると思います:すべてのボットがあるラウンドで同じ値を選択した場合、SecondSaboteurは何をすべきか、すべてのボットが異なる値を選択した場合、Profiturは何をすべきか?後にプロフィテュールで終了括弧が必要になる場合があり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

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

フォローボット

最終ラウンドの勝者をコピーします。勝者がいない場合は、少なくとも最小の結ばれた選択をコピーします。

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

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

フープ・ディ・スクープ・ディ・プープ

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を見たことも触れたこともありません。


1
<!-- language: lang-python -->コードブロックの前に行を追加して、構文の強調表示を有効にします
Herman L

@HermanL私pythonは質問のタグを幻覚にし、それは自動的になると思ったが、何か悪いことを書いた。
ミハイルマロスタニディス

1
それは言うことpythonicer考えられるかもしれない以外pythonicityについては、コードは、かなり良いですothers = [c for i, c in enumerate(choices) if i != self.index]、または、その後にあなただけのメンバーシップのテストのためにその変数を使用して、ため{ }ではなく、[ ]構築しまうsetのではなくをlist
ロマンオダイスキー

if (self.guess)また、非常に素朴です。
ジョナサンフレッチ

周りのこれらの括弧がどのようself.guessにそこに入ったのか分かりません!フォーマッターの1つである必要があります。
ミハイルマロスタニディス

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 に設定するつもりでしたか?
エミール

@エミル本当、それは元々このようなものでしたが、変更されました
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

ミンウィン

勝ち値と最小未選択値の実行カウントを保持します(最小未選択値は、勝ち値よりも小さい場合にのみ考慮されます)。これらの勝ち値と最小値の中からランダムに選択します。

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

編集:itertoolsでコードを改善してくれたTriggernometryに感謝します


コードは正常に機能しますが、intertools.cycle()を追加して、0-9を自動的に循環させ、増分やチェックを行う必要がないようにします。オンラインで試してください!
トリガー測定

2

ラクーン

前回のラウンドで選択されていない最小の番号を選択します。ただし、今回は再度選択できる以前の選択を除きます。最初のラウンドでは、1を選択します(9人の対戦相手と10の選択肢がある場合、1つの使用可能な値が保証されます)。

私はこれを独自に思いつきましたが、今では本質的に同じ少なくとも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))

アライグマの三角形

アライグマと三角形の組み合わせ:選択されていない値から、逆三角形の確率に基づいて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

ボットはランダムに選択しますが、前のラウンドと同じ番号を選択することは避けます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.