KOTH-ロードされたRPS


12

コンテストが恒久的に開催されました-2017年8月10日更新

2017年6月5日に勝者を宣言しましたが(勝者はベストアンサーとして保持されます)、新しいボットを獲得し、結果を更新します。

6月5日の結果

おめでとうuser1502040

同点がないため、勝った試合の割合のみを表示します。

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%

各ペアの結果のグリッドでGoogleスプレッドシートを作成しました: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"。別の何かを返す場合、それは自動的に試合に負けます。

ルール

組み込み関数は変更できません。

テスト中

コードとすべてのボットを補完するGitを更新しますhttps : //github.com/Masclins/LoadedRPS

審査

勝者は、1000回のラウンドロビン後、最も勝った試合をした人を選択して決定されます。結ばれたマッチにより、ネクタイは壊れます。私は多くのランダム性を期待しているため、1つではなく1000試合がプレイされています。

最大5つのボットを送信できます。

上のコンテスト終了7月 6月4日には、(それは私がすべての答えを受け入れるだろう最後の日になります)、そして上で7月 6月5日、私は(前advancemntを投稿しようとします)最終stadingsを投稿します。


これは私の最初のKOTHであるため、各ボットと対戦する試合の数など、改善のために何でも変更することに100%寛容です。

実際にはかなりのランダム性が関係しているので、1000マッチに編集されました。


いくつかの無作為化ボットで、あなたは実際には複数回の複数のゲーム作りたい
破壊可能なレモン

@DestructibleLemon各ボットを1回ではなく、互いにボットと3回対戦させることを考えました。あなたが同じように思うのを見て、そうします。
マスクリン

1
(。本当にあなたは試合の公正な量とそうではないでしょう、それはtrounced得ることができ、私のボットを参照してくださいいくつかの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
私はこのアプローチが本当に好きで、なぜラウンド間で状態を維持したいのか理解できます。提出の数を考えると、それを変更することは大きな問題だと思いますが。私はそれをさらなる挑戦のために考慮します(これが終わったときにするつもりです)。
マスクリン

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

間違いなく、シンプルさの結果で最高のもの
-Masclins

ちなみに、使用my_loadedするには、最後の動きに対して失う動きを評価する重みを追加できます。それは、対戦相手があなたがしたことと似たようなことをすることを前提としているので、あなたが同じことを続けていると想定することで彼を罰するようなものです。次のようなものですfor i, m in enumerate(reversed(my_history[-3:])): sc[(idx[m]+1)%3] += (K / (1 + i))
。– Masclins

@AlbertMasclansは別のソリューションを追加しました
表示名

1
私は本当にサタンが好きです。しかし、あなたが言ったように、私はそれが資格を与えるべきではないと信じています:たとえそれが明示的な規則を破らなくても、それは明らかにゲームの精神に反します。それでも、あなたのアイデアにおめでとうございます!
マスクリン

4

フィッター

このボットはパターンを改善し、エコノミストと融合します(パターンとエコノミストは参加しなくなります)

パターンの改善点は、ボットが2種類のパターンを探すようになったことです:相手が最後のプレイに反応し、相手が私の最後のプレイに反応します。次に、両方の予測を評価して、最適な予測を使用します。

このパターンから、ボットは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]

ここに2つの古いコードがあります

パターン(もう演奏していません)

パターンは、相手のパターンを見つけようとします。対戦相手が最後にプレイした後にプレイしたものに見えます(後者のプレイにより大きな重みを与えます)。それにより、対戦相手が何をプレイするかを推測し、それに対するカウンターマッチをプレイします。

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時間制限はありませんが、Yggdrasilは各対戦相手に対して1分以上かかっています。少し最適化することは可能でしょうか?
マスクリン

ええ、耐えられないほど遅いです
表示名

対戦相手ごとの分ごとの@AlbertMasclansは、対戦相手に対するすべてのゲームの合計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、do、not、need、these、arguments):return "P"

ロックスター

def rockstarfunc(I、do、not、need、these、arguments):return "R"

暗殺者

def assassinfunc(I、do、not、need、these、arguments):return "S"

説明

今、あなたはこれらのボットは完全に愚かだと思うかもしれません。

完全に真実ではありませんが、これらは実際には大きなボーナスを獲得するという考えに基づいており、敵はミスステップをしてそれで強奪されます。

現在、これらのボットは貪欲と非常によく似ていますが、よりシンプルであり、1つの武器に負荷がかかるまでランダムに選択せず、選択した武器に固執します。

別の注意事項:これらはそれぞれ半分の時間で貪欲に勝ち、3分の1の時間を引き、6分の1の時間を失います。彼らが勝つとき、彼らはたくさん勝つ傾向があります。どうしてこれなの?

