Trilemma tù nhân lặp đi lặp lại


19

TÌNH TRẠNG THÁCH THỨC: MỞ

Nhận xét, mở PR hoặc nói cách khác là mắng tôi nếu tôi thiếu bot của bạn.


Tình trạng khó xử của tù nhân ... với ba lựa chọn. Bị điên hả?

Đây là ma trận xuất chi của chúng tôi. Người chơi A bên trái, B ở trên cùng

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

Ma trận hoàn trả được thiết kế sao cho tốt nhất cho cả hai người chơi luôn hợp tác, nhưng bạn có thể đạt được (thông thường) bằng cách chọn Trung lập hoặc Khiếm khuyết.

Đây là một số bot (cạnh tranh).

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

Bot của bạn là một lớp Python3. Một phiên bản mới được tạo cho mọi trò chơi và round()được gọi là mỗi vòng, với lựa chọn của đối thủ của bạn từ vòng trước (hoặc Không có, nếu đó là vòng đầu tiên)

Có 50 tiền thưởng cho người chiến thắng trong vòng một tháng.

Cụ thể

  • Mỗi bot chơi mọi bot khác (1v1), bao gồm chính nó, trong các vòng [GIẢM GIÁ].
  • Sơ hở tiêu chuẩn không được phép.
  • Không gây rối với bất cứ điều gì bên ngoài lớp học của bạn hoặc các shenanigains ngầm.
  • Bạn có thể gửi tối đa năm bot.
  • Có, bạn có thể thực hiện bắt tay.
  • Bất kỳ phản ứng khác hơn C, Nhoặc Dsẽ được âm thầm lấy làm N.
  • Mỗi điểm của bot từ mỗi trò chơi họ chơi sẽ được tính tổng và so sánh.

Bộ điều khiển

Kiểm tra!

Những ngôn ngữ khác

Tôi sẽ kết hợp một API nếu có ai cần nó.

Điểm: 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
Làm thế nào các bot đặt chống lại nhau? Tôi nhận được từ Grudger rằng luôn có hai bot chống lại / với nhau và lựa chọn cuối cùng của kẻ thù được chuyển cho bot. Có bao nhiêu vòng được chơi? Và đối với một trò chơi: Chỉ tính tổng kết quả (tức là ai thắng) hay số điểm?
Black Owl Kai

1
Bạn sẽ nhận được nhiều mục hơn nếu bạn thực hiện ngôn ngữ này, hoặc ít nhất là rộng hơn. Bạn có thể có một lớp python bao bọc tạo ra một quá trình và gửi nó các lệnh văn bản để lấy lại phản hồi văn bản.
Sparr

1
Làm xong. Đây là trên hộp cát trong một tháng!
SIGSTACKFAULT

2
Nếu bạn quấn hầu hết main.py ở while len(botlist) > 1:với botlist.remove(lowest_scoring_bot)ở dưới cùng của vòng lặp, bạn nhận được một giải đấu loại bỏ những kết quả thú vị.
Sparr

1
Một phiên bản khác của một ngày nào đó có thể vượt qua toàn bộ lịch sử tương tác thay vì chỉ là bước cuối cùng. Nó không thay đổi nhiều mặc dù nó đơn giản hóa mã người dùng một chút. Nhưng nó sẽ cho phép các tiện ích mở rộng, chẳng hạn như các kênh liên lạc ồn ào làm rõ theo thời gian: "Thực sự, một D, mặc dù tôi đã nói C bốn lần liên tiếp? Không, tôi không nói D; bạn lấy gì cho tôi Vì sao? Ồ, xin lỗi, chúng ta có thể quên vòng đó không? "
Scott Sauyet

Câu trả lời:


10

Đánh giá

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]

Thắng được tất cả các bot đã gửi trước đó ngoại trừ (có thể) bot ngẫu nhiên (nhưng nó có thể có lợi thế, vì nó chọn D trong một trận hòa và D nên tối ưu) và chơi một trận hòa liên tục với chính chúng.


Đúng, đập tất cả mọi thứ.
SIGSTACKFAULT

Cào nó, Pattern Downloader đánh bại nó một chút.
SIGSTACKFAULT

