KOTH-로드 된 RPS


12

공모전 개설-2017 년 8 월 10 일 업데이트

2017 년 6 월 5 일에 나는 (최고의 답변으로 유지 될) 승자를 선언했지만 새로운 봇을 찢고 결과를 업데이트 할 것입니다.

6 월 5 일 결과

축하합니다 사용자

동점이 없기 때문에 나는이긴 경기의 % 만 보여줍니다.

Statistician2- 95.7 %
Fitter- 89.1 %
Nash- 83.9 %
Weigher- 79.9 %
ExpectedBayes- 76.4 %
AntiRepeater- 72.1 %
Yggdrasil- 65.0 %
AntiGreedy- 64.1 %
Reactor- 59.9 %
NotHungry- 57.3 %
NashBot- 55.1 %
Blodsocer- 48.6 %
BestOfBothWorlds- 48.4 %
GoodWinning- 43.9 %
Rockstar- 40.5 %
ArtsyChild- 40.4 %
Assassin- 38.1 %
WeightedRandom-37.7 %
Ensemble-37.4 %
UseOpponents-36.4 %
GreedyPsychologist-36.3 %
TheMessenger-33.9 %
Copycat-31.4 %
Greedy-28.3 %
SomewhatHungry-27.6 %
AntiAntiGreedy-21.0 %
Cycler-20.3 %
Swap-19.8 %
RandomBot-16.2 %

나는 각 쌍의 결과 그리드와 구글 시트를 만들어 : https://docs.google.com/spreadsheets/d/1KrMvcvWMkK-h1Ee50w0gWLh_L6rCFOgLhTN_QlEXHyk/edit?usp=sharing


페트리 딜레마 덕분에 나는이 언덕의 왕을 다룰 수 있다는 것을 알게되었습니다.

게임

이 게임은 트위스트와 함께 간단한 "바위 가위"입니다 : 경기 중 승리 할 때마다 얻는 포인트 (R, P 또는 S가로드 됨).

  • 종이는 바위를 이긴다
  • 가위는 종이를 이긴다
  • 가위 바위 우승

승자는 자신의 플레이로드만큼 많은 포인트를 얻습니다.

패자는 자신의 플레이에 가해지는 부하를 1만큼 증가시킵니다.

동점 인 경우, 각 플레이어는 자신의 플레이로드를 0.5만큼 증가시킵니다.

100 회 플레이 한 후 더 많은 포인트를 얻은 사람이 승자가됩니다.

예 : P1에는 하중 [10,11,12] (록, 종이, 가위) 및 P2 [7,8,9]가 있습니다. P1이 R을, P2가 P를, P2가 승리하고 8 점을 얻습니다. P1 부하는 [11,11,12]가되고 P2 부하는 동일하게 유지됩니다.

도전 사양

귀하의 프로그램은 Python으로 작성해야합니다 (죄송합니다, 달리 처리하는 방법을 모르겠습니다). 이러한 각 변수를 각 실행에 대한 인수로 사용하는 함수를 작성해야합니다.

my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history

points -현재 포인트 (당신과 당신의 OPP)

loaded-부하가있는 배열 (순서 RPS) (당신과 당신의 OPP)

history-모든 플레이가있는 문자열, 마지막 캐릭터는 마지막 플레이입니다 (당신과 당신의 OPP)

당신은 반환해야합니다 "R", "P"또는 "S". 다른 것을 반환하면 자동으로 경기에서 패합니다.

규칙

내장 기능은 변경할 수 없습니다.

테스팅

https://github.com/Masclins/LoadedRPS : 코드와 모든 봇이 경쟁하는 Git을 계속 업데이트합니다

심사

우승자는 1000 라운드 풀 로빈 후 가장 많은 승리를 한 사람을 선택하여 결정됩니다. 동점으로 묶인 경기에 의해 동점이됩니다. 무작위성이 많을 것으로 예상되므로 1000 개가 1 개가 아닌 1 회가됩니다.

최대 5 개의 봇을 제출할 수 있습니다.

에 경연 종료 7월 6 월 4 (즉 내가 어떤 대답을 받아 들일 겁니다 마지막 날이 될 것입니다), 그리고에 7월 6 월 5 나는 (전에 advancemnt를 게시하려고 할 수도 있습니다) 최종 stadings을 게시합니다.


이것이 첫 번째 KOTH이기 때문에 각 봇과의 경기 수와 같은 개선을 위해 아무것도 바꾸지 않기 위해 100 % 열렸습니다.

1000 개의 일치 항목으로 편집했는데, 실제로 임의성이 포함되어 있기 때문입니다.


무작위 봇으로, 당신은 실제로 여러 라운드의 여러 게임을 만들고 싶어
Destructible Lemon

@DestructibleLemon 각 봇이 한 번이 아닌 다른 봇에 대해 세 번 재생되도록 생각했습니다. 당신이 비슷하게 생각하면 그렇게 할 것입니다.
Masclins

1
(. 정말이 경기의 공정한 금액과 가능성이없는 것이 무참히 얻을 수있는 내 봇을 참조 일부 probabilites 정말 여러 경기를 통해 확장 않기 때문에, 공정한 큰 번호가 필요하지만)
파괴 가능한 레몬

1
내 질문이 @AlbertMasclans를 실행하는 데 도움이되어 기쁩니다!
그리폰

2
@AlbertMasclans 전체 테스트 스크립트 ( runcode및 포함 bots) 를 게시 할 수 있습니까 ?
CalculatorFeline

답변:


8

통계 학자 (더 이상 플레이하지 않음)

import random
import collections

R, P, S = moves = range(3)
move_idx = {"R": R, "P": P, "S": S}
name = "RPS"
beat = (P, S, R)
beaten = (S, R, P)

def react(_0, _1, _2, _3, _4, opp_history):
    if not opp_history:
        return random.randrange(0, 3)
    return beat[opp_history[-1]]

def anti_react(_0, _1, _2, _3, _4, opp_history):
    if not opp_history:
        return random.randrange(0, 3)
    return beaten[opp_history[-1]]

