KOTH - RPS caricato


12

Contest aperto permanentemente - Aggiornato il 10 agosto 2017

Anche se il 5 giugno 2017 ho dichiarato un vincitore (che sarà mantenuto come la migliore risposta), cercherò nuovi robot e aggiornerò i risultati.

Risultati del 5 giugno

Congratulazioni user1502040

Dal momento che non ci sono pareggi, mostro solo la percentuale di partite vinte.

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%

Ho creato un foglio di Google con la griglia dei risultati di ciascun abbinamento: https://docs.google.com/spreadsheets/d/1KrMvcvWMkK-h1Ee50w0gWLh_L6rCFOgLhTN_QlEXHyk/edit?usp=sharing


Grazie al dilemma di Petri mi sono trovato in grado di gestire questo re della collina.

Il gioco

Il gioco è un semplice "Rock-Paper-Scissors" con una svolta: punti guadagnati con ogni aumento di vittoria durante la partita (le tue R, P o S vengono caricate).

  • La carta vince il rock
  • Le forbici vincono la carta
  • Il rock vince le forbici

Il vincitore ottiene tanti punti quanto il suo carico sul suo gioco.

Il perdente aumenta di 1 il carico sul suo gioco.

In caso di pareggio, ogni giocatore aumenta il carico del suo gioco di 0,5.

Dopo 100 giochi, quello con più punti è il vincitore.

es: P1 ha carichi [10,11,12] (Rock, Paper, Scissors) e P2 [7,8,9]. P1 gioca R, P2 gioca P. P2 vince e ottiene 8 punti. I carichi P1 diventano [11,11,12], i carichi P2 rimangono invariati.

Specifiche della sfida

Il tuo programma deve essere scritto in Python (scusami, altrimenti non so come gestirlo). Devi creare una funzione che prenda ciascuna di queste variabili come argomento per ogni esecuzione:

my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history

points - Punti attuali (i tuoi e i tuoi avversari)

loaded- Matrice con carichi (in ordine di RPS) (tua e del tuo opp)

history- Stringa con tutti i giochi, l'ultimo personaggio è l'ultimo gioco (il tuo e il tuo opp)

Devi tornare "R", "P"o "S". Se restituissi qualcosa di diverso, sarebbe una perdita automatica della partita.

Regole

Non è possibile modificare le funzioni integrate.

analisi

Terrò un Git aggiornato con il codice e tutti i robot compiting: https://github.com/Masclins/LoadedRPS

A giudicare

Il vincitore verrà deciso selezionando la persona con il maggior numero di partite vincenti dopo 1000 round-robin completi. I legami saranno spezzati dalle partite legate. Si stanno giocando 1000 partite anziché una perché mi aspetto molta casualità, e in questo modo la casualità sarebbe meno rilevante.

Puoi inviare fino a 5 robot.