7

Trạng thái cân bằng Nash

Bot này đã tham gia một lớp lý thuyết trò chơi ở trường đại học nhưng lười biếng và không đến lớp nơi họ bao quát các trò chơi lặp đi lặp lại. Vì vậy, anh chỉ chơi một trò chơi cân bằng hỗn hợp nash. Hóa ra 1/5 2/5 2/5 là NE hỗn hợp cho số tiền chi trả.

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

Lạm dụng liên tục Nash cân bằng

Bot này chọn một hoặc hai bài học từ anh trai lười biếng của mình. Vấn đề của anh trai lười biếng là anh ta đã không tận dụng các chiến lược cố định. Phiên bản này kiểm tra xem đối thủ có phải là người chơi liên tục hoặc Titfortat hay không và chơi tương ứng, nếu không, nó sẽ chơi trạng thái cân bằng nash thông thường.

Nhược điểm duy nhất của nó là trung bình 2,2 điểm mỗi lượt chơi với chính nó.

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 cần lấy các đối số ngay cả khi nó không sử dụng chúng, để phù hợp với nguyên mẫu hàm dự kiến.
Ray

Cảm ơn bạn đã sửa nó
Ofya

Ngắn hơn một chút: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"

Bot này sẽ luân phiên chọn DNDN trong khi TitForTat thay thế CDCD, cho mức tăng ròng trung bình 3 điểm mỗi vòng nếu tôi đọc đúng ma trận xuất chi. Tôi nghĩ rằng điều này có thể là tối ưu so với TitForTat. Rõ ràng nó có thể được cải thiện để phát hiện một đối thủ không phải là TFT và áp dụng các chiến lược khác, nhưng tôi chỉ nhắm đến tiền thưởng ban đầu.


6

Pattern Downloader

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

Bot này tìm kiếm các lần xuất hiện trước của trạng thái trò chơi gần đây để xem đối thủ phản ứng như thế nào với các lần xuất hiện đó, với ưu tiên cho các trận đấu mẫu dài hơn và các trận đấu gần đây hơn, sau đó chơi động tác sẽ "đánh bại" bước đi dự đoán của đối thủ. Có rất nhiều chỗ để nó thông minh hơn với tất cả dữ liệu mà nó theo dõi, nhưng tôi đã hết thời gian để làm việc với nó.


Khi bạn có thời gian, tâm trí cho cô ấy vượt qua tối ưu hóa? Nó dễ dàng là thời gian lớn nhất.
SIGSTACKFAULT

2
@Blacksilver Tôi vừa giảm chiều dài mẫu tối đa từ 100 xuống 10. Nó sẽ chạy gần như ngay lập tức nếu bạn đang chạy <200 vòng
Sparr

1
Có lẽ sử dụng một số tổng hợp cao (tức là 12) sẽ ghi điểm tốt hơn?
SIGSTACKFAULT

5

Ngọc

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'

Bắt đầu lạc quan, nhưng càng ngày càng cay đắng hơn khi đối thủ không chịu hợp tác. Rất nhiều hằng số ma thuật có thể có thể được điều chỉnh, nhưng điều này có lẽ sẽ không đủ tốt để biện minh cho thời gian.


5

Bộ quần áo

Điều này chạy một tập hợp các mô hình liên quan. Các mô hình riêng lẻ xem xét số lượng lịch sử khác nhau và có tùy chọn luôn chọn cách di chuyển sẽ tối ưu hóa chênh lệch thanh toán dự kiến ​​hoặc sẽ chọn ngẫu nhiên một nước đi theo tỷ lệ chênh lệch thanh toán dự kiến.

Mỗi thành viên của đoàn sau đó bỏ phiếu cho di chuyển ưa thích của họ. Họ nhận được số phiếu bằng với số tiền họ đã giành được nhiều hơn so với đối thủ (điều đó có nghĩa là những người mẫu khủng sẽ nhận được số phiếu tiêu cực). Bất kỳ di chuyển nào thắng phiếu bầu sau đó được chọn.

