Yinelenen Mahkum Trilemi


19

SORUN DURUMU: AÇIK

Yorum yapın, bir PR açın veya botunuzu kaçırırsam başka türlü bağırmayın.


Mahkum ikilemi ... üç seçenekle. Deli, ha?

İşte ödeme matrisimiz. Oyuncu A solda, B üstte

A,B| C | N | D
---|---|---|---
 C |3,3|4,1|0,5
 N |1,4|2,2|3,2
 D |5,0|2,3|1,1

Kazanç matrisi, her iki oyuncunun da işbirliği yapması için en iyisi olacak şekilde tasarlanmıştır, ancak Nötr veya Kusur'u seçerek (genellikle) kazanabilirsiniz.

İşte bazı (rakip) örnek botlar.

# turns out if you don't actually have to implement __init__(). TIL!

class AllC:
    def round(self, _): return "C"
class AllN:
    def round(self, _): return "N"
class AllD:
    def round(self, _): return "D"
class RandomBot:
    def round(self, _): return random.choice(["C", "N", "D"])

# Actually using an identically-behaving "FastGrudger".
class Grudger:
    def __init__(self):
        self.history = []
    def round(self, last):
        if(last):
            self.history.append(last)
            if(self.history.count("D") > 0):
                return "D"
        return "C"

class TitForTat:
    def round(self, last):
        if(last == "D"):
            return "D"
        return "C"

Botunuz bir Python3 sınıfıdır. Her oyun için yeni bir örnek oluşturulur ve round()her turda, rakibinizin son turdan seçtiği (veya ilk tur ise Hiçbiri) olarak adlandırılır.

Bir ay içinde kazanan için 50 rep ödülü var.

Ayrıntılı Bilgiler

  • Her bot kendisi dahil olmak üzere diğer botları (1v1) [REDACTED] turda oynatır.
  • Standart boşluklara izin verilmedi.
  • Sınıfınızın dışında veya diğer el yapımı shenanigainlerle uğraşmak yok.
  • En fazla beş bot gönderebilirsiniz.
  • Evet, el sıkışma uygulayabilirsiniz.
  • Dışındaki herhangi bir yanıt C, Nya da Dsessizce alınacaktır N.
  • Oynadıkları her oyundaki her botun puanları toplanacak ve karşılaştırılacaktır.

kontrolör

Kontrol!

Diğer diller

Herkesin ihtiyacı varsa bir API'yı bir araya getireceğim.

Puanlar: 2018-11-27

27 bots, 729 games.

name            | avg. score/round
----------------|-------------------
PatternFinder   | 3.152
DirichletDice2  | 3.019
EvaluaterBot    | 2.971
Ensemble        | 2.800
DirichletDice   | 2.763
Shifting        | 2.737
FastGrudger     | 2.632
Nash2           | 2.574
HistoricAverage | 2.552
LastOptimalBot  | 2.532
Number6         | 2.531
HandshakeBot    | 2.458
OldTitForTat    | 2.411
WeightedAverage | 2.403
TitForTat       | 2.328
AllD            | 2.272
Tetragram       | 2.256
Nash            | 2.193
Jade            | 2.186
Useless         | 2.140
RandomBot       | 2.018
CopyCat         | 1.902
TatForTit       | 1.891
NeverCOOP       | 1.710
AllC            | 1.565
AllN            | 1.446
Kevin           | 1.322

1
Botlar birbirine nasıl konur? Grudger'dan her zaman birbirlerine karşı iki bot var ve düşmanın son tercihi bot'a geçti. Kaç tur oynanır? Ve bir oyun için: Sadece sonuç önemlidir (yani kim kazandı) ya da puanları?
Black Owl Kai

1
Bu dili agnostik veya en azından daha geniş yaparsanız daha fazla giriş alırsınız. Bir işlemi başlatan ve metin yanıtlarını geri almak için metin komutları gönderen bir sarıcı python sınıfınız olabilir.
Sparr

1
Bitti. Bu kum havuzunda bir ay kadar oldu!
SIGSTACKFAULT

2
İçinde main.py en sarın Eğer while len(botlist) > 1:ile botlist.remove(lowest_scoring_bot)döngü altındaki, ilginç sonuçlarla bir eleme turnuvası olsun.
Sparr

1
Bu bir günün başka bir sürümü, yalnızca son hamle yerine tüm etkileşim geçmişini geçebilir. Kullanıcı kodunu biraz basitleştirmesine rağmen çok fazla değişmez. Ama zamanla açıklığa kavuşturan gürültülü iletişim kanalları gibi uzantılara izin verirdi: "Gerçekten, D, üst üste dört kez C dememe rağmen? Hayır, D demedim; beni ne alıyorsun Ah, pardon, o turu unutabilir miyiz? "
Scott Sauyet

Yanıtlar:


10

EvaluaterBot

class EvaluaterBot:
    def __init__(self):
        self.c2i = {"C":0, "N":1, "D":2}
        self.i2c = {0:"C", 1:"N", 2:"D"}
        self.history = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        self.last = [None, None]

    def round(self, last):
        if self.last[0] == None:
            ret = 2
        else:
            # Input the latest enemy action (the reaction to my action 2 rounds ago)
            # into the history
            self.history[self.last[0]][self.c2i[last]] += 1
            # The enemy will react to the last action I did
            prediction,_ = max(enumerate(self.history[self.last[1]]), key=lambda l:l[1])
            ret = (prediction - 1) % 3
        self.last = [self.last[1], ret]
        return self.i2c[ret]

