Trilemma ของนักโทษที่ซ้ำแล้วซ้ำอีก


19

สถานะการท้าทาย: เปิด

แสดงความคิดเห็นเปิดการประชาสัมพันธ์หรือตะโกนใส่ฉันถ้าฉันขาดบอทของคุณ


ภาวะที่กลืนไม่เข้าคายไม่ออกของนักโทษ ... มีสามทางเลือก บ้าเหรอ?

นี่คือเมทริกซ์ผลตอบแทนของเรา ผู้เล่น A ด้านซ้าย, B อยู่ด้านบน

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

เมทริกซ์ผลตอบแทนได้รับการออกแบบมาเพื่อให้ดีที่สุดสำหรับผู้เล่นทั้งสองที่จะร่วมมือกัน แต่คุณสามารถได้รับ (โดยปกติ) โดยการเลือก Neutral หรือ Defection

นี่คือตัวอย่างบอท (แข่งขัน)

# 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"

บอทของคุณคือคลาส Python3 อินสแตนซ์ใหม่ถูกสร้างขึ้นสำหรับทุกเกมและround()ถูกเรียกแต่ละรอบโดยเลือกคู่ต่อสู้ของคุณจากรอบสุดท้าย (หรือไม่มีหากเป็นรอบแรก)

มีค่าหัว 50 ค่าตอบแทนสำหรับผู้ชนะในเดือนเดียว

ข้อมูลจำเพาะ

  • บอททุกคนเล่นบอทตัวอื่น (1v1) รวมถึงตัวมันเองในรอบ [ข้อมูลถูกลบ]
  • ช่องโหว่มาตรฐานไม่อนุญาต
  • ไม่ยุ่งกับสิ่งใดนอกชั้นเรียนของคุณหรือเล่ห์เหลี่ยมอื่น ๆ
  • คุณสามารถส่งได้ถึงห้าบอท
  • ใช่คุณสามารถใช้การจับมือกัน
  • การตอบสนองอื่น ๆ กว่าใด ๆC, Nหรือจะถูกนำมาเป็นอย่างเงียบDN
  • คะแนนแต่ละบอทจากทุกเกมที่เล่นจะถูกรวมและเปรียบเทียบ

ตัวควบคุม

ตรวจสอบ!

ภาษาอื่น ๆ

ฉันจะรวม API ถ้าใครต้องการมัน

คะแนน: 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
บอทใส่กันอย่างไร ฉันได้รับจาก Grudger ที่มีบอทสองตัวต่อ / ต่อกันและตัวเลือกสุดท้ายของศัตรูจะถูกส่งไปยังบอท มีการเล่นกี่รอบ และสำหรับเกม: มีเพียงผลคะแนนที่ได้รับ (เช่นใครชนะ) หรือแต้ม?
นกฮูกสีดำไก่

1
คุณจะได้รับข้อความเพิ่มเติมถ้าคุณทำให้ผู้ไม่เชื่อเรื่องภาษานี้หรืออย่างน้อยก็กว้างขวางขึ้น คุณสามารถมีคลาสไพ ธ อน wrapper ที่ spawns กระบวนการและส่งคำสั่งข้อความเพื่อรับการตอบกลับข้อความ
Sparr

1
เสร็จสิ้น นี่อยู่บนกล่องทรายเป็นเวลาหนึ่งเดือน!
SIGSTACKFAULT

2
หากคุณใส่ main.py ไว้while len(botlist) > 1:ด้วยbotlist.remove(lowest_scoring_bot)ที่ด้านล่างของลูปคุณจะได้ทัวร์นาเมนท์กำจัดพร้อมผลลัพธ์ที่น่าสนใจ
Sparr

1
อีกสักวันหนึ่งของรุ่นนี้อาจผ่านประวัติศาสตร์การโต้ตอบทั้งหมดมากกว่าแค่การเคลื่อนไหวครั้งสุดท้าย มันไม่เปลี่ยนแปลงมากนักแม้ว่าจะทำให้รหัสผู้ใช้ง่ายขึ้นเล็กน้อย แต่มันจะอนุญาตให้มีการขยายเช่นช่องทางการสื่อสารที่มีเสียงดังที่ชัดเจนเมื่อเวลาผ่านไป: "จริง ๆ , D แม้ว่าฉันจะพูด C ถึงสี่ครั้งติดต่อกันหรือไม่ไม่ฉันไม่ได้พูด D คุณเอาอะไรฉัน ใช่มั้ยขออภัยเราสามารถลืมรอบนั้นได้หรือไม่ "
Scott Sauyet