(Có lẽ họ nên chia phiếu bầu của họ cho các nước đi theo tỷ lệ với số tiền họ ủng hộ, nhưng tôi không quan tâm đủ để làm điều đó ngay bây giờ.)

Nó đánh bại mọi thứ được đăng cho đến nay ngoại trừ EvaluaterBot và Pattern Downloader. (Một chọi một, nó đánh bại EvaluaterBot và thua Pattern Downloader).

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"

Khung kiểm tra

Trong trường hợp bất kỳ ai khác thấy nó hữu ích, đây là một khung kiểm tra để xem xét các trận đấu riêng lẻ. Python2. Chỉ cần đặt tất cả các đối thủ mà bạn quan tâm vào đối thủ, và thay đổi các tham chiếu thành Đồng phục của riêng bạn.

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

Bộ điều khiển đã sẵn sàng, bạn không cần phải làm tất cả những điều đó ...
SIGSTACKFAULT

1
@Blacksilver Tôi nhận ra rằng ngay khi tôi sắp nộp. Nhưng cái này hoạt động trong các phiên bản trước 3.6 và cung cấp thông tin về các trận đấu riêng lẻ có thể giúp xác định các điểm yếu, vì vậy nó không hoàn toàn lãng phí thời gian.
Ray

Đủ công bằng; chạy ngay bây giờ Có lẽ tôi sẽ thêm các tùy chọn vào bộ điều khiển của mình để làm những việc tương tự.
SIGSTACKFAULT

"Nó đánh bại tất cả mọi thứ được đăng cho đến nay, ngoại trừ Bộ đồng phục và Mô hình" Tôi vinh dự :)
Sparr

@Sparr Rất tiếc. Điều đó được cho là nói EvaluaterBot và Pattern Downloader. Nhưng đó là khi so sánh tổng số điểm với toàn bộ lĩnh vực. Pattern Downloader vẫn là người duy nhất đánh bại điều này trong một trận đấu trực tiếp.
Ray

4

OldTitForTat

Người chơi trường cũ quá lười biếng để cập nhật cho các quy tắc mới.

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"

Nếu các bot đối lập khiếm khuyết hoặc là trung tính, chọn khiếm khuyết. Nếu không, nếu đây là lượt đầu tiên hoặc bot đối tác hợp tác, hãy chọn trung lập. Tôi không chắc nó sẽ hoạt động tốt như thế nào ...


Những gì thử / ngoại trừ?
SIGSTACKFAULT

1
@Blacksilver Tôi cho rằng nó phục vụ cùng mục đích với if(last):bot Grudger của bạn, phát hiện xem có vòng trước hay không.
Sản xuất ETH

à, tôi hiểu rồi. None in "ND"lỗi.
SIGSTACKFAULT

if last and last in "ND":quá phức tạp?
dùng253751

3

LastOptimalBot

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

Giả sử rằng bot đối lập sẽ luôn chơi cùng một động tác một lần nữa và chọn một bot có mức chi trả tốt nhất để chống lại nó.

Trung bình:

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 Có lẽ T4T sẽ làm tốt hơn như return last.
SIGSTACKFAULT

Tôi thích điều đó! Nếu TitForTat là return last, LOB sẽ đi 18-9 trên 6 vòng thay vì 13-10 trên 5 vòng mà hiện tại nó đang nhận được. Tôi nghĩ nó vẫn ổn như vậy - đừng lo lắng về việc tối ưu hóa các bot mẫu.
Spitemaster

return lasttôi nghĩ sẽ là một T4T tốt hơn cho thử thách này
Sparr

Chỉ cần cố gắng - if(last): return last; else: return "C"là tồi tệ hơn.
SIGSTACKFAULT

Đúng, nhưng như @Sparr đã nói, nó có thể phù hợp hơn. Tùy bạn, tôi cho là vậy.
Spitemaster

3

CopyCat

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

Sao chép chiêu cuối của đối thủ.
Tôi không hy vọng điều này sẽ làm tốt, nhưng chưa có ai thực hiện tác phẩm kinh điển này.


2

Cải tiến xúc xắc Dirichlet

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    