Il concorso termina il 4 luglio (che sarà l'ultimo giorno in cui accetterò qualsiasi risposta), e il 5 luglio pubblicherò la classifica finale (potrebbe provare a pubblicare un anticipo prima).


Dato che questo è il mio primo KOTH, sono aperto al 100% a cambiare qualsiasi cosa per migliorare, come il numero di partite giocate contro ogni bot.

Modificato per 1000 partite, dal momento che vedo che c'è davvero molta casualità.


con alcuni robot randomizzati, in realtà vuoi creare più giochi di più round
Destructible Lemon

@DestructibleLemon Ho pensato di far giocare ogni bot tre volte l'uno contro l'altro piuttosto che una volta. Vederti pensare allo stesso modo, lo farò.
Masclins,

1
(hai davvero bisogno di un numero abbastanza grande, dato che alcune probabilità si estendono su più partite. Vedi il mio bot, dove potrebbe essere trionfato, ma probabilmente non con una discreta quantità di partite)
Limone distruttibile

1
Sono contento che la mia domanda ti abbia aiutato a far funzionare questo, @AlbertMasclans!
Gryphon,

2
@AlbertMasclans Puoi pubblicare l'intero testscript (incluso runcodee bots)?
CalculatorFeline

Risposte:


8

Statistico (non gioca più)

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

Passa tra alcune semplici strategie basate sulla performance passata prevista

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

Calcola un equilibrio di Nash approssimativo per discesa gradiente.


1
Mi piace molto questo approccio e posso capire perché vorresti essere in grado di mantenere lo stato tra i round. Anche se lo vedo un grosso problema modificarlo, dato il numero di invii. Lo terrò in considerazione per ulteriori sfide (che mi aspetto di fare al termine).
Masclins,

5

bilancia

Ho perso la cognizione del ragionamento mentre sperimentavo il codice, ma l'idea di base è stimare la probabilità di mossa dell'avversario per le ultime 3 mosse usando alcuni pesi e moltiplicarli per un altro peso che dipende dai carichi. Pensavo di poterlo usare in qualche modo my_loaded, ma non riuscivo a decidere come, quindi l'ho lasciato fuori.

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

Satana

Probabilmente sarà squalificato, perché è una specie di imbroglio e fa alcune ipotesi sulla funzione di test (deve avere la funzione dell'avversario in una variabile sul suo frame dello stack), ma tecnicamente non infrange nessuna delle regole attuali - non lo fa ridefinire o riscrivere qualsiasi cosa. Usa semplicemente la magia nera per eseguire la funzione avversaria per vedere quale turno ha fatto / farà. Non può gestire la casualità, ma i robot deterministici non hanno alcuna possibilità di sconfiggere Satana.

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

Senza dubbio il migliore in termini di risultati di semplicità
Masclins

A proposito, per usare my_loadedpotresti aggiungere un peso che valorizzi la mossa che perderebbe con le tue ultime mosse. È come supporre che il tuo avversario farà qualcosa di simile a quello che hai fatto, e quindi punirlo per aver supposto che continuerai a giocare lo stesso. Qualcosa del tipo:for i, m in enumerate(reversed(my_history[-3:])): sc[(idx[m]+1)%3] += (K / (1 + i))
Masclins

@AlbertMasclans ha aggiunto un'altra soluzione
Visualizza nome

1
Mi piace molto quello di Satana. Ma come hai detto, credo che non dovrebbe qualificarsi: anche se non infrange alcuna regola esplicita, è chiaramente contro lo spirito del gioco. Ancora complimenti per la tua idea!
Masclins,

4

aggiustatore

Questo Bot migliora Pattern e lo fonde con Economist (Pattern ed Economist non parteciperanno più)

Il miglioramento di Pattern è che il Bot ora cerca due tipi di pattern: l'avversario reagisce alla sua ultima giocata e l'avversario reagisce alla mia ultima giocata. Quindi valuta entrambe le previsioni per utilizzare quella più adatta.

Da quel modello il Bot ha ora la probabilità per R, P e S. Tenendo conto di ciò e del valore atteso di ogni gioco (come ha fatto l'Economist), il Bot gioca quello che dà il maggior valore.

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]

Ecco i due vecchi codici

Pattern (non più in riproduzione)

Il modello cerca di trovare modelli sul suo avversario. Sembra che l'avversario abbia giocato dopo l'ultima giocata (dando più peso a quest'ultima giocata). Attraverso ciò, indovina cosa giocherà l'avversario e gioca la contromisura.

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]

Economista (non gioca più)

L'Economist fa quanto segue: Indovina la probabilità di ogni giocata dell'avversario guardando ciò che ha giocato negli ultimi 9 turni. Da ciò, calcola il beneficio atteso di ogni gioco e va con quello che ha il miglior valore atteso.

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

Questo si chiama "Yggdrasil" perché guarda avanti nella struttura del gioco. Questo bot non esegue alcuna previsione dell'avversario, tenta semplicemente di mantenere un vantaggio statistico se gli viene dato (bilanciando i profitti attuali e futuri). Calcola una strategia mista approssimativamente ideale e restituisce una mossa selezionata casualmente con quei pesi. Se questo bot fosse perfetto (cosa che non lo è, perché la funzione di valutazione dello stato è piuttosto male e non sembra molto avanti), sarebbe impossibile batterlo più del 50% delle volte. Non so quanto bene farà questo robot in pratica.

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

rimuovi i commenti che non rendono il codice più comprensibile
Nome visualizzato

@SargeBorsch done
PhiNotPi

1
@PhiNotPi Sono consapevole che non ho pubblicato limiti di tempo, ma Yggdrasil sta impiegando più di un minuto contro ogni avversario. Sarebbe possibile ottimizzarlo un po '?
Masclins,

sì, è insopportabilmente lento
Visualizza nome

@AlbertMasclans al minuto per avversario intendi un totale di 1 minuto per tutte le partite contro un avversario? Inoltre posso provare ad accelerarlo ma non so davvero come farlo, sembra solo 1 andare avanti così com'è.
PhiNotPi

4

Anti-ripetitore

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"

Prende la carta al primo turno, dopo di che restituisce tutto ciò che batte di più l'avversario, raccogliendo la carta in caso di pareggio.

Copione

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]

Copia semplicemente l'ultima mossa dell'avversario.

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

Prende tutto ciò che perde alla scelta più pesantemente avversaria dell'avversario.

Un po 'affamato

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

Il Messaggero

def themessengerfunc (io, non ho bisogno di questi argomenti): return "P"

Stella del rock

def rockstarfunc (io, non ho bisogno di questi argomenti): return "R"

Assassino

def assassinfunc (io, non ho bisogno, questi, argomenti): return "S"

Spiegazione

Ora, potresti pensare che questi robot siano completamente stupidi.

non del tutto vero, questi sono in realtà basati sull'idea, di accumulare un enorme bonus, e il nemico che fa un passo falso e viene inghiottito con esso.

ora, questi robot giocano in modo molto simile all'avido, tuttavia sono più semplici e non prendono casualmente fino a quando non ottengono un carico su un'arma, si attaccano con la loro arma preferita.

Un'altra cosa da notare: ognuno di essi batterà avido per metà del tempo, disegnando un terzo del tempo e perdendo un sesto del tempo. quando vinceranno, tenderanno a vincere molto. perchè è questo?

Greedy, fino a quando perde un round, sceglierà casualmente un'arma. questo significa che quando non vince un round, sceglierà di nuovo un'arma a caso, che potrebbe essere di nuovo vincente. se l'avido disegna o perde, si attacca con quell'arma. se l'avido vince almeno un round, quindi sceglie la stessa arma del robot, vince l'avido. se ad un certo punto l'avido prende l'arma perdente, il nostro robot vince, perché il carico sulla nostra arma sarebbe stato più alto del punteggio avido.

Supponendo che l'avido non sempre scelga l'arma vincente con grande probabilità, ciò significa che le probabilità sono:

1/3: {1/2 vittoria (1/6 in totale). 1/2 perdita (1/6 totale). }

1/3 di pareggio

1/3 di vittoria

quindi: 1/3 possibilità di pareggio, 1/6 possibilità di sconfitta, 1/2 possibilità di vincere.

questo probabilmente mostra che devi fare più giochi di più round

questi sono principalmente per far rotolare la sfida


3

Reattore

Fa il gioco che avrebbe vinto il round precedente.

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
È possibile sostituire opp_history[len(opp_history)-1]con opp_history[-1].
CalculatorFeline

3

Artsy Child

Questo robot si comporta come un bambino che gioca a arti e mestieri, inizierà con la carta e userà la carta o le forbici in modo casuale, ma non utilizzerà le forbici dopo il rock o le forbici perché ha bisogno di usare le forbici su carta. Lancerà una pietra contro chiunque le lanci una pietra.

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

Ecco i tre robot che ho creato per i test:


RandomBot

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

avido

Scegli semplicemente la sua opzione più caricata.

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

Presuppone che l'avversario giocherà avido e gioca l'alternativa vincente.

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

Non affamato

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

Questo è letteralmente l'inverso di Greedy, sceglie l'opzione con il punteggio più basso disponibile.


1

Usa il Preferito dell'avversario

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

Per il primo turno, sceglie un oggetto casuale. Per ogni altro turno, usa la scelta più comune dell'avversario. Se c'è un pareggio, il valore predefinito è la prima scelta più comune.

// Ho rubato il codice da qui


Vincere è buono

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

Restituisce la scelta del vincitore del round precedente. Se il round precedente era un pareggio, controlla ricorsivamente il round precedente. Se era solo un pareggio, o è il primo round, restituisce una scelta casuale.


1

Il meglio di entrambi i mondi

Questo bot fondamentalmente combina Anti-Greedy e Greedy (da cui il nome).

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

Questa è l'Antigreedy, già pubblicata come esempio.
Masclins,

@AlbertMasclans Lo ha cambiato in un altro bot.
clismique,

findè per archi. my_loadede opp_loadedsono entrambi elenchi. indexdovrebbe essere buono per quello che vuoi.
Masclins,

@AlbertMasclans Whoops, risolto ora. Grazie per la cattura! Spero che questo non sia un altro dup ... Non voglio cancellare di nuovo questo post.
clismique,

Va

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"

Sceglie casualmente tra le tre opzioni in modo tale che l'avversario statisticamente non abbia preferenze tra le mosse per quanto riguarda il punteggio; in altre parole, sia Greedy che Not Hungry dovrebbero avere lo stesso punteggio medio previsto contro di esso.


1

Expectedbayes

Modifica: classifica aggiornata

Questa è la nuova classifica superiore dopo l'inclusione di Expectedbayes:

  • statistician2func 91.89%
  • fitterfunc 85.65%
  • nashfunc 80,40%
  • peso bilancia 76.39%
  • expectedbayesfunc 73,33%
  • antirepeaterfunc 68.52%
  • ...

spiegazioni

(NB: invio post 05/06/2017)

Questo bot cerca di massimizzare il valore atteso della sua prossima mossa:

  • Calcolo della probabilità per ciascuna delle prossime mosse possibili dell'avversario
  • Utilizzando quella cifra e i carichi per calcolare il valore atteso per ciascuno di R, P e S
  • Selezione dello spostamento con il valore atteso più elevato
  • Selezione casuale di un valore se la previsione non è riuscita

Le probabilità vengono aggiornate ogni dieci mosse. Il numero di mosse passate utilizzate per calcolare le probabilità è stato impostato su 10 per ciascun bot (quindi 20 funzioni in totale). Questo è probabilmente un eccesso di dati, ma non ho provato a controllare ulteriormente.

Si basa sulla libreria di scikit per calcolare le probabilità di mossa dell'avversario (lo dico nel caso in cui ho letto male le regole e in realtà non era permesso).

Vince facilmente contro i robot che fanno sempre la stessa scelta. Sorprendentemente, è abbastanza efficace contro il bot casuale con una percentuale di vincita del 93% (credo che ciò sia dovuto al fatto che limita il numero di punti che il suo avversario può ottenere massimizzando il proprio numero di punti possibili per ogni round).

Ho provato velocemente con 100 turni e solo un numero limitato di bot, ed è quello che ho ottenuto da result_standing:

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

Che non è poi così male!

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

Benvenuto in PPCG e bel primo post!
Zacharý,

Molte grazie! Volevo partecipare a PPCG da molto tempo. Ora è risolto!
lesibius,

0

Cycler

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

0


0

insieme

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)