Rastgele bot haricinde (belki de) daha önce gönderilen tüm botlara karşı kazanır (ancak bir avantajı olabilir, çünkü D'yi bir beraberlikte alır ve D optimal olmalıdır) ve kendilerine karşı sürekli bir beraberlik oynar.


Evet, her şeyi yener.
SIGSTACKFAULT

Scratch, PatternFinder onu biraz yener.
SIGSTACKFAULT

7

Nash dengesi

Bu bot üniversitede bir oyun teorisi dersi aldı, ancak tembeldi ve yinelenen oyunları kapsadığı sınıfa gitmedi. Bu yüzden sadece tek oyun karışık nash dengesini oynuyor. Anlaşılan 1/5 2/5 2/5, getiriler için karışık NE'dir.

class NashEquilibrium:
    def round(self, _):
        a = random.random()
        if a <= 0.2:
            return "C"
        elif a <= 0.6:
            return "N"
        else:
            return "D" 

Sürekli İstismar Nash Dengesi

Bu bot tembel kardeşinden bir ya da iki ders aldı. Tembel kardeşinin sorunu, sabit stratejilerden faydalanmamasıydı. Bu versiyon, rakibin sabit bir oyuncu mu yoksa titfortat mı olduğunu kontrol eder ve buna göre oynar, aksi takdirde normal nash dengesini oynar.

Tek dezavantajı, kendisine karşı oynayan tur başına ortalama 2.2 puan olmasıdır.

class NashEquilibrium2:

    def __init__(self):
        self.opphistory = [None, None, None]
        self.titfortatcounter = 0
        self.titfortatflag = 0
        self.mylast = "C"
        self.constantflag = 0
        self.myret = "C"

    def round(self, last):
        self.opphistory.pop(0)
        self.opphistory.append(last)

        # check if its a constant bot, if so exploit
        if self.opphistory.count(self.opphistory[0]) == 3:
            self.constantflag = 1
            if last == "C":
                 self.myret = "D"
            elif last == "N":
                 self.myret = "C"
            elif last == "D":
                 self.myret = "N"

        # check if its a titfortat bot, if so exploit
        # give it 2 chances to see if its titfortat as it might happen randomly
        if self.mylast == "D" and last == "D":
            self.titfortatcounter = self.titfortatcounter + 1

        if self.mylast == "D" and last!= "D":
            self.titfortatcounter = 0

        if self.titfortatcounter >= 3:
            self.titfortatflag = 1

        if self.titfortatflag == 1:
            if last == "C":
                 self.myret = "D"
            elif last == "D":
                 self.myret = "N"    
            elif last == "N":
                # tit for tat doesn't return N, we made a mistake somewhere
                 self.titfortatflag = 0
                 self.titfortatcounter = 0

        # else play the single game nash equilibrium
        if self.constantflag == 0 and self.titfortatflag == 0:
            a = random.random()
            if a <= 0.2:
                self.myret = "C"
            elif a <= 0.6:
                self.myret = "N"
            else:
                self.myret = "D"


        self.mylast = self.myret
        return self.myret

1
NashEquilibrium.round, beklenen işlev prototipine uyacak şekilde kullanılmasa bile argümanlar almalıdır.
Ray

Teşekkür ederim düzeltti
Ofya

Biraz daha kısa:class NashEquilibrium: def round(self, _): a = random.random() for k, v in [(0.2, "C"), (0.6, "N"), (1, "D")]: if a <= k: return v
Robert Grant

7

TatForTit

class TatForTit:
    def round(self, last):
        if(last == "C"):
            return "N"
        return "D"

Eğer ödeme matrisini doğru okuduysam, TitForTat CDCD'yi değiştirirken bu bot DNDN'yi seçerek tur başına ortalama net 3 puan kazanacak. Bu TitForTat karşı en uygun olabilir düşünüyorum. Açıkçası, bir TFT üyesi olmayan rakibi tespit etmek ve diğer stratejileri benimsemek geliştirilebilir, ancak sadece orijinal ödülü hedefliyordum.


6

PatternFinder

class PatternFinder:
    def __init__(self):
        import collections
        self.size = 10
        self.moves = [None]
        self.other = []
        self.patterns = collections.defaultdict(list)
        self.counter_moves = {"C":"D", "N":"C", "D":"N"}
        self.initial_move = "D"
        self.pattern_length_exponent = 1
        self.pattern_age_exponent = 1
        self.debug = False
    def round(self, last):
        self.other.append(last)
        best_pattern_match = None
        best_pattern_score = None
        best_pattern_response = None
        self.debug and print("match so far:",tuple(zip(self.moves,self.other)))
        for turn in range(max(0,len(self.moves)-self.size),len(self.moves)):
            # record patterns ending with the move that just happened
            pattern_full = tuple(zip(self.moves[turn:],self.other[turn:]))
            if len(pattern_full) > 1:
                pattern_trunc = pattern_full[:-1]
                pattern_trunc_result = pattern_full[-1][1]
                self.patterns[pattern_trunc].append([pattern_trunc_result,len(self.moves)-1])
            if pattern_full in self.patterns:
                # we've seen this pattern at least once before
                self.debug and print("I've seen",pattern_full,"before:",self.patterns[pattern_full])
                for [response,turn_num] in self.patterns[pattern_full]:
                    score = len(pattern_full) ** self.pattern_length_exponent / (len(self.moves) - turn_num) ** self.pattern_age_exponent
                    if best_pattern_score == None or score > best_pattern_score:
                        best_pattern_match = pattern_full
                        best_pattern_score = score
                        best_pattern_response = response
                    # this could be much smarter about aggregating previous responses
        if best_pattern_response:
            move = self.counter_moves[best_pattern_response]
        else:
            # fall back to playing nice
            move = "C"
        self.moves.append(move)
        self.debug and print("I choose",move)
        return move

Bu bot, rakibin bu olaylara nasıl tepki verdiğini görmek için son oyun durumunun önceki tekrarlarını arar, daha uzun kalıp maçları ve daha yeni maçlar tercih edilir, ardından rakibin tahmin edilen hamlesini "yenecek" hamle oynanır. İzlediği tüm verilerle daha akıllı olması için çok yer var, ama üzerinde çalışmak için zamanım kalmadı.


Zaman geldiğinde, ona bir optimizasyon geçişi vermek ister misiniz? Kolayca en büyük zaman alıcısı.
SIGSTACKFAULT

2
@Blacksilver Maksimum desen uzunluğunu 100'den 10'a düşürdüm. <200 mermi çalıştırıyorsanız neredeyse anında çalışmalıdır
Sparr

1
Belki yüksek oranda kompozit bir sayı kullanmak (yani, 12) daha iyi bir puan verir mi?
SIGSTACKFAULT

5

yeşim taşı

class Jade:
    def __init__(self):
        self.dRate = 0.001
        self.nRate = 0.003

    def round(self, last):
        if last == 'D':
            self.dRate *= 1.1
            self.nRate *= 1.2
        elif last == 'N':
            self.dRate *= 1.03
            self.nRate *= 1.05
        self.dRate = min(self.dRate, 1)
        self.nRate = min(self.nRate, 1)

        x = random.random()
        if x > (1 - self.dRate):
            return 'D'
        elif x > (1 - self.nRate):
            return 'N'
        else:
            return 'C'

İyimser başlar, ancak rakip işbirliği yapmayı reddettikçe giderek daha acı olur. Muhtemelen ayarlanabilecek bir sürü sihirli sabit, ancak bu muhtemelen zamanı haklı çıkarmak için yeterince iyi olmayacaktır.


5

topluluk

Bu, ilgili modellerden oluşan bir topluluğu çalıştırır. Bireysel modeller farklı miktarlarda geçmiş düşünür ve her zaman beklenen ödeme farkını optimize edecek hareketi seçebilir veya beklenen ödeme farkıyla orantılı olarak bir hamle seçebilir.

Topluluğun her üyesi daha sonra tercih ettikleri hamleye oy verir. Rakibinden ne kadar kazandıklarına eşit sayıda oy alırlar (bu, korkunç modellerin olumsuz oy alacağı anlamına gelir). Hangi hamleyi kazanırsa, oylama seçilir.

(Muhtemelen oylarını hamleler arasında her birini ne kadar iyilikleriyle orantılı olarak bölmeliler, ama şu anda bunu yapmak için yeterince umursamıyorum.)

EvaluaterBot ve PatternFinder dışında şimdiye kadar yayınlanan her şeyi yener. (Bire bir, EvaluaterBot'u yener ve PatternFinder'a kaybeder).

from collections import defaultdict
import random
class Number6:
    class Choices:
        def __init__(self, C = 0, N = 0, D = 0):
            self.C = C
            self.N = N
            self.D = D

    def __init__(self, strategy = "maxExpected", markov_order = 3):
        self.MARKOV_ORDER = markov_order;
        self.my_choices = "" 
        self.opponent = defaultdict(lambda: self.Choices())
        self.choice = None # previous choice
        self.payoff = {
            "C": { "C": 3-3, "N": 4-1, "D": 0-5 },
            "N": { "C": 1-4, "N": 2-2, "D": 3-2 },
            "D": { "C": 5-0, "N": 2-3, "D": 1-1 },
        }
        self.total_payoff = 0

        # if random, will choose in proportion to payoff.
        # otherwise, will always choose argmax
        self.strategy = strategy
        # maxExpected: maximize expected relative payoff
        # random: like maxExpected, but it chooses in proportion to E[payoff]
        # argmax: always choose the option that is optimal for expected opponent choice

    def update_opponent_model(self, last):
        for i in range(0, self.MARKOV_ORDER):
            hist = self.my_choices[i:]
            self.opponent[hist].C += ("C" == last)
            self.opponent[hist].N += ("N" == last)
            self.opponent[hist].D += ("D" == last)

    def normalize(self, counts):
        sum = float(counts.C + counts.N + counts.D)
        if 0 == sum:
            return self.Choices(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)
        return self.Choices(
            counts.C / sum, counts.N / sum, counts.D / sum)

    def get_distribution(self):
        for i in range(0, self.MARKOV_ORDER):
            hist = self.my_choices[i:]
            #print "check hist = " + hist
            if hist in self.opponent:
                return self.normalize(self.opponent[hist])

        return self.Choices(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)

    def choose(self, dist):
        payoff = self.Choices()
        # We're interested in *beating the opponent*, not
        # maximizing our score, so we optimize the difference
        payoff.C = (3-3) * dist.C + (4-1) * dist.N + (0-5) * dist.D
        payoff.N = (1-4) * dist.C + (2-2) * dist.N + (3-2) * dist.D
        payoff.D = (5-0) * dist.C + (2-3) * dist.N + (1-1) * dist.D

        # D has slightly better payoff on uniform opponent,
        # so we select it on ties
        if self.strategy == "maxExpected":
            if payoff.C > payoff.N:
                return "C" if payoff.C > payoff.D else "D"
            return "N" if payoff.N > payoff.D else "D"
        elif self.strategy == "randomize":
            payoff = self.normalize(payoff)
            r = random.uniform(0.0, 1.0)
            if (r < payoff.C): return "C"
            return "N" if (r < payoff.N) else "D"
        elif self.strategy == "argMax":
            if dist.C > dist.N:
                return "D" if dist.C > dist.D else "N"
            return "C" if dist.N > dist.D else "N"

        assert(0) #, "I am not a number! I am a free man!")

    def update_history(self):
        self.my_choices += self.choice
        if len(self.my_choices) > self.MARKOV_ORDER:
            assert(len(self.my_choices) == self.MARKOV_ORDER + 1)
            self.my_choices = self.my_choices[1:]

    def round(self, last):
        if last: self.update_opponent_model(last)

        dist = self.get_distribution()
        self.choice = self.choose(dist)
        self.update_history()
        return self.choice

class Ensemble:
    def __init__(self):
        self.models = []
        self.votes = []
        self.prev_choice = []
        for order in range(0, 6):
            self.models.append(Number6("maxExpected", order))
            self.models.append(Number6("randomize", order))
            #self.models.append(Number6("argMax", order))
        for i in range(0, len(self.models)):
            self.votes.append(0)
            self.prev_choice.append("D")

        self.payoff = {
            "C": { "C": 3-3, "N": 4-1, "D": 0-5 },
            "N": { "C": 1-4, "N": 2-2, "D": 3-2 },
            "D": { "C": 5-0, "N": 2-3, "D": 1-1 },
        }

    def round(self, last):
        if last:
            for i in range(0, len(self.models)):
                self.votes[i] += self.payoff[self.prev_choice[i]][last]

        # vote. Sufficiently terrible models get negative votes
        C = 0
        N = 0
        D = 0
        for i in range(0, len(self.models)):
            choice = self.models[i].round(last)
            if "C" == choice: C += self.votes[i]
            if "N" == choice: N += self.votes[i]
            if "D" == choice: D += self.votes[i]
            self.prev_choice[i] = choice

        if C > D and C > N: return "C"
        elif N > D: return "N"
        else: return "D"

Test Çerçevesi

Başka birinin faydalı bulması durumunda, bireysel eşleşmelere bakmak için bir test çerçevesi. Python2. İlgilendiğiniz tüm rakipleri opponents.py'ye koyun ve Ensemble'a olan referansları kendiniz değiştirin.

import sys, inspect
import opponents
from ensemble import Ensemble

def count_payoff(label, them):
    if None == them: return
    me = choices[label]
    payoff = {
        "C": { "C": 3-3, "N": 4-1, "D": 0-5 },
        "N": { "C": 1-4, "N": 2-2, "D": 3-2 },
        "D": { "C": 5-0, "N": 2-3, "D": 1-1 },
    }
    if label not in total_payoff: total_payoff[label] = 0
    total_payoff[label] += payoff[me][them]

def update_hist(label, choice):
    choices[label] = choice

opponents = [ x[1] for x 
    in inspect.getmembers(sys.modules['opponents'], inspect.isclass)]

for k in opponents:
    total_payoff = {}

    for j in range(0, 100):
        A = Ensemble()
        B = k()
        choices = {}

        aChoice = None
        bChoice = None
        for i in range(0, 100):
            count_payoff(A.__class__.__name__, bChoice)
            a = A.round(bChoice)
            update_hist(A.__class__.__name__, a)

            count_payoff(B.__class__.__name__, aChoice)
            b = B.round(aChoice)
            update_hist(B.__class__.__name__, b)

            aChoice = a
            bChoice = b
    print total_payoff

Denetleyici hazır, tüm bunları yapmak zorunda
kalmadınız

1
@Blacksilver Bunu hemen hemen teslim etmek üzereyken fark ettim . Ancak bu, 3.6'dan önceki sürümlerde çalışır ve zayıf noktaları tanımlamaya yardımcı olabilecek bireysel eşleşmeler hakkında bilgi verir, bu yüzden tam bir zaman kaybı değildi.
Ray

Yeterince adil; şimdi çalışıyor. Muhtemelen benzer şeyleri yapmak için denetleyicime seçenekler ekleyeceğim.
SIGSTACKFAULT

"Şimdiye kadar Ensemble ve PatternFinder hariç gönderilen her şeyi yener" Onur duyuyorum :)
Sparr