Đây là phiên bản cải tiến của Dirichlet Dice. Thay vì lấy phân phối đa cực dự kiến ​​từ phân phối Dirichlet, nó rút ra phân phối Đa thức ngẫu nhiên từ phân phối Dirichlet. Sau đó, thay vì vẽ từ Đa thức và đưa ra phản hồi tối ưu cho điều đó, nó đưa ra phản hồi dự kiến ​​tối ưu cho Đa thức đã cho bằng cách sử dụng các điểm. Vì vậy, tính ngẫu nhiên về cơ bản đã được chuyển từ bản vẽ Multinomial sang bản vẽ Dirichlet. Ngoài ra, các linh mục bây giờ bằng phẳng hơn, để khuyến khích khám phá.

Nó "được cải thiện" bởi vì giờ đây nó chiếm hệ thống điểm bằng cách đưa ra giá trị mong đợi tốt nhất so với xác suất, trong khi vẫn duy trì tính ngẫu nhiên của nó bằng cách tự vẽ xác suất. Trước đây, tôi đã cố gắng đơn giản thực hiện mức chi trả dự kiến ​​tốt nhất từ ​​các xác suất dự kiến, nhưng điều đó rất tệ vì nó bị kẹt và không khám phá đủ để cập nhật xúc xắc của nó. Ngoài ra, nó đã được dự đoán và khai thác nhiều hơn.


Trình gốc:

Xúc xắc 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

Về cơ bản, tôi cho rằng phản ứng của đối thủ với đầu ra cuối cùng của tôi là một biến đa biến (xúc xắc có trọng số), một biến cho mỗi đầu ra của tôi, do đó, có một con xúc xắc cho "C", một cho "N" và một cho "D" . Vì vậy, nếu cuộn cuối cùng của tôi là, ví dụ: "N" thì tôi cuộn "N-dice" để đoán phản ứng của họ sẽ là "N" của tôi. Tôi bắt đầu với Dirichlet trước khi cho rằng đối thủ của mình hơi "thông minh" (nhiều khả năng chơi một người có tỷ lệ hoàn trả tốt nhất so với lần quay cuối cùng của tôi, ít có khả năng chơi một người có tỷ lệ hoàn trả tệ nhất). Tôi tạo phân phối đa thức "dự kiến" từ Dirichlet thích hợp trước đó (đây là giá trị dự kiến ​​của phân phối xác suất trên trọng số xúc xắc của chúng). Tôi tung xúc xắc có trọng số của sản phẩm cuối cùng của mình,

Bắt đầu từ vòng thứ ba, tôi thực hiện cập nhật Bayes về Dirichlet thích hợp trước phản ứng cuối cùng của đối thủ với thứ tôi đã chơi hai vòng trước. Tôi đang cố gắng lặp đi lặp lại tìm hiểu trọng lượng xúc xắc của họ.

Tôi cũng có thể chỉ đơn giản chọn phản hồi với kết quả "mong đợi" tốt nhất một khi tạo ra xúc xắc, thay vì chỉ đơn giản là lắc xí ngầu và trả lời kết quả. Tuy nhiên, tôi muốn giữ sự ngẫu nhiên, để bot của tôi ít bị tổn thương hơn với những người cố gắng dự đoán một mô hình.


2

Kevin

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

Chọn sự lựa chọn tồi tệ nhất. Bot tồi tệ nhất được thực hiện.

Vô ích

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

Nó nhìn vào hai động tác cuối cùng được thực hiện bởi đối thủ và chọn nhiều nhất không được thực hiện khác, nó chọn một cái gì đó ngẫu nhiên. Có lẽ có một cách tốt hơn để làm điều này.


2

Trung bình lịch sử

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)

Nhìn vào lịch sử và tìm thấy hành động trung bình tốt nhất. Bắt đầu hợp tác xã.


Điều này có thể chạy nhanh hơn nếu nó không tính lại mức trung bình mỗi vòng.
Sparr

@Sparr đúng. Tôi đã chỉnh sửa nó để nó bây giờ.
MegaTom

1

Bình quân gia quyền

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"