คำตอบ:


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]

ชนะกับบอทที่ส่งไปก่อนหน้าทั้งหมดยกเว้น (อาจจะ) บอทแบบสุ่ม (แต่อาจมีข้อได้เปรียบเพราะมันเลือก D ในการเสมอและดีที่สุดควร) และเล่นเสมอเสมอกับตัวเอง


ใช่เต้นทุกอย่าง
SIGSTACKFAULT

เกาที่ PatternFinder จะเต้นตามบิต
SIGSTACKFAULT

7

NashEquilibrium

บอทนี้เข้าเรียนวิชาทฤษฎีเกมในวิทยาลัย แต่ขี้เกียจและไม่ได้ไปเรียนที่พวกเขาครอบคลุมเกมที่ซ้ำแล้วซ้ำอีก ดังนั้นเขาจึงเล่นเพียงแค่เกมเดียวผสมสมดุลแนช เปลี่ยนเป็น 1/5 2/5 2/5 คือ NE ผสมสำหรับการจ่ายเงิน

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

การละเมิดอย่างต่อเนื่องของ Nash Nash

บอทนี้หยิบบทเรียนหนึ่งหรือสองอย่างจากพี่ชายขี้เกียจของเขา ปัญหาของพี่ชายขี้เกียจของเขาคือเขาไม่ได้ใช้ประโยชน์จากกลยุทธ์ที่แน่นอน รุ่นนี้ตรวจสอบว่าฝ่ายตรงข้ามเป็นผู้เล่นคงที่หรือ titfortat และเล่นตามนั้นมิฉะนั้นมันจะเล่นสมดุลแนชปกติ

ข้อเสียเพียงอย่างเดียวก็คือมันมีค่าเฉลี่ย 2.2 คะแนนต่อการเล่นกับตัวเอง

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 จำเป็นต้องรับอาร์กิวเมนต์แม้ว่ามันจะไม่ได้ใช้ก็ตามเพื่อให้เหมาะสมกับฟังก์ชั่นต้นแบบที่ต้องการ
เรย์

ขอบคุณคง
Ofya

สั้นไปหน่อย: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"

บอทนี้จะสลับการเลือก DNDN ในขณะที่ TitForTat สลับ CDCD เพื่อรับกำไรสุทธิเฉลี่ย 3 คะแนนต่อรอบถ้าฉันอ่านตารางการจ่ายเงินอย่างถูกต้อง ฉันคิดว่านี่อาจเป็นสิ่งที่ดีที่สุดสำหรับ TitForTat เห็นได้ชัดว่ามันสามารถปรับปรุงให้ดีขึ้นเพื่อตรวจจับคู่ต่อสู้ที่ไม่ใช่ TFT และปรับใช้กลยุทธ์อื่น ๆ แต่ฉันก็แค่ตั้งเป้าที่จะรับรางวัลดั้งเดิม


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

บอทนี้จะค้นหาเหตุการณ์ล่าสุดของสถานะเกมล่าสุดเพื่อดูว่าฝ่ายตรงข้ามตอบสนองต่อเหตุการณ์เหล่านั้นอย่างไรด้วยการตั้งค่าสำหรับรูปแบบการแข่งขันที่ยาวขึ้นและการแข่งขันที่ผ่านมามากขึ้นจากนั้นเล่นท่าที่จะ "เอาชนะ" มีห้องมากมายที่จะฉลาดกว่าเดิมด้วยข้อมูลทั้งหมดที่ติดตาม แต่ฉันหมดเวลาทำงาน


เมื่อคุณได้รับเวลาลองคิดให้เธอเพิ่มประสิทธิภาพ เป็นช่วงเวลาที่ใหญ่ที่สุด
SIGSTACKFAULT

2
@ Blacksilver ฉันเพิ่งลดความยาวสูงสุดของรูปแบบจาก 100 เป็น 10 มันควรจะทำงานเกือบจะทันทีในขณะนี้หากคุณใช้งาน <200 รอบ
Sparr