@Sparr Hata. Bu EvaluaterBot ve PatternFinder demekti. Ancak bu, toplam puanı tüm alanla karşılaştırırken. PatternFinder, bunu doğrudan bir maçta yenen tek kişi olmaya devam ediyor.
Ray

4

OldTitForTat

Eski okul oyuncusu yeni kuralları güncellemek için çok tembel.

class OldTitForTat:
    def round(self, last):
        if(last == None)
            return "C"
        if(last == "C"):
            return "C"
        return "D"

3

NeverCOOP

class NeverCOOP:
    def round(self, last):
        try:
            if last in "ND":
                return "D"
            else:
                return "N"
        except:
            return "N"

Rakip bot kusurları veya nötrse, kusuru seçin. Aksi takdirde, bu ilk dönüşse veya karşıt bot işbirliği yaparsa, nötr seçin. Bunun ne kadar iyi çalışacağından emin değilim ...


Deneme / dışında ne var?
SIGSTACKFAULT

1
@Blacksilver Grudger botunuzdaki ile aynı amaca hizmet ettiğini ve if(last):önceki bir tur olup olmadığını tespit ettiğini varsayabilirim.
ETHproductions

Ahh, anlıyorum. None in "ND"hatalar.
SIGSTACKFAULT

Çünkü if last and last in "ND":çok karmaşıktı?
user253751

