죄수의 트렐 레마


19

과제 상태 : 열기

내가 봇을 잃어 버렸다면 의견을 말하거나 PR을 열거 나 그렇지 않으면 나에게 소리 지른다.


죄수의 딜레마 ... 세 가지 선택. 미쳤어?

여기에 지불 행렬이 있습니다. 왼쪽의 플레이어 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

지불 매트릭스는 두 플레이어가 항상 협력하는 것이 가장 좋도록 설계되었지만 일반적으로 중립 또는 결함을 선택하여 얻을 수 있습니다.

다음은 (경쟁적인) 예제 봇입니다.

# 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 개의 보상 현상금이 있습니다.

사양

  • 모든 봇은 [편집 됨] 라운드에서 자신을 포함하여 다른 모든 봇 (1 대 1)을 플레이합니다.
  • 표준 허점은 허용되지 않습니다.
  • 수업 이외의 다른 손잡은 셰 나니 가인을 엉망으로 만들지 마십시오.
  • 최대 5 개의 봇을 제출할 수 있습니다.
  • 예, 악수를 구현할 수 있습니다.
  • 이외의 모든 응답은 C, N또는 D자동으로 이동합니다 N.
  • 그들이하는 모든 게임에서 각 봇의 점수는 합산됩니다.

제어 장치

검사!

다른 언어

누군가가 필요하면 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
이 언어에 구애받지 않거나 더 넓게 만들면 더 많은 항목을 얻을 수 있습니다. 프로세스를 생성하고 텍스트 명령을 보내 텍스트 응답을 다시 얻는 래퍼 파이썬 클래스를 가질 수 있습니다.
Sparr

1
끝난. 이것은 한 달 동안 샌드 박스에있었습니다!
SIGSTACKFAULT

2
당신이 main.py의 대부분을 포장하는 경우 while len(botlist) > 1:botlist.remove(lowest_scoring_bot)루프의 하단에, 당신은 흥미로운 결과와 함께 제거 대회를 얻을.
Sparr

1
언젠가이 버전의 다른 버전은 마지막 이동이 아닌 전체 상호 작용 기록을 전달할 수 있습니다. 사용자 코드를 약간 단순화하지만 많이 변경되지는 않습니다. 그러나 그것은 시간이 지남에 따라 시끄러운 통신 채널과 같은 확장을 허용 할 것입니다. 죄송합니다, 그 라운드를 잊을 수 있습니까? "
Scott Sauyet

답변:


10

평가자 봇

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를 선택하고 D가 최적이어야하므로 이점이있을 수 있음을 제외하고 이전에 제출 된 모든 봇에 대해 이기고 자신에 대해 지속적인 추첨을합니다.


네, 모든 것을 이깁니다.
SIGSTACKFAULT

스크래치, PatternFinder가 조금 이겼습니다.
SIGSTACKFAULT

7

내쉬 균형

이 봇은 대학에서 게임 이론 수업을 들었지만 게으 르며 반복되는 게임을 다루는 수업에는 가지 않았습니다. 그래서 그는 단일 게임 혼합 내쉬 평형을 재생합니다. 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" 

지속적인 학대 내쉬 균형

이 봇은 게으른 형제에게서 교훈을 얻었습니다. 그의 게으른 형제의 문제는 고정 전략을 이용하지 않았다는 것입니다. 이 버전은 상대방이 일정한 플레이어인지 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는 예상 함수 프로토 타입에 맞게 인수를 사용하지 않더라도 인수를 사용해야합니다.
Ray

감사합니다
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"

이 봇은 지불 행렬을 올바르게 읽은 경우 TitForTat가 CDCD를 교체하는 동안 라운드 당 3 포인트의 평균 순 이득을 얻기 위해 DNDN을 번갈아 선택합니다. 나는 이것이 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"

테스트 프레임 워크

다른 사람이 유용하다고 생각되면 여기 개별 매치업을보기위한 테스트 프레임 워크가 있습니다. 파이썬 2. 관심있는 모든 상대를 enemys.py에 넣고 Ensemble에 대한 참조를 자신의 것으로 변경하십시오.

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 이전 버전에서 작동하며 약점을 식별하는 데 도움이되는 개별 매치업에 대한 정보를 제공하므로 시간 낭비가 아닙니다.
Ray

그럴 수 있지; 지금 실행 중입니다. 비슷한 작업을 수행하기 위해 컨트롤러에 옵션을 추가 할 것입니다.
SIGSTACKFAULT

"앙상블과 PatternFinder를 제외하고 지금까지 게시 된 모든 내용을 능가합니다"영광입니다 :)
Sparr

@Sparr 죄송합니다. 그것은 EvaluaterBot과 PatternFinder라고 말합니다. 그러나 전체 점수와 전체 점수를 비교할 때입니다. PatternFinder는 직접 경기에서 이길 수있는 유일한 제품입니다.
Ray

4

OldTitForTat