1
อาจจะใช้หมายเลขคอมโพสิตสูง (เช่น 12) จะให้คะแนนดีกว่าหรือไม่
SIGSTACKFAULT

5

หยก

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'

เริ่มมองโลกในแง่ดี แต่ได้รับความขมขื่นมากขึ้นเรื่อย ๆ ในขณะที่คู่ต่อสู้ปฏิเสธที่จะร่วมมือ ค่าคงที่เวทย์มนตร์จำนวนมากที่อาจถูกปรับแต่ง แต่นี่อาจจะไม่ดีพอที่จะพิสูจน์เวลา


5

ทั้งมวล

สิ่งนี้รันชุดของโมเดลที่เกี่ยวข้อง แต่ละรุ่นจะพิจารณาจำนวนประวัติที่แตกต่างกันและมีตัวเลือกว่าจะเลือกการย้ายที่จะปรับความแตกต่างของการจ่ายเงินให้ได้ตามที่คาดหวังไว้เสมอหรือจะเลือกการย้ายสัดส่วนตามความแตกต่างของการจ่ายเงิน

สมาชิกของวงดนตรีแต่ละคนนั้นลงคะแนนในการย้ายที่ต้องการของพวกเขา พวกเขาได้รับคะแนนโหวตเท่ากับจำนวนที่พวกเขาชนะมากกว่าคู่ต่อสู้ (ซึ่งหมายความว่าแบบจำลองที่แย่จะได้รับคะแนนโหวตติดลบ) ไม่ว่าการเคลื่อนไหวใดจะชนะคะแนนโหวตก็จะถูกเลือก

(พวกเขาควรแบ่งคะแนนเสียงระหว่างการเคลื่อนไหวตามสัดส่วนที่พวกเขาชอบแต่ละคน แต่ฉันไม่สนใจพอที่จะทำในตอนนี้)

มันเต้นทุกอย่างที่โพสต์แล้วยกเว้น EvaluaterBot และ PatternFinder (หนึ่งต่อหนึ่งมันเต้น EvaluaterBot และสูญเสียไปที่ PatternFinder)

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"

กรอบการทดสอบ

ในกรณีที่คนอื่นเห็นว่ามีประโยชน์นี่คือกรอบการทดสอบสำหรับการดูการจับคู่ของแต่ละบุคคล Python2 เพียงแค่ใส่คู่ต่อสู้ทั้งหมดที่คุณสนใจในฝ่ายตรงข้ามและเปลี่ยนการอ้างอิงเป็นชุดของคุณเอง

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

คอนโทรลเลอร์พร้อมแล้วคุณไม่จำเป็นต้องทำทุกอย่าง ...
SIGSTACKFAULT

1
@Blacksilver ฉันตระหนักว่าเพียงแค่ขณะที่ผมกำลังจะส่ง แต่อันนี้ใช้ได้ในเวอร์ชั่นก่อน 3.6 และให้ข้อมูลเกี่ยวกับการจับคู่ส่วนบุคคลที่สามารถช่วยระบุจุดอ่อนได้ดังนั้นจึงเป็นการเสียเวลาโดยสิ้นเชิง
เรย์

ยุติธรรมเพียงพอ วิ่งตอนนี้ ฉันอาจเพิ่มตัวเลือกในคอนโทรลเลอร์ของฉันเพื่อทำสิ่งที่คล้ายกัน
SIGSTACKFAULT

"มันเต้นทุกอย่างที่โพสต์เพื่อให้ห่างไกลยกเว้นทั้งมวลและ PatternFinder" ผมรู้สึกเป็นเกียรติ :)
SPARR

@Sparr โอ๊ะโอ นั่นควรจะพูด EvaluaterBot และ PatternFinder แต่นั่นคือเมื่อเปรียบเทียบคะแนนรวมกับสนามทั้งหมด PatternFinder ยังคงเป็นสิ่งเดียวเท่านั้นที่เต้นแบบนี้ในการจับคู่โดยตรง
เรย์

4

OldTitForTat

ผู้เล่นโรงเรียนเก่าขี้เกียจเกินไปที่จะอัพเดทกฎใหม่

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"