3

LastOptimalBot

class LastOptimalBot:
    def round(self, last):
        return "N" if last == "D" else ("D" if last == "C" else "C")

Rakip botun her zaman aynı hareketi tekrar oynayacağını varsayar ve ona karşı en iyi getiriyi seçer.

Ortalamalar:

Me   Opp
2.6  2    vs TitForTat
5    0    vs AllC
4    1    vs AllN
3    2    vs AllD
3.5  3.5  vs Random
3    2    vs Grudger
2    2    vs LastOptimalBot
1    3.5  vs TatForTit
4    1    vs NeverCOOP
1    4    vs EvaluaterBot
2.28 2.24 vs NashEquilibrium

2.91 average overall

oof. Belki T4T daha iyi olurdu return last.
SIGSTACKFAULT

Bunu istiyorum! TitForTat return lastolsaydı, LOB şu anda aldığı 5 turda 13-10 yerine 6 turda 18-9 gidiyordu . Bence olduğu gibi iyi - örnek botlar optimize endişe etmeyin.
Spitemaster

return lastBu meydan okuma için daha iyi bir T4T olurdu, sanırım
Sparr

Sadece denedim - if(last): return last; else: return "C"daha kötü.
SIGSTACKFAULT

Doğru, ama @Sparr'ın dediği gibi, daha uygun olabilir. Sanırım size kalmış.
Spitemaster