def random_max(scores):
    scores = [s + random.normalvariate(0, 1) for s in scores]
    return scores.index(max(scores))

def greedy_margin(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    scores = [my_loaded[move] - opp_loaded[beat[move]] for move in moves]
    return random_max(scores)

def anti_greedy(my_points, opp_pints, my_loaded, opp_loaded, my_history, opp_history):
    scores = [-my_loaded[move] for move in moves]
    return random_max(scores)

def recent_stats(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    opp_history = opp_history[-10:-1]
    counts = collections.Counter(opp_history)
    scores = [(counts[beaten[move]] + 1) * my_loaded[move] - 
              (counts[beat[move]] + 1) * opp_loaded[move] for move in moves]
    return random_max(scores)

def statistician(_0, _1, _2, _3, my_history, opp_history):
    m1 = []
    o1 = []
    my_loaded = [0] * 3
    opp_loaded = [0] * 3
    my_points = 0
    opp_points = 0
    strategies = [react, anti_react, greedy_margin, anti_greedy, recent_stats]
    strategy_scores = [0 for _ in strategies]
    for i, (mx, ox) in enumerate(zip(my_history, opp_history)):
        mx = move_idx[mx]
        ox = move_idx[ox]
        for j, strategy in enumerate(strategies):
            strategy_scores[j] *= 0.98
            move = strategy(my_points, opp_points, my_loaded, opp_loaded, m1, o1)
            if move == beat[ox]:
                strategy_scores[j] += my_loaded[move]
            elif move == beaten[ox]:
                strategy_scores[j] -= opp_loaded[ox]
        m1.append(mx)
        o1.append(ox)
        if mx == beat[ox]:
            opp_loaded[ox] += 1
            my_points += my_loaded[mx]
        elif mx == beaten[ox]:
            my_loaded[mx] += 1
            opp_points += opp_loaded[ox]
        else:
            my_loaded[mx] += 0.5
            opp_loaded[ox] += 0.5
    strategy = strategies[random_max(strategy_scores)]
    return name[strategy(my_points, opp_points, my_loaded, opp_loaded, m1, o1)]

과거의 예상 성능을 기반으로 몇 가지 간단한 전략 간 전환

통계 학자 2

import random
import collections
import numpy as np

R, P, S = moves = range(3)
move_idx = {"R": R, "P": P, "S": S}
names = "RPS"
beat = (P, S, R)
beaten = (S, R, P)

def react(my_loaded, opp_loaded, my_history, opp_history):
    if not opp_history:
        return random.randrange(0, 3)
    counts = [0, 0, 0]
    counts[beat[opp_history[-1]]] += 1
    return counts

def random_max(scores):
    scores = [s + random.normalvariate(0, 1) for s in scores]
    return scores.index(max(scores))

def argmax(scores):
    m = max(scores)
    return [s == m for s in scores]

def greedy_margin(my_loaded, opp_loaded, my_history, opp_history):
    scores = [my_loaded[move] - opp_loaded[beat[move]] for move in moves]
    return argmax(scores)

recent_counts = None

def best_move(counts, my_loaded, opp_loaded):
    scores = [(counts[beaten[move]] + 0.5) * my_loaded[move] - 
              (counts[beat[move]] + 0.5) * opp_loaded[move] for move in moves]
    return argmax(scores)

def recent_stats(my_loaded, opp_loaded, my_history, opp_history):
    if len(opp_history) >= 10:
        recent_counts[opp_history[-10]] -= 1
    recent_counts[opp_history[-1]] += 1
    return best_move(recent_counts, my_loaded, opp_loaded)

order2_counts = None

def order2(my_loaded, opp_loaded, my_history, opp_history):
    if len(my_history) >= 2:
        base0 = 9 * my_history[-2] + 3 * opp_history[-2]
        order2_counts[base0 + opp_history[-1]] += 1
    base1 = 9 * my_history[-1] + 3 * opp_history[-1]
    counts = [order2_counts[base1 + move] for move in moves]
    return best_move(counts, my_loaded, opp_loaded)

def nash(my_loaded, opp_loaded, my_history, opp_history):
    third = 1.0 / 3
    p = np.full(3, third)
    q = np.full(3, third)
    u = np.array(my_loaded)
    v = np.array(opp_loaded)
    m0 = np.zeros(3)
    m1 = np.zeros(3)
    lr = 0.2
    for _ in range(10):
        de0 = u * np.roll(q, 1) - np.roll(v * q, 2)
        de1 = v * np.roll(p, 1) - np.roll(u * p, 2)
        m0 = 0.9 * m0 + 0.1 * de0
        m1 = 0.9 * m1 + 0.1 * de1
        p += lr * m0
        q += lr * m1
        p[p < 0] = 0
        q[q < 0] = 0
        tp, tq = np.sum(p), np.sum(q)
        if tp == 0 or tq == 0:
            return np.full(3, third)
        p /= tp
        q /= tq
        lr *= 0.9
    return p

strategies = [react, greedy_margin, recent_stats, order2, nash]

predictions = strategy_scores = mh = oh = None

def statistician2func(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    global strategy_scores, history, recent_counts, mh, oh, predictions, order2_counts
    if not opp_history:
        strategy_scores = [0 for _ in strategies]
        recent_counts = collections.Counter()
        order2_counts = collections.Counter()
        mh, oh = [], []
        predictions = None
        return random.choice(names)
    my_move = move_idx[my_history[-1]]
    opp_move = move_idx[opp_history[-1]]
    if predictions is not None:
        for j, p in enumerate(predictions):
            good = beat[opp_move]
            bad = beaten[opp_move]
            strategy_scores[j] += (my_loaded[good] * p[good] - opp_loaded[opp_move] * p[bad]) / sum(p)
    mh.append(my_move)
    oh.append(opp_move)
    predictions = [strategy(my_loaded, opp_loaded, mh, oh) for strategy in strategies]
    strategy = random_max(strategy_scores)
    p = predictions[strategy]
    r = random.random()
    for i, pi in enumerate(p):
        r -= pi
        if r <= 0:
            break
    return names[i]

내쉬

import numpy as np
import random

def nashfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    third = 1.0 / 3
    p = np.full(3, third)
    q = np.full(3, third)
    u = np.array(my_loaded)
    v = np.array(opp_loaded)
    m0 = np.zeros(3)
    m1 = np.zeros(3)
    lr = 0.2
    for _ in range(10):
        de0 = u * np.roll(q, 1) - np.roll(v * q, 2)
        de1 = v * np.roll(p, 1) - np.roll(u * p, 2)
        m0 = 0.9 * m0 + 0.1 * de0
        m1 = 0.9 * m1 + 0.1 * de1
        p += lr * m0
        q += lr * m1
        p[p < 0] = 0
        q[q < 0] = 0
        tp, tq = np.sum(p), np.sum(q)
        if tp == 0 or tq == 0:
            return random.choice("RPS")
        p /= tp
        q /= tq
        lr *= 0.9
    r = random.random()
    for i, pi in enumerate(p):
        r -= pi
        if r <= 0:
            break
    return "RPS"[i]

경사 하강에 의해 근사 내쉬 평형을 계산합니다.


1
나는이 접근법을 정말로 좋아하며 왜 당신이 라운드 사이에 상태를 유지할 수 있기를 원하는지 이해할 수 있습니다. 제출 횟수가 주어지면 변경하는 것이 큰 문제라고 생각하지만. 추가 과제 (이 작업이 완료 될 때 수행 할 것으로 예상 됨)에 대해이를 고려하겠습니다.
Masclins

5

계량기

코드를 실험하는 동안 추론을 잃었지만 기본 아이디어는 일부 가중치를 사용하여 마지막 3 번의 움직임으로 상대의 이동 확률을 추정하고 부하에 따라 다른 가중치로 곱하는 것입니다. 어떻게 든 사용할 수 있다고 생각 my_loaded했지만 방법을 결정할 수 없으므로 생략했습니다.

def weigher(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    idx = {"R": 0, "P": 1, "S": 2}
    sc = [0, 0, 0]
    for i, m in enumerate(reversed(opp_history[-3:])):
        sc[idx[m]] += (1 / (1 + i))

    for i in range(3):
        sc[i] *= (opp_loaded[i] ** 2)

    return "PSR"[sc.index(max(sc))]

사탄

아마도 부정 행위이기 때문에 테스트 기능에 대한 몇 가지 가정 (스택 프레임의 변수에서 상대의 기능을 가져야 함)으로 인해 실격 처리 될 수 있지만 기술적으로 현재 규칙을 위반하지는 않습니다. 재정의하거나 무언가를 다시 쓰십시오. 단순히 흑 마법을 사용하여 상대 기능을 실행하여 자신의 차례가 무엇을했는지 확인합니다. 그것은 무작위성을 다룰 수 없지만 결정 론적 봇은 사탄을 물리 칠 기회가 없습니다.

def satan(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    import inspect, types
    f = inspect.currentframe()
    s = f.f_code.co_name
    try:
        for v in f.f_back.f_locals.values():
            if isinstance(v, types.FunctionType) and v.__name__ != s:
                try:
                    return "PSR"[{"R": 0, "P": 1, "S": 2}[
                        v(opp_points, my_points, opp_loaded, my_loaded, opp_history, my_history)]]
                except:
                    continue
    finally:
        del f

의심의 여지없이 Simplicity-Results
Masclins의

그건 그렇고, my_loaded당신은 마지막 움직임에 대해 잃을 움직임을 가치있게 가중치를 추가 할 수 있습니다. 그것은 상대방이 당신이 한 것과 비슷한 일을 할 것이라고 가정하는 것과 같습니다. 따라서 당신이 계속 똑같이 플레이한다고 가정하면 그를 처벌합니다. 뭔가 같은 :for i, m in enumerate(reversed(my_history[-3:])): sc[(idx[m]+1)%3] += (K / (1 + i))
Masclins

@AlbertMasclans는 다른 솔루션을 추가했습니다
표시 이름

1
나는 사탄 사람을 정말 좋아합니다. 그러나 당신이 말했듯이, 나는 그것이 자격이 없어야한다고 믿습니다 : 명백한 규칙을 위반하지 않더라도 분명히 게임의 정신에 위배됩니다. 그래도, 당신의 아이디어를 축하합니다!
Masclins

4

맞추는 사람

이 봇은 패턴을 개선하고 이코노미스트와 융합시킵니다 (패턴과 이코노미스트는 더 이상 참여하지 않습니다)

패턴의 개선은 봇이 두 가지 종류의 패턴을 찾는다는 것입니다. 상대는 마지막 경기에 반응하고 상대방은 마지막 경기에 반응합니다. 그런 다음 두 예측을 평가하여 가장 적합한 예측을 사용합니다.

이 패턴에서 봇은 이제 R, P 및 S에 대한 확률을 가지게되었습니다. 봇은 그 값과 각 플레이의 예상 값 (이코노미스트가 한 것처럼)을 고려하여 가장 가치있는 것을 재생합니다.

import random
import numpy as np
def fitterfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
        t = len(opp_history)
        RPS = ["R","P","S"]
        if t <= 2:
                return RPS[t]
        elif t == 3:
                return random.choice(RPS)

        def n(c): return RPS.index(c)

        total_me = np.zeros(shape=(3,3))
        total_opp= np.zeros(shape=(3,3))
        p_me = np.array([[1/3]*3]*3)
        p_opp = np.array([[1/3]*3]*3)

        for i in range(1, t):
                total_me[n(my_history[i-1]), n(opp_history[i])] += 1
                total_opp[n(opp_history[i-1]), n(opp_history[i])] += 1
        for i in range(3):
                if np.sum(total_me[i,:]) != 0:
                        p_me[i,:] = total_me[i,:] / np.sum(total_me[i,:])
                if np.sum(total_opp[i,:]) != 0:
                        p_opp[i,:] = total_opp[i,:] / np.sum(total_opp[i,:])

        error_me = 0
        error_opp = 0

        for i in range(1, t):
                diff = 1 - p_me[n(my_history[i-1]), n(opp_history[i])]
                error_me += diff * diff
                diff = 1 - p_opp[n(opp_history[i-1]), n(opp_history[i])]
                error_opp += diff * diff

        if error_me < error_opp:
                p = p_me[n(my_history[-1]),:]
        else:
                p = p_opp[n(opp_history[-1]),:]


# From here, right now I weight values, though not 100% is the best idea, I leave the alternative in case I'd feel like changing it
        value = [(p[2]*my_loaded[0] - p[1]*opp_loaded[1], "R"), (p[0]*my_loaded[1] - p[2]*opp_loaded[2], "P"), (p[1]*my_loaded[2] - p[0]*opp_loaded[0], "S")]
        value.sort()

        if value[-1][0] > value[-2][0]:
                return value[-1][1]
        elif value[-1][0] > value[-3][0]:
                return random.choice([value[-1][1], value[-2][1]])
        else:
                return random.choice(RPS)

#       idx = p.tolist().index(max(p))
#       return ["P", "S", "R"][idx]

여기에 두 개의 오래된 코드가 있습니다

패턴 (더 이상 재생되지 않음)

패턴은 상대에서 패턴을 찾으려고합니다. 상대가 자신이 마지막으로 한 경기 (후자의 경기에 더 많은 비중을 둔) 이후에 한 경기를 본다. 이를 통해 상대가 무엇을하는지 추측하고 그에 대응합니다.

import random
import numpy as np
def patternfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
        if len(opp_history) == 0:
                return random.choice(["R","P","S"])
        elif len(opp_history) == 1:
                if opp_history == "R":
                        return "P"
                elif opp_history == "P":
                        return "S"
                elif opp_history == "S":
                        return "R"

        p = np.array([1/3]*3)
        c = opp_history[-1]
        for i in range(1, len(opp_history)):
                c0 = opp_history[i-1]
                c1 = opp_history[i]
                if c0 == c:
                        p *= .9
                        if c1 == "R":
                                p[0] += .1
                        elif c1 == "P":
                                p[1] += .1
                        elif c1 == "S":
                                p[2] += .1

        idx = p.tolist().index(max(p))
        return ["P", "S", "R"][idx]

경제학자 (더 이상 연주하지 않음)

이코노미스트는 다음을 수행합니다. 상대가 자신이 지난 9 턴을했던 것을 보면서 각 플레이의 확률을 추측합니다. 그것으로부터, 각 놀이의 기대되는 이익을 계산하고 가장 기대되는 가치를 가진 것과 함께 간다.

import random
def economistfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
        if len(opp_history) == 0:
                return random.choice(["R","P","S"])
        if len(opp_history) > 9:
                opp_history = opp_history[-10:-1]
        p = [opp_history.count("R"), opp_history.count("P"), opp_history.count("S")]

        value = [(p[2]*my_loaded[0] - p[1]*opp_loaded[1], "R"), (p[0]*my_loaded[1] - p[2]*opp_loaded[2], "P"), (p[1]*my_loaded[2] - p[0]*opp_loaded[0], "S")]
        value.sort()

        if value[-1][0] > value[-2][0]:
                return value[-1][1]
        elif value[-1][0] > value[-3][0]:
                return random.choice([value[-1][1], value[-2][1]])
        else:
                return random.choice(["R","P","S"])

4

이그드라실

"Yggdrasil"이라는 이름이 게임 트리에서 앞서 있기 때문입니다. 이 봇은 상대방에 대한 예측을 수행하지 않으며, 현재와 미래의 이익을 균형있게하여 주어진 통계적 이점을 유지하려고 시도합니다. 대략 이상적인 혼합 전략을 계산하고 해당 가중치를 사용하여 임의로 선택된 이동을 반환합니다. 이 봇이 완벽했다면 (상태 평가 기능이 꽤 나쁘고 훨씬 멀리 보이지 않기 때문에 그렇지 않습니다),이 봇을 50 % 이상 이길 수는 없습니다. 이 봇이 실제로 얼마나 잘 수행 될지 모르겠습니다.

def yggdrasil(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    cache = {}
    def get(turn, ml, ol):
        key = str(turn) + str(ml) + str(ol)
        if not key in cache:
            cache[key] = State(turn, ml, ol)
        return cache[key]

    def wrand(opts):
        total = sum(abs(w) for c,w in opts.items())
        while True:
            r = random.uniform(0, total)
            for c, w in opts.items():
                r -= abs(w)
                if r < 0:
                    return c
            print("error",total,r)

    class State():
        turn = 0
        ml = [1,1,1]
        ol = [1,1,1]
        val = 0
        strat = [1/3, 1/3, 1/3]
        depth = -1
        R = 0
        P = 1
        S = 2
        eps = 0.0001
        maxturn = 1000

        def __init__(self, turn, ml, ol):
            self.turn = turn
            self.ml = ml
            self.ol = ol
        def calcval(self, depth):
            if depth <= self.depth:
                return self.val
            if turn >= 1000:
                return 0
            a = 0
            b = -self.ol[P]
            c = self.ml[R]
            d = self.ml[P]
            e = 0
            f = -self.ol[S]
            g = -self.ol[R]
            h = self.ml[S]
            i = 0
            if depth > 0:
                a += get(self.turn+1,[self.ml[R]+1,self.ml[P],self.ml[S]],[self.ol[R]+1,self.ol[P],self.ol[S]]).calcval(depth-1)
                b += get(self.turn+1,[self.ml[R]+2,self.ml[P],self.ml[S]],[self.ol[R],self.ol[P],self.ol[S]]).calcval(depth-1)
                c += get(self.turn+1,[self.ml[R],self.ml[P],self.ml[S]],[self.ol[R],self.ol[P],self.ol[S]+2]).calcval(depth-1)
                d += get(self.turn+1,[self.ml[R],self.ml[P],self.ml[S]],[self.ol[R]+2,self.ol[P],self.ol[S]]).calcval(depth-1)
                e += get(self.turn+1,[self.ml[R],self.ml[P]+1,self.ml[S]],[self.ol[R],self.ol[P]+1,self.ol[S]]).calcval(depth-1)
                f += get(self.turn+1,[self.ml[R],self.ml[P]+2,self.ml[S]],[self.ol[R],self.ol[P],self.ol[S]]).calcval(depth-1)
                g += get(self.turn+1,[self.ml[R],self.ml[P],self.ml[S]+2],[self.ol[R],self.ol[P],self.ol[S]]).calcval(depth-1)
                h += get(self.turn+1,[self.ml[R],self.ml[P],self.ml[S]],[self.ol[R],self.ol[P]+2,self.ol[S]]).calcval(depth-1)
                i += get(self.turn+1,[self.ml[R],self.ml[P],self.ml[S]+1],[self.ol[R],self.ol[P],self.ol[S]+1]).calcval(depth-1)
            self.val = -9223372036854775808
            for pr in range(0,7):
                for pp in range(0,7-pr):
                    ps = 6-pr-pp
                    thisval = min([pr*a+pp*d+ps*g,pr*b+pp*e+ps*h,pr*c+pp*f+ps*i])
                    if thisval > self.val:
                        self.strat = [pr,pp,ps]
                        self.val = thisval
            self.val /= 6


            if depth == 0:
                self.val *= min(self.val, self.maxturn - self.turn)
            return self.val

    turn = len(my_history)
    teststate = get(turn, [x * 2 for x in my_loaded], [x * 2 for x in opp_loaded])
    teststate.calcval(1)
    return wrand({"R":teststate.strat[R],"P":teststate.strat[P],"S":teststate.strat[S]})

코드를 이해하기
표시 이름

@SargeBorsch 완료
PhiNotPi

1
@PhiNotPi 시간 제한이 없다는 것을 알고 있지만 이그드라실은 각 상대에게 1 분 이상 걸립니다. 조금 최적화하는 것이 가능할까요?
Masclins

그래 너무 느려
표시 이름

상대 당 분당 @AlbertMasclans 상대에 대한 모든 게임의 총 1 분을 의미합니까? 또한 속도를 높이려고하지만 실제로 어떻게 해야할지 모르겠습니다.있는 그대로 1 움직입니다.
PhiNotPi

4

리피터 방지

from random import choice
def Antirepeaterfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    s = opp_history.count("S")
    r = opp_history.count("R")
    p = opp_history.count("P")

    if s>p and s>r:
        return "R"
    elif p>s and p>r:
        return "S"
    else:
        return "P"

첫 번째 턴에서 종이를 집어 들고, 그 후에 상대방이 가장 많이 한 것을 이길 수있는 것을 돌려줍니다.

모방

import random
def copycatfunc(I,dont,care,about,these,enmoves):
    if not enmoves:
        return random.choice(["R","P","S"])
    else:
        return enmoves[len(enmoves)-1]

상대를 마지막으로 이동하기 만하면됩니다.

안티 욕심

from random import choice
def antiantigreedy(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    if opp_loaded[0] > opp_loaded[1] and opp_loaded[0] > opp_loaded[2]:
        return "S"
    if opp_loaded[1] > opp_loaded[0] and opp_loaded[1] > opp_loaded[2]:
        return "R"
    if opp_loaded[2] > opp_loaded[0] and opp_loaded[2] > opp_loaded[1]:
        return "P"
    else:
        return choice(["R","P","S"])

상대방의 가장 무거운 선택에지는 것을 골라냅니다.

다소 배고파

from random import choice
def somewhathungryfunc(blah, blah2, load, blah3, blah4, blah5):
    if load[0] > load[1] and load[0] < load[2] or load[0] < load[1] and load[0] > load[2]:
        return "R"
    if load[1] > load[0] and load[1] < load[2] or load[1] < load[0] and load[1] > load[2]:
        return "P"
    if load[2] > load[1] and load[2] < load[0] or load[2] < load[1] and load[2] > load[0]:
        return "S"
    else:
        return choice(["R","P","S"])

3

메신저

def themessengerfunc (I,하지, 필요, 이것, 인수) : return "P"

락스타

데프 락스타 펑크 (I, do, not, need, these, arguments) : return "R"

암살자

def assassinfunc (I,하지, 필요, 이것, 인수) : return "S"

설명

이제이 봇은 완전히 바보라고 생각할 수 있습니다.

완전히 사실은 아니지만, 이들은 실제로 아이디어에 기반을두고 있으며, 엄청난 보너스를 모으고, 적의 실수를 저지르고 벽에 갇히게됩니다.

이제이 봇들은 욕심과 매우 유사하지만, 더 단순하고, 하나의 무기에 짐을 싣기 전까지 무작위로 선택하지 않으며, 선택한 무기를 고수합니다.

주목해야 할 또 다른 사항 : 이들은 각각 절반의 시간을 욕심으로 치고, 1/3을 그리고 6 분의 1을 잃을 것입니다. 그들이 이길 때, 그들은 많이이기는 경향이 있습니다. 왜 이런거야?

탐욕은 라운드를 잃을 때까지 무작위로 무기를 선택합니다. 이것은 그가 한 라운드에서 이기지 않으면 다시 무작위로 무기를 선택하여 다시 이길 수 있음을 의미합니다. 욕심이 끌 리거나지면, 그는 그 무기를 고수합니다. 욕심이 한 라운드 이상 승리하면 봇과 동일한 무기를 선택하고 욕심이 승리합니다. 만약 욕심이 어느 시점에서 잃어버린 무기를 선택하면, 우리의 로봇이 승리합니다.

욕심이 항상 큰 기회를 통해 승리하는 무기를 고르는 것이 아니라고 가정하면 다음과 같은 가능성이 있습니다.

1/3 : {1/2 승 (총 1/6). 1/2 잃습니다 (총 1/6). }

1/3 무승부

1/3 승

그래서 : 1/3 확률, 1/6 확률, 1/2 확률.

이것은 아마도 여러 라운드의 여러 게임을해야한다는 것을 보여줍니다

이들은 주로 도전의 롤링을 얻는 것입니다


3

원자로

이전 라운드에서 이겼 던 플레이를합니다.

import random
def reactfunc(I, dont, need, all, these, opp_history):
    if not opp_history:
        return random.choice(["R","P","S"])
    else:
        prev=opp_history[len(opp_history)-1]
        if prev == "R":
            return "P"
        if prev == "P":
            return "S"
        else:
            return "R"

1
당신은 대체 할 수 있습니다 opp_history[len(opp_history)-1]opp_history[-1].
CalculatorFeline

3

예술 아이

이 봇은 예술과 공예를하는 어린이처럼 행동하며 종이로 시작하여 종이나 가위를 무작위로 사용하지만 가위를 종이에 사용해야하기 때문에 가위 나 가위 뒤에는 가위를 사용하지 않습니다. 그녀에게 돌을 던지는 사람에게 돌을 던질 것입니다.

import random
def artsychildfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    if len(opp_history) == 0:
            return "P"
    elif opp_history[-1] == "R":
            return "R"
    elif my_history[-1] != "P":
            return "P"
    else:
            return random.choice(["P", "S"])

2

테스트를 위해 구축 한 3 개의 봇은 다음과 같습니다.


랜덤 봇

import random
def randombotfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
        return random.choice(["R","P","S"])

욕심

가장 많이로드 된 옵션을 선택하기 만하면됩니다.

import random
def greedyfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
        if my_loaded[0] > my_loaded[1]:
                if my_loaded[0] > my_loaded[2]:
                        return "R"
                elif my_loaded[0] < my_loaded[2]:
                        return "S"
                else:
                        return random.choice(["R","S"])
        elif my_loaded[0] < my_loaded[1]:
                if my_loaded[1] > my_loaded[2]:
                        return "P"
                elif my_loaded[1] < my_loaded[2]:
                        return "S"
                else:
                        return random.choice(["P","S"])
        else:
                if my_loaded[0] > my_loaded[2]:
                        return random.choice(["R","P"])
                elif my_loaded[0] < my_loaded[2]:
                        return "S"
                else:
                        return random.choice(["R","P","S"])

욕심

상대방이 탐욕스럽고이기는 대안을 연주한다고 가정합니다.

import random
def antigreedyfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
        if opp_loaded[0] > opp_loaded[1]:
                if opp_loaded[0] > opp_loaded[2]:
                        return "P"
                elif opp_loaded[0] < opp_loaded[2]:
                        return "R"
                else:
                        return "R"
        elif opp_loaded[0] < opp_loaded[1]:
                if opp_loaded[1] > opp_loaded[2]:
                        return "S"
                elif opp_loaded[1] < opp_loaded[2]:
                        return "R"
                else:
                        return "S"
        else:
                if opp_loaded[0] > opp_loaded[2]:
                        return "P"
                elif opp_loaded[0] < opp_loaded[2]:
                        return "R"
                else:
                        return random.choice(["R","P","S"])

1

배고프지 않아

def nothungryfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    if my_loaded[0] < my_loaded[1]:
            if my_loaded[0] < my_loaded[2]:
                    return "R"
            elif my_loaded[0] > my_loaded[2]:
                    return "S"
            else:
                    return random.choice(["R","S"])
    elif my_loaded[0] > my_loaded[1]:
            if my_loaded[1] < my_loaded[2]:
                    return "P"
            elif my_loaded[1] > my_loaded[2]:
                    return "S"
            else:
                    return random.choice(["P","S"])
    else:
            if my_loaded[0] < my_loaded[2]:
                    return random.choice(["R","P"])
            elif my_loaded[0] > my_loaded[2]:
                    return "S"
            else:
                    return random.choice(["R","P","S"])

이것은 문자 그대로 Greedy와 반대이며 사용 가능한 최저 점수 옵션을 선택합니다.


1

상대방의 즐겨 찾기 사용

from collections import Counter
import random
def useopponents(hi, my, name, is, stephen, opp_history):
  if opp_history:
    data = Counter(opp_history)
    return data.most_common(1)[0][0]
  else:
    return random.choice(["R","P","S"])

첫 번째 턴에서는 무작위 아이템을 선택합니다. 다른 차례마다 상대의 가장 일반적인 선택을 사용합니다. 동점이있는 경우 가장 빠른 선택이 기본값입니다.

// 여기서 코드를 훔쳤다


승리는 좋다

import random
def goodwinning(no, yes, maybe, so, my_history, opp_history):
  if opp_history:
    me = my_history[len(my_history)-1]
    you = opp_history[len(opp_history)-1]
    if you == me:
      return goodwinning(no, yes, maybe, so, my_history[:-1], opp_history[:-1])
    else:
      if me == "R":
        if you == "P":
          return "P"
        else:
          return "R"
      elif me == "P":
        if you == "S":
          return "S"
        else:
          return "R"
      else:
        if you == "R":
          return "R"
        else:
          return "P"
  else:
    return random.choice(["R","P","S"])

이전 라운드의 승자 선택을 반환합니다. 이전 라운드가 동점이라면, 그 전에 라운드를 재귀 적으로 확인합니다. 동점이거나 첫 번째 라운드 인 경우 무작위 선택을 반환합니다.


1

두 세계의 최고

이 봇은 기본적으로 Anti-Greedy와 Greedy (따라서 이름)를 결합합니다.

def bobwfunc(a, b, my_loaded, opp_loaded, c, d):
    opp_max = max(opp_loaded)
    opp_play = "PSR"[opp_loaded.index(opp_max)]

    my_max = max(my_loaded)
    my_play = "RPS"[my_loaded.index(my_max)]

    if opp_play == my_play:
        return opp_play
    else:
        return my_play if opp_max < my_max else opp_play

이것은 이미 예로써 게시 된 Antigreedy입니다.
Masclins

@AlbertMasclans 다른 봇으로 변경했습니다.
clismique

find문자열입니다. my_loaded그리고 opp_loaded둘 다 목록입니다. index당신이 원하는 것에 좋을 것입니다.
Masclins

@AlbertMasclans Whoops, 이제 수정되었습니다. 캐치 주셔서 감사합니다! 나는 이것이 다른 멍청이가 아니기를 바랍니다 ... 나는이 게시물을 다시 삭제하고 싶지 않습니다.
clismique

플레이 해 주셔서 감사합니다
Masclins

1

내쉬 봇

import random
def nashbotfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    r = opp_loaded[0] * opp_loaded[2]
    p = opp_loaded[0] * opp_loaded[1]
    s = opp_loaded[1] * opp_loaded[2]
    q = random.uniform(0, r + p + s) - r
    return "R" if q < 0 else "P" if q < p else "S"

상대가 점수를 얼마나 내는지에 대해 통계적으로 선호도를 갖지 않도록 세 가지 옵션 중에서 무작위로 선택합니다. 다시 말해, 욕심과 배고프지 모두 평균 기대 점수가 동일해야합니다.


1

기대되는 만

편집 : 업데이트 순위

이것은 Expectedbayes를 포함시킨 후 새로운 최고 순위입니다.

  • 통계 학자 2 기능 91.89 %
  • fitterfunc 85.65 %
  • 내쉬 펀 80.40 %
  • 계량기 기능 76.39 %
  • 베이 즈 펑크 예상 73.33 %
  • 해열 방지제 68.52 %
  • ...

설명

(NB : 게시물 05/06/2017 제출)

이 봇은 다음과 같이 다음 움직임의 예상 가치를 극대화하려고 시도합니다.

  • 상대방의 다음 가능한 움직임 각각에 대한 확률 계산
  • 해당 수치와 하중을 사용하여 각각의 R, P 및 S에 대한 예상 값을 계산
  • 예상 값이 가장 높은 이동 선택
  • 예측에 실패한 경우 임의로 값을 선택

10 개의 움직임마다 확률이 업데이트됩니다. 확률을 계산하는 데 사용 된 과거 이동 횟수는 각 봇에 대해 10으로 설정되었습니다 (따라서 전체 20 개의 기능). 이것은 아마도 데이터에 비해 적합하지만 더 이상 확인하지 않았습니다.

그것은 scikit 라이브러리를 사용하여 상대방의 이동 확률을 계산합니다 (규칙을 잘못 읽고 실제로 허용되지 않은 경우라고 말합니다).

항상 같은 선택을하는 봇에 대해 쉽게 이깁니다. 놀랍게도, 93 %의 승리 율로 랜덤 봇에 비해 매우 효과적입니다.

나는 100 턴과 제한된 수의 봇으로 빠른 시도를했으며 이것이 result_standing에서 얻은 것입니다.

  • randombotfunc, 35
  • 내쉬 봇 펑크, 333
  • 탐욕스러운, 172
  • antigreedyfunc, 491
  • 테마 센저 펑크, 298
  • rockstarfunc, 200
  • 통계 학자 2func, 748
  • fitterfunc, 656
  • 기대하는 베이직 스 펑크, 601

어느 것이 나쁘지 않습니까!

from sklearn.naive_bayes import MultinomialNB
import random

#Number of past moves used to compute the probability of next move
#I did not really try to make such thing as a cross-validation, so this number is purely random
n_data = 10

#Some useful data structures
choices = ['R','P','S']
choices_dic = {'R':0,'P':1,'S':2}
point_dic = {(0,0):0,(1,1):0,(2,2):0, #Same choices
             (0,1):-1,(0,2):1, #me = rock
             (1,0):1,(1,2):-1, #me = paper
             (2,0):-1,(2,1):1} #me = scissor

def compute_points(my_choice,opp_choice,my_load,opp_load):
    """
    Compute points
    @param my_choice My move as an integer
    @param opp_choice Opponent choice as an integer
    @param my_load my_load array
    @param opp_load opp_load array
    @return A signed integer (+ = points earned, - = points losed)
    """
    points = point_dic[(my_choice,opp_choice)] #Get -1, 0 or 1
    if points > 0:
        return points*my_load[my_choice] 
    else:
        return points*opp_load[opp_choice]

#This use to be a decision tree, before I changed it to something else. Nevertheless, I kept the name
class Decision_tree:
    def __init__(self):
        self.dataX = []
        self.dataY = []
        self.clf = MultinomialNB()

    def decide(self,my_load,opp_load,my_history,opp_history):
        """
        Returns the decision as an integer

        Done through a try (if a prediction could be made) except (if not possible)
        """
        try:
            #Let's try to predict the next move
            my_h = list(map(lambda x: choices_dic[x],my_history[-n_data:-1]))
            opp_h = list(map(lambda x: choices_dic[x],opp_history[-n_data:-1]))
            pred = self.clf.predict_proba([my_h+opp_h])
            #We create a points array where keys are the available choices
            pts = []
            for i in range(3):
                #We compute the expected gain/loss for each choice
                tmp = 0
                for j in range(3):
                    tmp += compute_points(i,j,my_load,opp_load)*pred[0][j]
                pts.append(tmp)
            return pts.index(max(pts)) #We return key for the highest expected value
        except:
            return random.choice(range(3))

    def append_data(self,my_history,opp_history):
        if my_history == "":
            self.clf = MultinomialNB()
        elif len(my_history) < n_data:
            pass
        else:
            my_h = list(map(lambda x: choices_dic[x],my_history[-n_data:-1]))
            opp_h = list(map(lambda x: choices_dic[x],opp_history[-n_data:-1]))
            self.dataX = self.dataX + [my_h+opp_h]
            self.dataY = self.dataY + [choices_dic[opp_history[-1:]]]

            if len(self.dataX) >= 10:
                self.clf.partial_fit(self.dataX,self.dataY,classes=[0,1,2])

                self.dataX = []
                self.dataY = []


#Once again, this is not actually a decision tree
dt = Decision_tree()

#There we go:
def expectedbayesfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    dt.append_data(my_history,opp_history)
    choice = choices[dt.decide(my_loaded,opp_loaded,my_history,opp_history)]
    return choice

PPCG에 오신 것을 환영합니다.
Zacharý

고마워요! PPCG에 오랫동안 참여하고 싶었습니다. 이제 수정되었습니다!
lesibius

0

자전거 타는 사람

def cycler(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    return "RPS"[len(myhistory)%3]

0


0

앙상블

from random import *
def f(I):
    if I==0:return "R"
    if I==1:return "P"
    return "S"
def b(I):
    if I=="R":return 0
    if I=="P":return 1
    return 2
def Ensemble(mp,op,ml,ol,mh,oh):
    A=[0]*3
    B=[0]*3
    if(len(oh)):
        k=b(oh[-1])
        A[k-2]+=0.84
        A[k]+=0.29
        for x in range(len(oh)):
            g=b(oh[x])
            B[g-2]+=0.82
            B[g]+=0.22
        s=sum(B)
        for x in range(len(B)):
            A[x]+=(B[x]*1.04/s)
        r=max(A)
    else:
        r=randint(0,3)
    return f(r)

여러 경쟁 알고리즘이 최상의 솔루션에 투표합니다.

교환

from random import *
def f(I):
    if I==0:return "R"
    if I==1:return "P"
    return "S"
def b(I):
    if I=="R":return 0
    if I=="P":return 1
    return 2
def Swap(mp,op,ml,ol,mh,oh):
    A=[0]*3
    B=[0]*3
    if(len(mh)):
        r=(b(mh[-1])+randint(1,2))%3
    else:
        r=randint(0,3)
    return f(r)

무작위 이동을 수행하지만 마지막 이동을 반복하지 않습니다.


0

혈액

간장

나는 그것을 고쳤으므로 아마 지금 작동해야한다.

나는 무언가를 다시 엉망으로 만들었으므로 삭제하고 삭제를 취소했습니다. 나는 많은 혼란을 겪고있다.

def blodsocerfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    import random
    # tuned up an ready to go hopeful
    # s o c e r y
    if len(my_history) > 40 and len(set(opp_history[-30:])) == 1:
        if opp_history[-1] == "S":
            return "R"
        elif opp_history[-1] == "R":
            return "P"
        else:
            return "S"
        # against confused bots that only do one thing most of the time.
    elif len(my_history)>30 and min(opp_history.count(i) for i in "RPS")/max(opp_history.count(i) for i in "RPS") >0.8:
        return "RPS"[my_loaded.index(max(my_loaded))] # This is so if the other bot is acting errratic
                                                      # the max bonus is used for advantage
    elif len(my_history) < 10:
        if len(my_history) > 2 and all(i == "S" for i in opp_history[1:]):
            if len(my_history) > 5: return "S"
            return "P"
        return "S" # Be careful, because scissors are SHARP
    elif len(set(opp_history[1:10])) == 1 and len(my_history) < 20:
        if opp_history[1] == "S":
            return "R"
        elif opp_history[1] == "R":
            return "R"
        else:
            return "P"
    elif len(opp_history) -  max(opp_history.count(i) for i in "RPS") < 4 and len(my_history) < 30:
        if opp_history.count("R") > max(opp_history.count(i) for i in "PS"):
            return "P"
        if opp_history.count("P") > max(opp_history.count(i) for i in "RS"):
            return "S"
        if opp_history.count("S") > max(opp_history.count(i) for i in "RP"):
            return "R"
    elif len(my_history) < 15:
        if max(opp_loaded)<max(my_loaded):
            return "RPS"[len(my_history)%3]
        else:
            return "RPS"[(my_loaded.index(max(my_loaded))+len(my_history)%2)%3]
    elif len(my_history) == 15:
        if max(opp_loaded)<max(my_loaded):
            return "RPS"[(len(my_history)+1)%3]
        else:
            return "RPS"[(my_loaded.index(max(my_loaded))+ (len(my_history)%2)^1)%3]
    else:
        if max(opp_loaded)<max(my_loaded):
            return random.choice("RPS")
        else:
            return "RPS"[(my_loaded.index(max(my_loaded))+ (random.randint(0,1)))%3]

1
if opp_history[1] == "S": return "R" elif opp_history[1] == "R": return "R" else: return "P"어떤 종류의 간장인가요?
Robert Fraser 17 년

@DestructibleLemon 이것은 0으로 나눕니다 :elif min(opp_history.count(i) for i in "RPS")/max(opp_history.count(i) for i in "RPS") >0.8 and len(my_history)>30:
Masclins

@AlbertMasclans 나는 그것을 고쳤다.
Destructible Lemon

@RobertFraser 정확히 그 코드 스 니펫에서 눈에 띄는 것은 무엇입니까?
Destructible Lemon

@DestructibleLemon 나는 당신이 여기서 무엇을하고 싶었는지 완전히 확신하지 못합니다 "RPS"[my_loaded.index(max(my_loaded))+len(my_history)%2].
Masclins

0

가중 랜덤

RandomBot와 비슷하지만 호출 할 때마다 2 개만 던집니다. 때때로 Rockstar 또는 Assassin을 이길 수 있지만, 다른 하나의 점수를 올리게됩니다 (예 : Rockstar를 이길 경우 Assassin에게 포인트 증가).

import random

selection_set = ["R", "P", "S"]
selection_set.pop(random.randint(0,2))
def weightedrandombotfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    return random.choice(selection_set)

0

욕심 심리학자

기본적으로 욕심이 많기 때문에 이름을 결정할 수 없다면 상대방이 욕심 전략을 사용했을 때 무엇을 하든지 반대합니다. 여전히 결정할 수 없으면 무작위로 진행됩니다.

from random import choice

def greedypsychologistfunc(my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history):
    greedy = get_my_move(my_loaded)
    combined = list(set(greedy) & set(get_opp_counter(opp_loaded)))

    if len(combined) == 0:
        return choice(greedy)
    return choice(combined)

def get_indexes(lst, value):
    return [i for i,x in enumerate(lst) if x == value]

def get_my_move(my_loaded):
    return ["RPS"[i] for i in get_indexes(my_loaded, max(my_loaded))]

def get_opp_counter(opp_loaded):
    return ["PSR"[i] for i in get_indexes(opp_loaded, max(opp_loaded))]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.