หากข้อบกพร่องของบอทฝ่ายตรงข้ามหรือเป็นกลางให้เลือกข้อบกพร่อง มิฉะนั้นถ้านี่เป็นเทิร์นแรกหรือบอทฝ่ายตรงข้ามให้เลือกที่เป็นกลาง ฉันไม่แน่ใจว่ามันจะทำงานได้ดีเพียงใด ...


ลอง / ยกเว้นคืออะไร
SIGSTACKFAULT

1
@ Blacksilver ฉันจะถือว่ามันมีจุดประสงค์เดียวกันกับif(last):ใน bot ของคุณ Grudger โดยตรวจสอบว่ามีรอบก่อนหน้านี้หรือไม่
ETHproductions

อ่าฉันเข้าใจแล้ว None in "ND"ข้อผิดพลาด
SIGSTACKFAULT

เพราะif last and last in "ND":มันซับซ้อนเกินไป
user253751

3

LastOptimalBot

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

สมมติว่าบอทที่เป็นปฏิปักษ์จะเล่นท่าเดิมอีกครั้งและเลือกอันที่มีการจ่ายผลตอบแทนที่ดีที่สุด

ค่าเฉลี่ย:

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 บางที T4T น่าจะทำได้ดีกว่าreturn lastนี้
SIGSTACKFAULT

ฉันต้องการสิ่งนั้น! หาก TitForTat เป็นreturn lastLOB จะไป 18-9 มากกว่า 6 รอบแทนที่จะเป็น 13-10 มากกว่า 5 รอบที่กำลังได้รับในปัจจุบัน ฉันคิดว่ามันดีเหมือนเดิม - ไม่ต้องกังวลกับการเพิ่มประสิทธิภาพบอทตัวอย่าง
Spitemaster

return lastจะเป็น T4T ที่ดีกว่าสำหรับความท้าทายนี้ผมคิดว่า
SPARR

แค่พยายาม - if(last): return last; else: return "C"แย่กว่านั้น
SIGSTACKFAULT

ใช่ แต่อย่างที่ @Sparr พูดมันอาจจะเหมาะสมกว่า ฉันคิดว่าขึ้นอยู่กับคุณ
Spitemaster

3

CopyCat

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

คัดลอกการเคลื่อนไหวล่าสุดของคู่ต่อสู้
ฉันไม่คาดหวังว่าสิ่งนี้จะทำได้ดี แต่ยังไม่มีใครนำคลาสสิกนี้ไปใช้


2

ปรับปรุง Dirichlet Dice

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    

นี่เป็น Dirichlet Dice รุ่นปรับปรุง แทนที่จะใช้การกระจายแบบหลายจุดที่คาดหวังจากการแจกแจง Dirichlet จะดึงการแจกแจงแบบหลายเส้นแบบสุ่มจากการแจกแจง Dirichlet จากนั้นแทนที่จะวาดจาก Multinomial และให้การตอบสนองที่ดีที่สุดมันจะให้การตอบสนองที่คาดหวังที่ดีที่สุดสำหรับ Multinomial ที่กำหนดโดยใช้คะแนน ดังนั้นการสุ่มได้เปลี่ยนจากการวาด Multinomial เป็น Dirichlet ยิ่งไปกว่านั้นบรรดานักบวชก็แบนราบมากขึ้นในตอนนี้เพื่อส่งเสริมการสำรวจ

มันคือ "ปรับปรุง" เพราะตอนนี้บัญชีสำหรับระบบคะแนนโดยให้ค่าที่คาดหวังที่ดีที่สุดเมื่อเทียบกับความน่าจะเป็นในขณะที่รักษาแบบแผนโดยการวาดความน่าจะเป็นของตัวเอง ก่อนหน้านี้ฉันพยายามทำสิ่งที่คาดหวังได้ดีที่สุดจากความน่าจะเป็นที่คาดหวัง แต่นั่นก็ไม่ดีเพราะมันติดขัดและไม่ได้สำรวจพอที่จะอัพเดทลูกเต๋า นอกจากนี้ยังสามารถคาดการณ์และใช้ประโยชน์ได้มากกว่า


ส่งต้นฉบับ:

ลูกเต๋า Dirichlet

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