3

CopyCat

class CopyCat:
    def round(self, last):
        if last:
            return last
        return "C"

Rakibin son hamlesini kopyalar.
Bunun iyi olmasını beklemiyorum, ama henüz kimse bu klasiği uygulamamıştı.


2

Geliştirilmiş Dirichlet Zarları

import random

class DirichletDice2:
    def __init__(self):

        self.alpha = dict(
                C = {'C' : 1, 'N' : 1, 'D' : 1},
                N = {'C' : 1, 'N' : 1, 'D' : 1},
                D = {'C' : 1, 'N' : 1, 'D' : 1}
        )
        self.myLast = [None, None]
        self.payoff = dict(
                C = { "C": 0, "N": 3, "D": -5 },
                N = { "C": -3, "N": 0, "D": 1 },
                D = { "C": 5, "N": -1, "D": 0 }
        )

    def DirichletDraw(self, key):
        alpha = self.alpha[key].values()
        mu = [random.gammavariate(a,1) for a in alpha]
        mu = [m / sum(mu) for m in mu]
        return mu

    def ExpectedPayoff(self, probs):
        expectedPayoff = {}
        for val in ['C','N','D']:
            payoff = sum([p * v for p,v in zip(probs, self.payoff[val].values())])
            expectedPayoff[val] = payoff
        return expectedPayoff

    def round(self, last):
        if last is None:
            self.myLast[0] = 'D'
            return 'D'

        #update dice corresponding to opponent's last response to my
        #outcome two turns ago
        if self.myLast[1] is not None:
            self.alpha[self.myLast[1]][last] += 1

        #draw probs for my opponent's roll from Dirichlet distribution and then return the optimal response
        mu = self.DirichletDraw(self.myLast[0])
        expectedPayoff = self.ExpectedPayoff(mu)
        res = max(expectedPayoff, key=expectedPayoff.get)

        #update myLast
        self.myLast[1] = self.myLast[0]
        self.myLast[0] = res

        return res    

Bu Dirichlet Dice'in geliştirilmiş bir versiyonudur. Beklenen multinom dağılımını Dirichlet dağılımından almak yerine, Dirichlet dağılımından rastgele bir Multinom dağılımını çizer. Daha sonra, Multinomial'dan çizim yapmak ve buna en uygun yanıtı vermek yerine, noktaları kullanarak verilen Multinomial'e en uygun beklenen yanıtı verir. Bu nedenle, rastgelelik esasen Multinomial çekimden Dirichlet çekimine kaydırılmıştır. Ayrıca, keşifleri teşvik etmek için öncelikler artık daha düz.