Numerosi algoritmi concorrenti votano la soluzione migliore.

Scambiare

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)

Fa una mossa casuale, ma senza ripetere l'ultima mossa ha fatto.


0

blodsocer

socery

Gli ho dato una soluzione, quindi probabilmente dovrebbe funzionare ora spero

Ho sbagliato di nuovo qualcosa, quindi ho cancellato e cancellato. Sto facendo molti pasticci.

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"che tipo di società è questa?
Robert Fraser,

@DestructibleLemon Questo divide per 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 L'ho risolto.
Limone distruttibile

@RobertFraser cosa c'è di eccezionale in questo frammento di codice?
Limone distruttibile

@DestructibleLemon Non sono del tutto sicuro di cosa tu voglia fare qui: "RPS"[my_loaded.index(max(my_loaded))+len(my_history)%2]ma sembra fuori portata (e così anche le altre righe).
Masclins,

0

Casuale ponderato

Come RandomBot, ma ne sceglie solo 2 da lanciare ogni volta che viene invocato. A volte battere Rockstar o Assassin, ma aumenterà i punteggi dell'altro (ad esempio, se batte Rockstar, dà ad Assassin una spinta in punti).

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

Psicologo avido

Chiamato così perché di default è avido, ma se non può decidere, contrasta qualunque cosa l'avversario farebbe se usasse la strategia avida. Se ancora non riesce a decidere, va a caso.

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))]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.