โดยทั่วไปฉันสมมติว่าการตอบสนองของฝ่ายตรงข้ามต่อผลลัพธ์สุดท้ายของฉันคือตัวแปร multinomial (ลูกเต๋าถ่วงน้ำหนัก) หนึ่งรายการสำหรับแต่ละผลลัพธ์ของฉันดังนั้นจึงมีลูกเต๋าสำหรับ "C", หนึ่งสำหรับ "N" และอีกหนึ่งสำหรับ "D" . ดังนั้นหากการหมุนรอบสุดท้ายของฉันเป็นเช่น "N" ดังนั้นฉันจึงหมุน "N-dice" เพื่อเดาว่าคำตอบของพวกเขาจะเป็นอย่างไรกับ "N" ของฉัน ฉันเริ่มต้นด้วย Dirichlet ก่อนที่จะถือว่าฝ่ายตรงข้ามของฉันค่อนข้าง "ฉลาด" (มีแนวโน้มที่จะเล่นกับการจ่ายผลตอบแทนที่ดีที่สุดกับการหมุนรอบสุดท้ายของฉันอย่างน้อยน่าจะเล่นกับการจ่ายที่เลวร้ายที่สุด) ฉันสร้างการกระจาย Multinomial "ที่คาดหวัง" จาก Dirichlet ที่เหมาะสมก่อนหน้า (นี่คือค่าที่คาดหวังของการกระจายความน่าจะเป็นมากกว่าน้ำหนักของลูกเต๋า) ฉันหมุนลูกเต๋าที่ถ่วงน้ำหนักของผลลัพธ์สุดท้ายของฉัน

เริ่มต้นในรอบที่สามฉันทำการอัพเดต Bayesian ของ Dirichlet ที่เหมาะสมก่อนที่คู่ต่อสู้ของฉันจะตอบสนองต่อสิ่งที่ฉันเล่นสองรอบที่ผ่านมา ฉันพยายามที่จะเรียนรู้การยกน้ำหนักของพวกเขาซ้ำ ๆ

ฉันสามารถเลือกการตอบสนองกับผลลัพธ์ "ที่คาดหวัง" ที่ดีที่สุดเมื่อสร้างลูกเต๋าแทนที่จะเพียงหมุนลูกเต๋าและตอบสนองต่อผลลัพธ์ อย่างไรก็ตามฉันต้องการเก็บแบบแผนไว้เพื่อที่บอทของฉันจะมีความเสี่ยงน้อยกว่าคนที่พยายามทำนายรูปแบบ


2

เควิน

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

เลือกตัวเลือกที่แย่ที่สุด บอทที่เลวร้ายที่สุดทำ

ไร้ประโยชน์

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

ดูที่การเคลื่อนไหวสองครั้งสุดท้ายที่ทำโดยฝ่ายตรงข้ามและเลือกสิ่งที่ไม่ได้ทำมากที่สุด อาจมีวิธีที่ดีกว่าในการทำเช่นนี้


2

ค่าเฉลี่ยประวัติศาสตร์

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)

ดูประวัติและค้นหาการกระทำที่จะดีที่สุดโดยเฉลี่ย เริ่มสหกรณ์


สิ่งนี้สามารถทำงานได้เร็วขึ้นหากไม่คำนวณค่าเฉลี่ยใหม่ทุกรอบ
Sparr

@ Sparr จริง ฉันแก้ไขมันทันที
MegaTom

1

ถัวเฉลี่ยถ่วงน้ำหนัก

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"

พฤติกรรมของคู่ต่อสู้ถูกจำลองเป็นรูปสามเหลี่ยมมุมฉากที่มีมุมสำหรับ CND ที่ 0,0 0,1 1,0 ตามลำดับ การเคลื่อนไหวของฝ่ายตรงข้ามแต่ละครั้งเลื่อนจุดภายในสามเหลี่ยมนั้นไปยังมุมนั้นและเราเล่นเพื่อเอาชนะการเคลื่อนไหวที่ระบุโดยจุดนั้น (โดยที่ C ได้รับชิ้นเล็ก ๆ ที่ปรับได้ของสามเหลี่ยม) ในทางทฤษฎีแล้วฉันต้องการให้หน่วยความจำนานขึ้นโดยมีน้ำหนักมากกว่าการเคลื่อนไหวก่อนหน้า แต่ในทางปฏิบัติ meta ปัจจุบันสนับสนุนบอตที่เปลี่ยนแปลงอย่างรวดเร็วดังนั้นสิ่งนี้จึงกลายเป็นการประมาณ LastOptimalBot กับศัตรูส่วนใหญ่ โพสต์สำหรับลูกหลาน; บางทีใครบางคนจะได้รับแรงบันดาลใจ


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

