KOTH - RPS chargé

12

Concours ouvert en permanence - Mis à jour le 10 août 2017

Même si le 5 juin 2017, j'ai déclaré un gagnant (qui restera la meilleure réponse), je vais lancer de nouveaux bots et mettre à jour les résultats.

Résultats du 5 juin

Félicitations user1502040

Puisqu'il n'y a pas d'égalité, je montre seulement le% de matchs gagnés.

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%

J'ai créé une feuille Google avec la grille de résultats de chaque association: https://docs.google.com/spreadsheets/d/1KrMvcvWMkK-h1Ee50w0gWLh_L6rCFOgLhTN_QlEXHyk/edit?usp=sharing


Grâce au dilemme de Petri, je me suis retrouvé capable de gérer ce roi de la colline.

Le jeu

Le jeu est un simple "Rock-Paper-Scissors" avec une touche: les points gagnés à chaque victoire augmentent pendant le match (votre R, P ou S est chargé).

  • Le papier gagne le rock
  • Ciseaux remporte Paper
  • Rock gagne des ciseaux

Le gagnant obtient autant de points que sa charge sur son jeu.

Le perdant augmente de 1 la charge de son jeu.

En cas d'égalité, chaque joueur augmente la charge de son jeu de 0,5.

Après 100 parties, celui qui a le plus de points est le gagnant.

par exemple: P1 a des charges [10,11,12] (roche, papier, ciseaux) et P2 [7,8,9]. P1 joue R, P2 joue P. P2 gagne et obtient 8 points. Les charges P1 deviennent [11,11,12], les charges P2 restent les mêmes.

Spécifications du défi

Votre programme doit être écrit en Python (désolé, je ne sais pas comment le gérer autrement). Vous devez créer une fonction qui prend chacune de ces variables comme argument à chaque exécution:

my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history

points - Points actuels (le vôtre et votre opp)

loaded- Tableau avec charges (dans l'ordre RPS) (le vôtre et votre opp)

history- Chaîne avec tous les jeux, le dernier personnage est le dernier jeu (le vôtre et votre opp)

Vous devez revenir "R", "P"ou "S". Si vous retourniez quelque chose de différent, ce serait une perte automatique du match.

Règles

Vous ne pouvez pas modifier les fonctions intégrées.

Essai

Je garderai un Git mis à jour avec le code et tous les bots compiting: https://github.com/Masclins/LoadedRPS

Juger

Le vainqueur sera déterminé en sélectionnant la personne avec le plus de matchs gagnants après 1000 tournois complets. Les égalités seront brisées par des matchs à égalité. 1000 matchs sont joués plutôt qu'un, car je m'attends à beaucoup d'aléatoire, et de cette façon l'aléatoire serait moins pertinent.

Vous pouvez soumettre jusqu'à 5 bots.

Le concours se termine le 4 juillet (ce sera le dernier jour où j'accepterai une réponse), et le 5 juillet je posterai les stadings finaux (je pourrais essayer de poster un avancemnt avant).


Comme il s'agit de mon premier KOTH, je suis 100% ouvert à tout changement pour l'amélioration, comme le nombre de matchs joués contre chaque bot.

Modifié à 1000 correspondances, car je vois qu'il y a vraiment du hasard.

Masclins
la source
avec des robots aléatoires, vous voulez réellement faire plusieurs jeux de plusieurs tours
Destructible Lemon
@DestructibleLemon J'ai pensé à faire jouer chaque bot trois fois l'un contre l'autre plutôt qu'une fois. En vous voyant penser de la même façon, je le ferai.
Masclins
1
(vraiment vous avez besoin d'un assez grand nombre, car certaines probabilités s'étendent vraiment sur plusieurs matchs. voir mon bot, où il pourrait être annulé, mais probablement pas avec une bonne quantité de matchs)
Destructible Lemon
1
Je suis content que ma question vous ait aidé à exécuter ceci, @AlbertMasclans!
Gryphon
2
@AlbertMasclans Pouvez-vous publier le script de test complet (y compris runcodeet bots)?
CalculatorFeline

Réponses:

8

Statisticien (ne joue plus)

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

Bascule entre quelques stratégies simples basées sur les performances passées attendues

Statisticien 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]

Nash

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]

Calcule un équilibre de Nash approximatif par descente de gradient.