"Geliştirildi" çünkü artık olasılıklara karşı en iyi değeri vererek puan sistemini açıklarken, olasılıkları kendileri çizerek rasgeleliğini koruyor. Daha önce, beklenen olasılıklardan en iyi beklenen getiriyi yapmayı denedim, ancak bu sadece sıkıştı, çünkü zarını güncellemek için yeterince araştırma yapmadı. Ayrıca daha öngörülebilir ve sömürülebilirdi.


Orijinal gönderim:

Dirichlet Zar

import random

class DirichletDice:
    def __init__(self):

        self.alpha = dict(
                C = {'C' : 2, 'N' : 3, 'D' : 1},
                N = {'C' : 1, 'N' : 2, 'D' : 3},
                D = {'C' : 3, 'N' : 1, 'D' : 2}
        )

        self.Response = {'C' : 'D', 'N' : 'C', 'D' : 'N'}
        self.myLast = [None, None]

    #expected value of the dirichlet distribution given by Alpha
    def MultinomialDraw(self, key):
        alpha = list(self.alpha[key].values())
        probs = [x / sum(alpha) for x in alpha]
        outcome = random.choices(['C','N','D'], weights=probs)[0]
        return outcome

    def round(self, last):
        if last is None:
            self.myLast[0] = 'D'
            return 'D'

        #update dice corresponding to opponent's last response to my
        #outcome two turns ago
        if self.myLast[1] is not None:
            self.alpha[self.myLast[1]][last] += 1

        #predict opponent's move based on my last move
        predict = self.MultinomialDraw(self.myLast[0])
        res = self.Response[predict]

        #update myLast
        self.myLast[1] = self.myLast[0]
        self.myLast[0] = res

        return res

Temel olarak, rakibin son çıkışımdaki cevabının, çıktılarımın her biri için bir tane olmak üzere, çok sayıda değişken (ağırlıklı zar) olduğunu varsayıyorum, bu yüzden "C", biri "N" ve diğeri "D" için bir zar var . Son ruhum örneğin bir "N" ise, "N-zar" ı "N-zar" a ne olacağını tahmin etmek için yuvarlarım. Rakibimin biraz "akıllı" olduğunu varsaymadan önce bir Dirichlet ile başlıyorum (son ruhumda en iyi getiriyi olanı oynama olasılığı daha yüksek, en kötü getirisi olanı oynama olasılığı daha yüksek). Önceden uygun Dirichlet'ten "beklenen" Multinomial dağılımını oluşturuyorum (bu, zar ağırlıklarına göre olasılık dağılımının beklenen değeridir). Son çıktımın ağırlıklı zarını yuvarlıyorum,

Üçüncü turdan başlayarak, rakibimin iki tur önce oynadığım şeye son cevabından önce uygun Dirichlet'in Bayesian güncellemesini yapıyorum. Zar ağırlıklarını tekrar tekrar öğrenmeye çalışıyorum.

Ben de sadece zarları haddeleme ve sonuca cevap yerine, bir kez zar ürettikten sonra en iyi "beklenen" sonucu ile cevap almış olabilir. Ancak, rastgele kalmayı korumak istedim, böylece botum bir deseni tahmin etmeye çalışanlara karşı daha az savunmasız.


2

Kevin

class Kevin:
    def round(self, last):      
        return {"C":"N","N":"D","D":"C",None:"N"} [last]

En kötü tercihi seçer. En kötü bot yaptı.

Faydasız

import random

class Useless:
    def __init__(self):
        self.lastLast = None

    def round(self, last):
        tempLastLast = self.lastLast
        self.lastLast = last

        if(last == "D" and tempLastLast == "N"):
            return "C"
        if(last == "D" and tempLastLast == "C"):
            return "N"

        if(last == "N" and tempLastLast == "D"):
            return "C"
        if(last == "N" and tempLastLast == "C"):
            return "D"

        if(last == "C" and tempLastLast == "D"):
            return "N"
        if(last == "C" and tempLastLast == "N"):
            return "D"

        return random.choice("CND")

Rakip tarafından yapılan son iki hamleye bakar ve en çok yapılmayanı seçer, rastgele bir şey alır. Muhtemelen bunu yapmanın daha iyi bir yolu var.


2

Tarihi Ortalama

class HistoricAverage:
    PAYOFFS = {
        "C":{"C":3,"N":1,"D":5},
        "N":{"C":4,"N":2,"D":2},
        "D":{"C":0,"N":3,"D":1}}
    def __init__(self):
        self.payoffsum = {"C":0, "N":0, "D":0}
    def round(this, last):
        if(last != None):
            for x in this.payoffsum:
               this.payoffsum[x] += HistoricAverage.PAYOFFS[last][x]
        return max(this.payoffsum, key=this.payoffsum.get)

Tarihe bakar ve ortalama olarak en iyi olacak eylemi bulur. Kooperatife başlar.


Her turda ortalamaları yeniden hesaplamazsa bu daha hızlı çalışabilir.
Sparr

@Sparr true. Düzenledim, şimdi öyle.
MegaTom

1

Ağırlıklı ortalama