พยายามหารูปแบบการเคลื่อนไหวของคู่ต่อสู้โดยสมมติว่าพวกเขากำลังดูท่าสุดท้ายของเราด้วย


1

การจับมือกัน

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]

รับรู้เมื่อเล่นกับตัวเองแล้วให้ความร่วมมือ มิฉะนั้นเลียนแบบ LastOptimalBot ซึ่งดูเหมือนว่าเป็นกลยุทธ์หนึ่งบรรทัดที่ดีที่สุด ดำเนินการที่เลวร้ายยิ่งกว่า LastOptimalBot โดยจำนวนที่แปรผกผันกับจำนวนรอบ เห็นได้ชัดว่าจะดีขึ้นถ้ามีสำเนามากกว่านี้ในฟิลด์ * ไอ ** ขยิบตา *


เพียงส่งโคลนนิ่งสองสามตัวที่มีพฤติกรรมที่ไม่จับมือกัน
SIGSTACKFAULT

ดูเหมือนว่าจะใช้ประโยชน์จาก y ฉันสามารถส่งโคลนนิ่งดังกล่าวหนึ่งอันสำหรับพฤติกรรมง่าย ๆ ทุกอันที่แสดงไว้ที่นี่
Sparr

ฉันได้เพิ่มประโยคพิเศษที่บอกว่าคุณสามารถส่งได้สูงสุดห้าบอทเท่านั้น
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

บอทนี้ใช้อัลกอริทึมของ LastOptimalBot ตราบใดที่ยังชนะ ถ้าบอตตัวอื่นเริ่มทำนายมันมันจะเริ่มเล่นไม่ว่าฝ่ายตรงข้ามจะเล่นครั้งสุดท้ายเมื่อใด มันวนรอบผ่านการแปลงสัญญาณอย่างง่ายของอัลกอริธึมเหล่านั้นตราบใดที่มันยังคงสูญเสียไป

สุจริตฉันประหลาดใจที่ LastOptimalBot กำลังนั่งอยู่ในอันดับที่ 5 ในขณะที่โพสต์ข้อความนี้ ฉันค่อนข้างแน่ใจว่าสิ่งนี้จะทำได้ดีกว่าโดยสมมติว่าฉันเขียน ธ นี้ถูกต้อง


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

ทำไมรูปแบบการจับคู่ตัวเอง? จับมือกันและร่วมมือกันออกไป


import PatternFinderกำลังโกงหนังสือของฉัน
SIGSTACKFAULT

@Blacksilver มันทำมาตลอดเวลาใน KOTH มันไม่แตกต่างจากการคัดลอกรหัสในคำตอบที่มีอยู่และใช้มัน หุ่นยนต์รูเล็ต: การพนันหุ่นยนต์มีเดิมพันสูงเกิดขึ้นทั่วทุกจุดจนถึงจุดที่บอทจะตรวจพบว่ารหัสของพวกเขาถูกเรียกโดยฝ่ายตรงข้ามและก่อวินาศกรรมกลับมา
Draco18s

เอาล่ะแล้ว TIL
SIGSTACKFAULT

พรุ่งนี้ฉันจะทำกระทืบ
SIGSTACKFAULT

นี่คือตัวอย่างที่สมบูรณ์แบบของการใช้รหัสบอทอื่น ๆ โดยปกติแล้วจะเกิดขึ้นกับ "ผู้ชายคนนั้นคิดเลขได้ยากฉันต้องการผลลัพธ์ของเขาภายใต้เงื่อนไขเหล่านี้" ( รายการของฉันเองนั้นให้ผลที่ดีทีเดียว; UpYours ของคุณกระจัดกระจายมากขึ้นในแนวทางของมัน)
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]

เพียงแค่เล่นลำดับการเคลื่อนไหวที่ได้รับการปรับแต่งเพื่อเอาชนะบ็อตที่กำหนดค่าสูงสุดบางอย่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.