user1502040
la source
1
J'aime vraiment cette approche, et je peux comprendre pourquoi vous voudriez pouvoir garder l'état entre les tours. Même si je considère qu'il est très difficile de le changer étant donné le nombre de soumissions. Je prendrai cela en compte pour d'autres défis (ce que je m'attends à faire lorsque cela se terminera).
Masclins
5

Peseur

J'ai perdu la trace du raisonnement lors de l'expérimentation du code, mais l'idée de base est d'estimer la probabilité de mouvement de l'adversaire par les 3 derniers mouvements en utilisant des poids et de les multiplier par un autre poids qui dépend des charges. Je pensais que je pouvais my_loadedégalement l' utiliser , mais je ne pouvais pas décider comment, alors laissez-le de côté.

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

Satan

Sera probablement disqualifié, car c'est une sorte de tricherie et il fait des hypothèses sur la fonction de test (il doit avoir la fonction de l'adversaire dans une variable sur son cadre de pile), mais il ne casse techniquement aucune règle actuelle - il ne le fait pas redéfinir ou réécrire quoi que ce soit. Il utilise simplement la magie noire pour exécuter la fonction adverse pour voir quel tour ils ont fait / vont faire. Il ne peut pas gérer le hasard, mais les robots déterministes n'ont aucune chance de vaincre Satan.

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
Afficher un nom
la source
Sans aucun doute le meilleur en termes de simplicité-résultats
Masclins
Soit dit en passant, pour l'utiliser, my_loadedvous pouvez ajouter un poids qui évalue le coup qui serait perdu par rapport à votre dernier coup. C'est comme supposer que votre adversaire fera quelque chose de similaire à ce que vous avez fait, et donc le punir pour supposer que vous continuerez à jouer de la même manière. Quelque chose comme:for i, m in enumerate(reversed(my_history[-3:])): sc[(idx[m]+1)%3] += (K / (1 + i))
Masclins
@AlbertMasclans a ajouté une autre solution
Nom d'affichage
1
J'aime vraiment celui de Satan. Mais comme vous l'avez dit, je pense que cela ne devrait pas être qualifié: même si cela ne viole aucune règle explicite, c'est clairement contraire à l'esprit du jeu. Encore bravo pour votre idée!
Masclins
4

Installateur

Ce robot améliore le modèle et le fusionne avec l'économiste (le modèle et l'économiste ne participeront plus)

L'amélioration de Pattern est que le Bot recherche maintenant deux types de patterns: l'adversaire réagissant à son dernier jeu et l'adversaire réagissant à mon dernier jeu. Évalue ensuite les deux prédictions pour utiliser celle qui correspond le mieux.

De ce modèle, le Bot a maintenant la probabilité de R, P et S. En tenant compte de cela et de la valeur attendue de chaque jeu (comme l'a fait Economist), le Bot joue celui qui donne le plus de valeur.

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]

Voici les deux anciens codes

Motif (ne joue plus)

Le Pattern essaie de trouver des modèles sur son adversaire. Il ressemble à ce que l'adversaire avait joué après le dernier jeu qu'il a fait (donnant plus de poids à ces derniers jeux). Grâce à cela, il devine ce que l'adversaire va jouer et joue le contre-match.

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]

Economiste (ne joue plus)

The Economist fait ce qui suit: Devine la probabilité de chaque jeu de l'adversaire en regardant ce qu'il avait joué les 9 derniers tours. À partir de là, calcule le bénéfice attendu de chaque jeu et va avec celui qui a la meilleure valeur attendue.

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"])
Masclins
la source
4

Yggdrasil

Ceci est nommé "Yggdrasil" car il regarde vers l'avant dans l'arbre du jeu. Ce bot n'effectue aucune prédiction sur l'adversaire, il tente simplement de maintenir un avantage statistique s'il lui est attribué (en équilibrant les bénéfices actuels et futurs). Il calcule une stratégie mixte approximativement idéale et renvoie un mouvement sélectionné au hasard avec ces poids. Si ce bot était parfait (ce qui n'est pas le cas, car la fonction de valorisation de l'État est assez mauvaise et ne semble pas très loin), il serait impossible de battre ce bot plus de 50% du temps. Je ne sais pas dans quelle mesure ce bot fera l'affaire.

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]})
PhiNotPi
la source
veuillez supprimer les commentaires qui ne rendent pas le code plus compréhensible
Nom d'affichage
@SargeBorsch done
PhiNotPi
1
@PhiNotPi Je suis conscient que je n'ai posté aucune limitation de temps, mais Yggdrasil prend plus d'une minute contre chaque adversaire. Serait-il possible de l'optimiser un peu?
Masclins
ouais c'est insupportablement lent
Nom d'affichage
@AlbertMasclans par minute par adversaire voulez-vous dire 1 minute au total pour tous les matchs contre un adversaire? Je peux aussi essayer de l'accélérer mais je ne sais pas vraiment comment le faire, il semble que je n'avance que tel quel.
PhiNotPi
4