class WeightedAverageBot:
  def __init__(self):
    self.C_bias = 1/4
    self.N = self.C_bias
    self.D = self.C_bias
    self.prev_weight = 1/2
  def round(self, last):
    if last:
      if last == "C" or last == "N":
        self.D *= self.prev_weight
      if last == "C" or last == "D":
        self.N *= self.prev_weight
      if last == "N":
        self.N = 1 - ((1 - self.N) * self.prev_weight)
      if last == "D":
        self.D = 1 - ((1 - self.D) * self.prev_weight)
    if self.N <= self.C_bias and self.D <= self.C_bias:
      return "D"
    if self.N > self.D:
      return "C"
    return "N"

Rakibin davranışı CND için köşeleri sırasıyla 0,0 0,1 1,0 olan sağ üçgen şeklinde modellenmiştir. Her rakibin hareketi o üçgen içindeki noktayı o köşeye kaydırır ve biz nokta ile gösterilen hareketi yenmek için oynarız (C'ye üçgenden küçük bir dilim verilir). Teorik olarak, bunun önceki hamlelere daha fazla ağırlıkla daha uzun bir hafızaya sahip olmasını istedim, ancak pratikte mevcut meta hızla değişen botları destekliyor, bu nedenle bu, çoğu düşmana karşı LastOptimalBot'un bir yaklaşımına dönüşüyor. Gelecek kuşaklar için paylaşım; belki biri ilham alır.


1

Tetragram

import itertools

class Tetragram:
    def __init__(self):
        self.history = {x: ['C'] for x in itertools.product('CND', repeat=4)}
        self.theirs = []
        self.previous = None

    def round(self, last):
        if self.previous is not None and len(self.previous) == 4:
            self.history[self.previous].append(last)
        if last is not None:
            self.theirs = (self.theirs + [last])[-3:]

        if self.previous is not None and len(self.previous) == 4:
            expected = random.choice(self.history[self.previous])
            if expected == 'C':
                choice = 'C'
            elif expected == 'N':
                choice = 'C'
            else:
                choice = 'N'
        else:
            choice = 'C'

        self.previous = tuple(self.theirs + [choice])
        return choice

Son hamlemizi de izlediklerini varsayarak, rakibin hamlelerinde bir desen bulmaya çalışın.


1

Tokalaşma

class HandshakeBot:
  def __init__(self):
    self.handshake_length = 4
    self.handshake = ["N","N","C","D"]
    while len(self.handshake) < self.handshake_length:
      self.handshake *= 2
    self.handshake = self.handshake[:self.handshake_length]
    self.opp_hand = []
    self.friendly = None
  def round(self, last):
    if last:
      if self.friendly == None:
        # still trying to handshake
        self.opp_hand.append(last)
        if self.opp_hand[-1] != self.handshake[len(self.opp_hand)-1]:
          self.friendly = False
          return "D"
        if len(self.opp_hand) == len(self.handshake):
          self.friendly = True
          return "C"
        return self.handshake[len(self.opp_hand)]
      elif self.friendly == True:
        # successful handshake and continued cooperation
        if last == "C":
          return "C"
        self.friendly = False
        return "D"
      else:
        # failed handshake or abandoned cooperation
        return "N" if last == "D" else ("D" if last == "C" else "C")
    return self.handshake[0]

Kendisine karşı oynadığında tanır, sonra işbirliği yapar. Aksi takdirde en iyi tek satırlık strateji gibi görünen LastOptimalBot'u taklit eder. Tur sayısı ile ters orantılı olarak LastOptimalBot'tan daha kötü performans gösterir. Açıkçası * öksürük * göz kırpma * alanında daha fazla kopya olsaydı daha iyi olurdu.


Farklı el sıkışma davranışı olmayan birkaç klon gönderin.
SIGSTACKFAULT

Bu istismar gibi görünüyor. Burada temsil edilen her basit davranış için böyle bir klon gönderebilirim.
Sparr

Yalnızca en fazla beş bot gönderebileceğinizi belirten ekstra bir madde ekledim.
SIGSTACKFAULT

1

ShiftingOptimalBot

class ShiftingOptimalBot:
    def __init__(self):
        # wins, draws, losses
        self.history = [0,0,0]
        self.lastMove = None
        self.state = 0
    def round(self, last):
        if last == None:
            self.lastMove = "C"
            return self.lastMove
        if last == self.lastMove:
            self.history[1] += 1
        elif (last == "C" and self.lastMove == "D") or (last == "D" and self.lastMove == "N") or (last == "N" and self.lastMove == "C"):
            self.history[0] += 1
        else:
            self.history[2] += 1

        if self.history[0] + 1 < self.history[2] or self.history[2] > 5:
            self.state = (self.state + 1) % 3
            self.history = [0,0,0]
        if self.history[1] > self.history[0] + self.history[2] + 2:
            self.state = (self.state + 2) % 3
            self.history = [0,0,0]

        if self.state == 0:
            self.lastMove = "N" if last == "D" else ("D" if last == "C" else "C")
        elif self.state == 1:
            self.lastMove = last
        else:
            self.lastMove = "C" if last == "D" else ("N" if last == "C" else "D")
        return self.lastMove

Bu bot, kazandığı sürece LastOptimalBot algoritmasını kullanır. Bununla birlikte, diğer bot tahmin etmeye başlarsa, rakibinin en son oynadığı hamleyi oynamaya başlayacaktır (LastOptimalBot'u yenecek hamleyi yenen hareket). Kaybetmeye devam ettiği sürece (veya çok fazla çizim yaparak sıkıldığında) bu algoritmaların basit aktarımları arasında geçiş yapar.

Dürüst olmak gerekirse, bunu gönderirken LastOptimalBot'un 5.'de oturduğuna şaşırdım. Bu pitonu doğru yazdığımı varsayarak, bunun daha iyi olacağından eminim.


0

HandshakePatternMatch

from .patternfinder import PatternFinder
import collections

class HandshakePatternMatch:
    def __init__(self):
        self.moves = [None]
        self.other = []
        self.handshake = [None,"N","C","C","D","N"]
        self.friendly = None
        self.pattern = PatternFinder()
    def round(self, last):
        self.other.append(last)
        if last:
            if len(self.other) < len(self.handshake):
                # still trying to handshake
                if self.friendly == False or self.other[-1] != self.handshake[-1]:
                    self.friendly = False
                else:
                    self.friendly = True
                move = self.handshake[len(self.other)]
                self.pattern.round(last)
            elif self.friendly == True:
                # successful handshake and continued cooperation
                move = self.pattern.round(last)
                if last == "C":
                    move = "C"
                elif last == self.handshake[-1] and self.moves[-1] == self.handshake[-1]:
                    move = "C"
                else:
                    self.friendly = False
            else:
                # failed handshake or abandoned cooperation
                move = self.pattern.round(last)
        else:
            move = self.handshake[1]
            self.pattern.round(last)
        self.moves.append(move)
        return move

Desen neden kendinize uyuyor? El sıkışma ve işbirliği.


import PatternFinderkitaplarımı aldatıyor.
SIGSTACKFAULT

@Blacksilver Her zaman KOTH'de yapılır. Kodu mevcut bir cevapta kopyalayıp kullanmaktan farklı değildir. Robot Ruleti: Robot bahisleri , her yerde, botların kodlarının bir rakip tarafından çağrılıp çağrılmadığını tespit edeceği ve dönüşü sabote ettiği noktaya kadar gerçekleşti.
Draco18s

Tamam o zaman. TIL.
SIGSTACKFAULT

Yarın çatırtıyı yapacağım.
SIGSTACKFAULT

İşte diğer bot kodlarını kullanmaya mükemmel bir örnek. Genellikle "bu adam zor bir matematik yaptı, sonuçlarını bu koşullar altında istiyorum." ( Kendi girişim bunu oldukça iyi bir etki için yaptı; UpYours yaklaşımında daha fazla saçılmıştı).
Draco18s

0

Hardcoded

class Hardcoded:
    sequence = "DNCNNDDCNDDDCCDNNNNDDCNNDDCDCNNNDNDDCNNDDNDDCDNCCNNDNNDDCNNDDCDCNNNDNCDNDNDDNCNDDCDNNDCNNDDCDCNNDNNDDCDNDDCCNNNDNNDDCNNDDNDCDNCNDDCDNNDDCCNDNNDDCNNNDCDNDDCNNNNDNDDCDNCDCNNDNNDDCDNDDCCNNNDNDDCNNNDNDCDCDNNDCNNDNDDCDNCNNDDCNDNNDDCDNNDCDNDNCDDCNNNDNDNCNDDCDNDDCCNNNNDNDDCNNDDCNNDDCDCNNDNNDDCDNDDCCNDNNDDCNNNDCDNNDNDDCCNNNDNDDNCDCDNNDCNNDNDDCNNDDCDNCNNDDCDNNDCDNDNCDDCNDNNDDCNNNDDCDNCNNDNNDDCNNDDNNDCDNCNDDCNNDCDNNDDCNNDDNCDCNNDNDNDDCDNCDCNNNDNDDCDCNNDNNDDCDNDDCCNNNDNNDDCNDNDNCDDCDCNNNNDNDDCDNCNDDCDNNDDCNNNDNDDCDNCNNDCNNDNDDNCDCDNNNDDCNNDDCNNDDNNDCDNCNDDCNNDDNDCDNNDNDDCCNCDNNDCNNDDNDDCNCDNNDCDNNNDDCNNDDCDCDNNDDCNDNCNNDNNDNDNDDCDNCDCNNNDNDDCDNCNNDDCDNNDCNNDDCNNDDCDCDNNDDCNDNCNNNDDCDNNDCDNDNCNNDNDDNNDNDCDDCCNNNDDCNDNDNCDDCDCNNNDNNDDCNDCDNDDCNNNNDNDDCCNDNNDDCDCNNNDNDDNDDCDNCCNNDNNDDCNNDDCDCNNDNNDDCNNDDNCNDDNNDCDNCNDDCNNDDNDCDNNDNDDCCNCDNNDCNNDNDDCNNDDNCDCDNNDCNNDNDDCDCDNNNNDDCNNDDNDCCNNDDNDDCNCDNNDCNNDDNDDCDNCNDDCNNNNDCDNNDDCNDNDDCDNCNNDCDNNDCNNDNDDNCDCNNDNDDCDNDDCCNNNNDNDDCNNDDCDCNNDNNDDCDCDNNDDC"
    def __init__(self):
        self.round_num = -1
    def round(self,_):
        self.round_num += 1
        return Hardcoded.sequence[self.round_num % 1000]

Sadece üst deterministik botlardan bazılarını yenmek için optimize edilmiş sabit kodlu bir dizi hareket oynar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.