Hành vi của đối thủ được mô hình hóa thành một tam giác vuông với các góc cho CND tương ứng là 0,0 0,1 1,0. Mỗi lần di chuyển của đối thủ sẽ dịch chuyển điểm trong tam giác đó về góc đó và chúng tôi chơi để đánh bại di chuyển được chỉ định bởi điểm đó (với C được cho một lát tam giác nhỏ có thể điều chỉnh). Về lý thuyết, tôi muốn điều này có bộ nhớ dài hơn với trọng lượng lớn hơn so với các lần di chuyển trước đó, nhưng trên thực tế, meta hiện tại ủng hộ các bot thay đổi nhanh chóng, vì vậy điều này phá hủy thành xấp xỉ LastOptimalBot chống lại hầu hết kẻ thù. Đăng bài cho hậu thế; có lẽ ai đó sẽ được truyền cảm hứng.


1

Chương trình

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

Cố gắng tìm một mô hình trong các bước di chuyển của đối thủ, giả sử họ cũng đang theo dõi bước đi cuối cùng của chúng tôi.


1

Bắt tay

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]

Nhận ra khi nó chơi với chính nó, sau đó hợp tác. Mặt khác, bắt chước LastOptimalBot có vẻ như là chiến lược một dòng tốt nhất. Thực hiện kém hơn LastOptimalBot, bằng một tỷ lệ nghịch với số vòng. Rõ ràng sẽ làm tốt hơn nếu có nhiều bản sao của nó trong lĩnh vực này * ho ** nháy mắt *.


Chỉ cần gửi một vài bản sao có hành vi không bắt tay khác nhau.
SIGSTACKFAULT

Điều đó có vẻ khai thác-y. Tôi có thể gửi một bản sao như vậy cho mọi hành vi đơn giản được trình bày ở đây.
Sparr

Tôi đã thêm một điều khoản bổ sung nói rằng bạn chỉ có thể gửi tối đa năm bot.
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

Bot này sử dụng thuật toán của LastOptimalBot miễn là nó chiến thắng. Tuy nhiên, nếu bot khác bắt đầu dự đoán nó, tuy nhiên, nó sẽ bắt đầu chơi bất cứ động tác nào mà đối thủ của nó đã chơi lần cuối (đó là động tác đánh bại bước di chuyển sẽ đánh bại LastOptimalBot). Nó quay vòng qua các chuyển vị đơn giản của các thuật toán đó miễn là nó tiếp tục bị mất (hoặc khi nó bị nhàm chán bằng cách vẽ nhiều).

Thành thật mà nói, tôi ngạc nhiên rằng LastOptimalBot đang ngồi ở vị trí thứ 5 khi tôi đăng bài này. Tôi khá chắc chắn điều này sẽ làm tốt hơn, giả sử rằng tôi đã viết con trăn này một cách chính xác.


0

HandshakePotypeMatch

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

Tại sao mô hình phù hợp với chính mình? Bắt tay và hợp tác đi.


import PatternFinderlà gian lận trong sách của tôi.
SIGSTACKFAULT

@Blacksilver Nó được thực hiện mọi lúc trong KOTH. Không khác gì sao chép mã trong câu trả lời hiện có và sử dụng nó. Robot Roulette: Các trò cá cược có tính đặt cược cao đã xảy ra ở mọi nơi đến mức các bot sẽ phát hiện ra nếu mã của họ bị đối thủ gọi và phá hoại sự trở lại.
Draco18

Được rồi. GẠCH
SIGSTACKFAULT

Tôi sẽ làm khủng hoảng vào ngày mai.
SIGSTACKFAULT

Đây là một ví dụ hoàn hảo về việc sử dụng mã của các bot khác. Thông thường nó nói đến "anh chàng đó đã làm ra một số phép toán khó, tôi muốn kết quả của anh ta trong những điều kiện này." ( Mục nhập của riêng tôi đã làm điều đó cho hiệu quả khá tốt; UpYours bị bắn phân tán hơn trong cách tiếp cận của nó).
Draco18

0

Mã hóa cứng

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]

Chỉ cần chơi một chuỗi các bước di chuyển được mã hóa tối ưu để đánh bại một số bot xác định hàng đầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.