Anti-répéteur

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"

Picks papier au premier tour, après quoi il retourne tout ce qui bat ce que l'adversaire a fait le plus, ramasser du papier en cas d'égalité.

Imitateur

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]

Copie simplement le dernier coup de l'adversaire.

Anti-Anti-Greedy

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

Prend tout ce qui perd au choix le plus lourd de l'adversaire.

Un peu faim

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"])
Gryphon
la source
3

Le Messager

def themessengerfunc (I, do, not, need, these, arguments): return "P"

Rock star

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

Assassin

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

Explication

Maintenant, vous pensez peut-être que ces robots sont totalement stupides.

pas entièrement vrai, ceux-ci sont en fait basés sur l'idée, d'amasser un énorme bonus, et l'ennemi de faire un faux pas et de se faire surprendre.

maintenant, ces robots jouent de manière très similaire aux gourmands, cependant, ils sont plus simples, et ne choisissent pas au hasard jusqu'à ce qu'ils obtiennent une charge sur une arme, ils s'en tiennent à leur arme de choix.

Autre chose à noter: celles-ci battront chacune gourmande environ la moitié du temps, tirant un tiers du temps, et perdant un sixième du temps. quand ils gagnent, ils ont tendance à gagner beaucoup. Pourquoi est-ce?

Gourmand, jusqu'à ce qu'il perde un tour, choisira une arme au hasard. cela signifie que lorsqu'il ne gagne pas un tour, il choisira à nouveau une arme au hasard, qui pourrait être gagnante à nouveau. si le cupide tire ou perd, il reste avec cette arme. si le cupide gagne au moins un round, puis choisit la même arme que le bot, le cupide gagne. si le gourmand choisit l'arme perdante à un moment donné, notre bot gagne, car la charge sur notre arme aurait été plus élevée que le score du gourmand.

En supposant que les gourmands ne choisissent pas toujours l'arme gagnante par grande chance, cela signifie que les chances sont les suivantes:

1/3: {1/2 victoire (1/6 au total). 1/2 défaite (1/6 au total). }

1/3 nul

1/3 victoire

donc: 1/3 chance de tirer, 1/6 chance de perdre, 1/2 chance de gagner.

cela montre probablement que vous devez faire plusieurs jeux de plusieurs tours

ce sont principalement pour lancer le défi

Citron destructible
la source
3

Réacteur

Fait le jeu qui aurait gagné le tour précédent.

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"
KSmarts
la source
1
Vous pouvez remplacer opp_history[len(opp_history)-1]par opp_history[-1].
CalculatorFeline
3

Enfant Artsy

Ce bot agit comme un enfant qui joue aux arts et métiers, commencera avec du papier et utilisera du papier ou des ciseaux au hasard, mais n'utilisera pas de ciseaux après la roche ou des ciseaux car il doit utiliser les ciseaux sur du papier. Jettera un rocher à quiconque lui lancera un rocher.

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"])
TitusLucretius
la source
2

Voici les trois robots que j'ai construits pour les tests:


RandomBot

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

Glouton

Choisit simplement son option la plus chargée.

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

Antigreedy

Suppose que l'adversaire jouera gourmand et joue l'alternative gagnante.

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"])
Masclins
la source
1

Pas faim

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

C'est littéralement l'inverse de Greedy, il choisit l'option de points les plus bas disponible.

Pélican bleu canard
la source
1

Utiliser le favori de l'adversaire

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

Pour le premier tour, choisit un objet au hasard. Pour chaque autre tour, utilise le choix le plus courant de l'adversaire. S'il y a égalité, il s'agit par défaut du premier choix le plus courant.

// J'ai volé du code d' ici


Gagner, c'est bien

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

Renvoie le choix du gagnant du tour précédent. Si le tour précédent était une égalité, vérifie récursivement le tour précédent. S'il ne s'agissait que d'égalité, ou s'il s'agit du premier tour, renvoie un choix aléatoire.

Stephen
la source
1

Le meilleur des deux mondes