貪欲は、彼がラウンドを失うまで、ランダムに武器を選びます。これは、彼がラウンドに勝てない場合、彼は再びランダムに武器を選ぶことを意味します。貪欲なドローまたは負けた場合、彼はその武器に固執します。貪欲が少なくとも1ラウンド勝った後、ボットと同じ武器を選ぶと、貪欲が勝ちます。ある時点で欲張りが負けた武器を選んだ場合、ボットが勝ちます。なぜなら、武器の負荷は貪欲なスコアよりも高いからです。

貪欲が常に大チャンスを通して勝った武器を選ぶとは限らないと仮定すると、これはチャンスがあることを意味します:

1/3:{1/2勝(合計1/6)。1/2を失う(合計1/6)。}

1/3引き分け

1/3勝

so:引く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つのボットを示します。


RandomBot

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

これは文字通り貪欲の逆であり、利用可能な最低ポイントオプションを選択します。


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

最初のターンでは、ランダムなアイテムを選択します。1ターンおきに、相手の最も一般的な選択肢を使用します。同点がある場合、デフォルトで最も早い最も一般的な選択肢になります。

// ここからコードを盗みました


勝利は良い

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であり、すでに例として投稿されています。
マスクリン

@AlbertMasclans別のボットに変更しました。
clismique

find文字列用です。my_loadedそしてopp_loaded両方のリストです。indexあなたが望むものに良いはずです。
マスクリン

@AlbertMasclans Whoops、現在修正済み。キャッチしてくれてありがとう!これが別の重複ではないことを願っています...この投稿を再び削除したくないです。
clismique

これは大丈夫です、プレイしてくれてありがとう
Masclins

1

NashBot

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"

対戦相手が得点の大きさに関して統計的に動きの間で好みを持たないように、3つのオプションからランダムに選択します。言い換えれば、貪欲と空腹ではない両方が同じ平均予想スコアを持っている必要があります。


1

Expectedbayes

編集:ランキングの更新

これは、Expectedbayesを含めた後の新しいトップランキングです。

  • statistician2func 91.89%
  • fitterfunc 85.65%
  • nashfunc 80.40%
  • weigherfunc 76.39%
  • expectedbayesfunc 73.33%
  • antirepeaterfunc 68.52%
  • ...

説明

(注:2017年5月6日投稿後)

このボットは、次の動きの期待値を最大化しようとします:

  • 敵の次の可能な動きのそれぞれの確率を計算する
  • その数値と負荷を使用して、R、P、Sのそれぞれの期待値を計算します
  • 最も高い期待値を持つ動きを選択する
  • 予測が失敗した場合に値をランダムに選択する

確率は10手ごとに更新されます。確率を計算するために使用される過去の動きの数は、各ボットについて10に設定されています(全体で20の機能)。これはおそらくデータを過剰に適合させていますが、私はそれ以上確認しようとしませんでした。

scikitライブラリに依存して、相手の移動確率を計算します(ルールを読み間違え、実際に許可されなかった場合に備えて言っています)。

常に同じ選択をするボットに対して簡単に勝ちます。驚いたことに、93%の勝率でランダムボットに対して非常に効果的です(これは、各ラウンドで可能なポイントの最大数を最大化する一方で、相手が獲得できるポイントの数を制限するためです)。

100ターンで限られた数のボットで簡単に試してみましたが、これはresult_standingから得たものです。

  • randombotfunc、35
  • nashbotfunc、333
  • greedyfunc、172
  • antigreedyfunc、491
  • themessengerfunc、298
  • rockstarfunc、200
  • statistician2func、748
  • fitterfunc、656
  • expectedbayesfunc、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へようこそ、最初の投稿です!
ザカリー

どうもありがとう!PPCGに長い間参加したかった。修正されました!
レシビウス

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"これはどんな社会ですか?
ロバートフレイザー

@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それを修正しました。
破壊可能なレモン

@RobertFraserそのコードスニペットについて正確に優れているのは何ですか?
破壊可能なレモン

@DestructibleLemonあなたがここで何をしたいのか完全にはわかりません:"RPS"[my_loaded.index(max(my_loaded))+len(my_history)%2]しかし、それは範囲外に見えます(そして、それ以降の行もそうです)。
マスクリン

0

重み付けランダム

RandomBotに似ていますが、呼び出されるたびにスローするのは2つだけです。ロックスターやアサシンに勝つこともありますが、他のスコアを上げます(たとえば、ロックスターに勝った場合、アサシンにポイントブーストを与えます)。

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.