올드 스쿨 플레이어는 새로운 규칙을 업데이트하기에는 너무 게으르다.

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

3

네버 쿠프

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

반대쪽 봇 결함 또는 중립 인 경우 결함을 선택하십시오. 그렇지 않으면 이것이 첫 번째 턴이거나 상대 봇이 협력하는 경우 중립을 선택하십시오. 이것이 얼마나 잘 작동하는지 잘 모르겠습니다 ...


시도 / 제외 란 무엇입니까?
SIGSTACKFAULT

1
@Blacksilver 나는 그것이 if(last):이전 라운드가 있었는지 여부를 감지하여 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

돈. 아마도 T4T는 다음과 같이 더 나을 것 return last입니다.
SIGSTACKFAULT

나는 그것을 원합니다! TitForTat이 인 return last경우 LOB는 현재 받고있는 5 라운드에서 13-10이 아니라 6 라운드에서 18-9로갑니다. 예제 봇 최적화에 대해 걱정하지 마십시오.
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

디리 클릿 주사위 개선

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 분포에서 임의로 다항 분포를 그립니다. 그런 다음 다항식에서 그림을 그리고 최적의 응답을 제공하는 대신 점을 사용하여 주어진 다항식에 최적의 예상 응답을 제공합니다. 따라서 무작위성은 본질적으로 다항식 추첨에서 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

기본적으로 나는 마지막 출력에 대한 상대방의 반응이 다항식 변수 (가중 주사위)이며 각 출력마다 하나씩 있으므로 "C", "N", "D"에 대한 주사위가 있다고 가정합니다. . 마지막 롤이 예를 들어 "N"인 경우 "N-dice"를 굴려서 "N"에 대한 응답이 무엇인지 추측합니다. 나는 상대방이 다소 "스마트"하다고 가정하기 전에 Dirichlet으로 시작합니다 (마지막 롤에 대해 가장 좋은 대가를 가진 사람을 플레이 할 가능성이 가장 높고 가장 나쁜 대가를 가진 사람을 플레이 할 가능성이 가장 낮습니다). 적절한 Dirichlet에서 "예상 된"다항 분포를 먼저 생성합니다 (이는 주사위 무게에 대한 확률 분포의 예상 값입니다). 마지막 출력의 가중 주사위를 굴립니다.

3 라운드에서 시작하여, 나는 2 라운드 전에 내가 한 것에 대한 상대의 마지막 응답 이전에 적절한 Dirichlet의 Bayesian 업데이트를 수행합니다. 주사위 가중치를 반복적으로 배우려고합니다.

주사위를 굴려서 결과에 응답하는 대신 주사위를 생성 한 후 최고의 "예상"결과로 응답을 간단히 선택할 수있었습니다. 그러나 무작위성을 유지하고 싶었 기 때문에 내 봇은 패턴을 예측하려는 봇에 덜 취약합니다.


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"

상대방의 행동은 각각 0,0 0,1 1,0의 CND 코너를 가진 직각 삼각형으로 모델링됩니다. 각 상대 이동은 해당 삼각형 내에서 해당 모서리쪽으로 점을 이동하고 점으로 표시된 이동을 이길 수 있도록 재생합니다 (C에 조정 가능한 작은 삼각형 조각이 제공됨). 이론적으로 나는 이것이 이전 움직임에 더 많은 무게를 가진 더 긴 메모리를 갖기를 원했지만 실제로 현재의 메타는 빠르게 변화하는 봇을 선호하므로 이것은 대부분의 적에 대한 LastOptimalBot의 근사치로 전개됩니다. 후손에 대한 게시; 아마 누군가가 영감을받을 것입니다.


1

테트라 그램

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

그것은 악용으로 보입니다. 여기에 표시된 모든 간단한 동작마다 하나의 복제본을 제출할 수 있습니다.
Sparr

최대 5 개의 봇만 제출할 수 있다는 추가 조항을 추가했습니다.
SIGSTACKFAULT 2

1

최적화 최적 봇

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을이기는 이동을이기는 이동)을 시작합니다. 이 알고리즘은 계속 손실되는 한 (또는 많이 그려서 지루할 때) 해당 알고리즘의 간단한 전치를 순환합니다.

솔직히 LastOptimalBot이 5 위에 올랐다는 것에 놀랐습니다. 이 파이썬을 올바르게 작성했다고 가정하면 이것이 더 잘 될 것이라고 확신합니다.


0

악수 패턴

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

좋아, 그럼. 틸.
SIGSTACKFAULT

내일 바삭 바삭 할게요
SIGSTACKFAULT

다음은 다른 봇 코드를 사용하는 완벽한 예입니다. 일반적으로 "그 사람은 까다로운 수학을 수행했습니다. 이러한 조건 하에서 그의 결과를 원합니다." (내 자신의 입장 은 그 효과를 꽤 좋게 만들었습니다.
Draco18s

0

하드 코드

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.