Ce bot combine essentiellement Anti-Greedy et Greedy (d'où son nom).

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
clismique
la source
Ceci est l'Antigreedy, déjà affiché à titre d'exemple.
Masclins
@AlbertMasclans l'a changé en un autre bot.
clismique
findest pour les chaînes. my_loadedet opp_loadedsont les deux listes. indexdevrait être bon pour ce que vous voulez.
Masclins
@AlbertMasclans Whoops, corrigé maintenant. Merci pour la capture! J'espère que ce n'est pas un autre dupe ... Je ne veux pas supprimer à nouveau ce post.
clismique
C'est bon, merci d'avoir joué
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"

Choisit au hasard entre les trois options de manière à ce que l'adversaire n'ait statistiquement aucune préférence entre les mouvements en ce qui concerne le score; en d'autres termes, Greedy et Not Hungry devraient avoir le même score moyen attendu.

Neil
la source
1

Baies attendues

Modifier: classement mis à jour

Il s'agit du nouveau meilleur classement après l'inclusion d'Expectedbayes:

  • statisticien2func 91,89%
  • fitterfunc 85,65%
  • nashfunc 80,40%
  • weigherfunc 76,39%
  • attendubayesfunc 73,33%
  • antirepeaterfunc 68,52%
  • ...

Explications

(NB: soumission post 05/06/2017)

Ce bot essaie de maximiser la valeur attendue de son prochain mouvement en:

  • Calcul de la probabilité pour chacun des prochains coups possibles de l'adversaire
  • Utiliser ce chiffre et les charges pour calculer la valeur attendue pour chacun des R, P et S
  • Sélection du mouvement qui a la valeur attendue la plus élevée
  • Sélection aléatoire d'une valeur si la prédiction a échoué

Les probabilités sont mises à jour tous les dix coups. Le nombre de mouvements passés utilisés pour calculer les probabilités a été fixé à 10 pour chaque bot (soit 20 fonctionnalités au total). Cela surestime probablement les données, mais je n'ai pas essayé de vérifier davantage.

Il s'appuie sur la bibliothèque scikit pour calculer les probabilités de déplacement de l'adversaire (je le dis au cas où j'aurais mal lu les règles et que ce n'était en fait pas autorisé).

Il gagne facilement contre des bots qui font toujours le même choix. Étonnamment, il est assez efficace contre le bot aléatoire avec un taux de victoire de 93% (je pense que cela est dû au fait qu'il limite le nombre de points que son adversaire peut obtenir tout en maximisant son propre nombre de points possibles pour chaque tour).

J'ai fait un essai rapide avec 100 tours et seulement un nombre limité de bots, et c'est ce que j'ai obtenu de result_stand:

  • randombotfunc, 35
  • nashbotfunc, 333
  • greedyfunc, 172
  • antigreedyfunc, 491
  • themessengerfunc, 298
  • rockstarfunc, 200
  • statisticien2func, 748
  • fitterfunc, 656
  • attendubayesfunc, 601

Ce qui n'est pas si mal!

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
lesibius
la source
Bienvenue sur PPCG et bon premier post!
Zacharý
Merci beaucoup! Je voulais participer au PPCG depuis longtemps. Maintenant c'est réparé!
lesibius
0

Cycleur

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

0

CalculatorFeline
la source
0

Ensemble

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)

Plusieurs algorithmes concurrents votent sur la meilleure solution.

Échanger

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)

Fait un coup au hasard, mais sans répéter le dernier coup qu'il a fait.

Magenta
la source
0

blodsocer

épicerie

Je lui ai donné un correctif, donc ça devrait probablement fonctionner maintenant j'espère

J'ai gâché quelque chose à nouveau, j'ai donc supprimé et restauré. Je fais beaucoup de dégâts.

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]
Citron destructible
la source
1
if opp_history[1] == "S": return "R" elif opp_history[1] == "R": return "R" else: return "P"quelle sorte d'épicerie est-ce?
Robert Fraser,
@DestructibleLemon Cela divise par 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 J'ai corrigé cela.
Destructible Lemon
@RobertFraser Qu'est-ce qui se passe exactement avec cet extrait de code?
Destructible Lemon
@DestructibleLemon Je ne suis pas tout à fait sûr de ce que vous vouliez faire ici: "RPS"[my_loaded.index(max(my_loaded))+len(my_history)%2]mais il semble hors de portée (tout comme les autres lignes).
Masclins
0

Aléatoire pondéré

Comme RandomBot, mais il n'en prend que 2 à lancer chaque fois qu'il est invoqué. Battra parfois Rockstar ou Assassin, mais augmentera les scores de l'autre (par exemple, s'il bat Rockstar, il donne un coup de pouce à 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)
Andrew U Baker
la source
0

Psychologue gourmand

Nommé cela parce qu'il est par défaut gourmand, mais s'il ne peut pas décider, il contrecarre tout ce que l'adversaire ferait s'il utilisait la stratégie gourmande. S'il ne peut toujours pas décider, il se déroule au hasard.

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))]
Solomon Ucko
la source