KOTH - RPS cargado

12

Concurso abierto permanentemente - Actualizado el 10 de agosto de 2017

A pesar de que el 5 de junio de 2017 declaró un ganador (que se mantendrá como la mejor respuesta), estaré buscando nuevos bots y actualizando los resultados.

Resultados del 5 de junio

Felicidades usuario1502040

Como no hay empates, solo muestro el% de partidos ganados.

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%

Creé una hoja de Google con la cuadrícula de resultados de cada emparejamiento: https://docs.google.com/spreadsheets/d/1KrMvcvWMkK-h1Ee50w0gWLh_L6rCFOgLhTN_QlEXHyk/edit?usp=sharing


Gracias al Dilema de Petri, pude manejar a este Rey de la Colina.

El juego

El juego es un simple "Piedra-Papel-Tijera" con un giro: los puntos ganados con cada victoria aumentan durante el partido (su R, P o S se cargan).

  • El papel gana roca
  • Tijeras gana papel
  • Rock gana tijeras

El ganador obtiene tantos puntos como su carga en su juego.

El perdedor aumenta en 1 la carga en su juego.

En caso de empate, cada jugador aumenta la carga en su juego en 0.5.

Después de 100 jugadas, el que tenga más puntos es el ganador.

Por ejemplo: P1 tiene cargas [10,11,12] (piedra, papel, tijeras) y P2 [7,8,9]. P1 juega R, P2 juega P. P2 gana y obtiene 8 puntos. Las cargas P1 se convierten en [11,11,12], las cargas P2 permanecen igual.

Especificaciones de desafío

Su programa debe estar escrito en Python (lo siento, no sé cómo manejarlo de otra manera). Debe crear una función que tome cada una de estas variables como argumento en cada ejecución:

my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history

points - Puntos actuales (tuyo y tu opp)

loaded- Matriz con cargas (en orden RPS) (la suya y su opp)

history- Cadena con todas las jugadas, el último personaje es la última jugada (la tuya y tu opp)

Debes volver "R", "P"o "S". Si devolvieras algo diferente, sería una pérdida automática del partido.

Reglas

No puede cambiar las funciones integradas.

Pruebas

Mantendré un Git actualizado con el código y todos los bots que compitan: https://github.com/Masclins/LoadedRPS

Juzgar

El ganador se decidirá seleccionando a la persona con la mayor cantidad de partidos ganadores después de 1000 round-robin completos. Los empates se romperán por partidos empatados. Se juegan 1000 partidos en lugar de uno porque espero mucha aleatoriedad, y de esa manera la aleatoriedad sería menos relevante.

Puedes enviar hasta 5 bots.

El concurso finaliza el 4 de julio (que será el último día en que aceptaré cualquier respuesta), y el 5 de julio publicaré las clasificaciones finales (podría intentar publicar un avance antes).


Como este es mi primer KOTH, estoy 100% abierto a cambiar cualquier cosa para mejorar, como la cantidad de partidos jugados contra cada bot.

Editado a 1000 coincidencias, ya que veo que realmente hay bastante aleatoriedad involucrado.

Masclins
fuente
con algunos bots aleatorios, realmente quieres hacer múltiples juegos de múltiples rondas
Destructible Lemon
@DestructibleLemon Pensé en hacer que cada bot juegue tres veces contra el bot en lugar de una vez. Al verte pensar de manera similar, lo haré.
Masclins
1
(realmente necesitas un gran número, ya que algunas probabilidades realmente se extienden a través de múltiples partidos. Mira mi bot, donde podría ser derrotado, pero probablemente no lo haría con una buena cantidad de partidos)
Destructible Lemon
1
¡Me alegra que mi pregunta te haya ayudado a ejecutar esto, @AlbertMasclans!
Gryphon
2
@AlbertMasclans ¿Puedes publicar el script completo (incluyendo runcodey bots)?
CalculatorFeline

Respuestas:

8

Estadístico (ya no juega)

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

Cambia entre algunas estrategias simples basadas en el rendimiento pasado esperado

Estadístico 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]

Calcula un equilibrio aproximado de Nash por gradiente de descenso.

user1502040
fuente
1
Realmente me gusta este enfoque, y puedo entender por qué querrías poder mantener el estado entre rondas. Aunque veo que es un gran problema cambiarlo dada la cantidad de envíos. Lo tendré en cuenta para otros desafíos (que espero hacer cuando esto termine).
Masclins
5

Pesador

Perdí la noción del razonamiento mientras experimentaba con el código, pero la idea básica es estimar la probabilidad de movimiento del oponente en los últimos 3 movimientos usando algunos pesos y multiplicarlos por otro peso que depende de las cargas. Pensé que también podría usarlo de alguna manera my_loaded, pero no podía decidir cómo, así que lo dejé fuera.

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

Satán

Probablemente será descalificado, porque es una especie de trampa y hace algunas suposiciones sobre la función de prueba (tiene que tener la función del oponente en una variable en su marco de pila), pero técnicamente no rompe las reglas actuales, no lo hace. redefinir o reescribir cualquier cosa. Simplemente usa magia negra para ejecutar la función del oponente para ver qué turno hicieron / harán. No puede lidiar con la aleatoriedad, pero los bots deterministas no tienen oportunidad de derrotar a Satanás.

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
Nombre para mostrar
fuente
Sin duda el mejor en términos de simplicidad-resultados
Masclins
Por cierto, para usar my_loadedpodría agregar un peso que valore el movimiento que perdería contra su último movimiento (s). Es como asumir que tu oponente hará algo similar a lo que hiciste y, por lo tanto, castigarlo por asumir que seguirás jugando lo mismo. Algo así como:for i, m in enumerate(reversed(my_history[-3:])): sc[(idx[m]+1)%3] += (K / (1 + i))
Masclins
@AlbertMasclans agregó otra solución
nombre para mostrar
1
Realmente me gusta el de Satanás. Pero como dijiste, creo que no debería calificar: incluso si no rompe ninguna regla explícita, está claramente en contra del espíritu del juego. Aún así, ¡felicidades por tu idea!
Masclins
4

Ajustador

Este bot mejora el patrón y lo fusiona con Economist (Pattern and Economist ya no participará)

La mejora del Patrón es que el Bot ahora busca dos tipos de patrones: el oponente que reacciona a su última jugada y el oponente que reacciona a mi última jugada. Luego evalúa ambas predicciones para usar la que mejor se ajuste.

A partir de ese patrón, el Bot tiene ahora la probabilidad de R, P y S. Teniendo eso en cuenta y el valor esperado de cada jugada (como hizo Economist), el Bot juega el que da el mayor valor.

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]

Aquí están los dos códigos antiguos.

Patrón (ya no se reproduce)

El Patrón intenta encontrar patrones en su oponente. Se ve lo que el oponente había jugado después de la última jugada que hizo (dando más peso a las últimas jugadas). A través de eso, adivina lo que jugará el oponente, y juega el contraataque a eso.

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 (ya no juega)

The Economist hace lo siguiente: Adivina la probabilidad de cada jugada del oponente observando lo que había jugado los últimos 9 turnos. A partir de eso, calcula el beneficio esperado de cada jugada y va con el que tiene el mejor valor esperado.

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
fuente
4

Yggdrasil

Esto se llama "Yggdrasil" porque mira hacia adelante en el árbol del juego. Este bot no realiza ninguna predicción del oponente, simplemente intenta mantener una ventaja estadística si se le da una (equilibrando las ganancias actuales y futuras). Calcula una estrategia mixta aproximadamente ideal y devuelve un movimiento seleccionado al azar con esos pesos. Si este bot fuera perfecto (que no lo es, porque la función de valoración del estado es bastante mala y no se ve muy lejos), entonces sería imposible vencer a este bot más del 50% del tiempo. No sé qué tan bien le irá a este bot en la práctica.

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
fuente
elimine los comentarios que no hacen que el código sea más comprensible
Nombre para mostrar
@SargeBorsch hecho
PhiNotPi
1
@PhiNotPi Soy consciente de que no publiqué límite de tiempo, pero Yggdrasil está tomando más de un minuto contra cada oponente. ¿Sería posible optimizarlo un poco?
Masclins
sí, es insoportablemente lento
Nombre para mostrar
@AlbertMasclans por minuto por oponente, ¿quieres decir 1 minuto en total para todos los juegos contra un oponente? También puedo intentar acelerarlo, pero realmente no sé cómo hacerlo, solo se ve 1 movimiento como está.
PhiNotPi
4

Anti-Repetidor

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"

Selecciona papel en el primer turno, después de lo cual devuelve lo que sea mejor que lo que el oponente haya hecho más, recogiendo papel en caso de empate.

Imitador

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]

Simplemente copia el último movimiento de los oponentes.

Anti-Anti-codicioso

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

Elige lo que pierde ante la elección más pesada del oponente.

Algo hambriento

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"])
Grifo
fuente
3

El mensajero

def themessengerfunc (yo, no, necesito, estos, argumentos): devuelve "P"

Estrella de rock

def rockstarfunc (yo, no, necesito, estos, argumentos): devuelve "R"

Asesino

def assassinfunc (yo, no, necesito, estos, argumentos): devuelve "S"

Explicación

Ahora, puedes pensar que estos bots son completamente estúpidos.

no del todo cierto, estos en realidad se basan en la idea, de acumular una gran bonificación, y el enemigo hace un paso en falso y es golpeado con él.

ahora, estos bots juegan de manera muy similar a los codiciosos, sin embargo, son más simples y no se seleccionan al azar hasta que obtienen una carga en un arma, se quedan con su arma de elección.

Otra cosa a tener en cuenta: cada uno de ellos vencerá a los codiciosos aproximadamente la mitad del tiempo, atrayendo un tercio del tiempo y perdiendo un sexto del tiempo. cuando ganen, tenderán a ganar mucho. ¿Por qué es esto?

Codicioso, hasta que pierda una ronda, elegirá un arma al azar. Esto significa que cuando no gane una ronda, volverá a elegir un arma al azar, que podría volver a ser ganadora. si el codicioso empata o pierde, se queda con esa arma. si codicioso gana al menos una ronda, luego elige la misma arma que el bot, codicioso gana. si codicioso elige el arma perdedora en algún momento, nuestro bot gana, porque la carga en nuestra arma habría sido más alta que la puntuación que codicioso tiene.

Asumir que codicioso no siempre elige el arma ganadora a través de una gran oportunidad, esto significará que las posibilidades son:

1/3: {1/2 victoria (1/6 total). 1/2 perder (1/6 en total). }

1/3 de sorteo

1/3 de victoria

Entonces: 1/3 de probabilidad de empatar, 1/6 de probabilidad de pérdida, 1/2 de probabilidad de ganar.

esto probablemente muestra que necesitas hacer múltiples juegos de múltiples rondas

estos son principalmente para poner en marcha el desafío

Limón Destructible
fuente
3

Reactor

Realiza la jugada que habría ganado la ronda anterior.

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
fuente
1
Se puede reemplazar opp_history[len(opp_history)-1]con opp_history[-1].
CalculatorFeline
3

Niño artístico

Este bot actúa como un niño jugando artes y manualidades, comenzará con papel y usará papel o tijeras al azar, pero no usará tijeras después de piedra o tijeras porque necesita usar las tijeras en papel. Le arrojará una piedra a cualquiera que la arroje a ella.

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"])
Tito Lucrecio
fuente
2

Aquí los tres Bots que he creado para probar:


RandomBot

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

Codicioso

Simplemente elige su opción más cargada.

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

Antigueria

Asume que el oponente jugará con avaricia y juega la alternativa ganadora.

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
fuente
1

Sin hambre

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

Esto es literalmente lo contrario de Greedy, elige la opción de puntos más bajos disponible.

Pelícano verde azulado
fuente
1

Use el favorito del oponente

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

Para el primer turno, elige un elemento aleatorio. Para cada otro turno, usa la opción más común del oponente. Si hay un empate, el valor predeterminado es la primera opción más común.

// Robé el código de aquí


Ganar es bueno

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

Devuelve la elección del ganador de la ronda anterior. Si la ronda anterior fue un empate, verifica recursivamente la ronda anterior. Si fue solo empate, o es la primera ronda, devuelve una elección aleatoria.

Stephen
fuente
1

Lo mejor de ambos mundos

Este bot básicamente combina Anti-Greedy y Greedy (de ahí el nombre).

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
fuente
Esta es la Antigredía, ya publicada como ejemplo.
Masclins
@AlbertMasclans lo cambió a otro bot.
clismique
findes para cuerdas. my_loadedy opp_loadedson ambas listas. indexdebería ser bueno para lo que quieres.
Masclins
@AlbertMasclans Whoops, arreglado ahora. Gracias por la captura! Espero que este no sea otro dup ... No quiero eliminar esta publicación nuevamente.
clismique
Esto está bien, gracias por jugar
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"

Elige aleatoriamente entre las tres opciones de tal manera que el oponente estadísticamente no tiene preferencia entre movimientos con respecto a cuánto anota; en otras palabras, tanto Greedy como Not Hungry deberían tener el mismo puntaje promedio esperado en su contra.

Neil
fuente
1

EBays esperados

Editar: Clasificación actualizada

Este es el nuevo ranking superior después de la inclusión de Expectedbayes:

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

Explicaciones

(Nota: envío posterior al 05/06/2017)

Este bot intenta maximizar el valor esperado de su próximo movimiento:

  • Calcular la probabilidad de cada uno de los siguientes movimientos posibles del oponente
  • Usando esa cifra y las cargas para calcular el valor esperado para cada uno de R, P y S
  • Seleccionar el movimiento que tiene el valor más alto esperado
  • Selección aleatoria de un valor si la predicción falla

Las probabilidades se actualizan cada diez movimientos. El número de movimientos pasados ​​utilizados para calcular las probabilidades se ha establecido en 10 para cada bot (es decir, 20 características en general). Probablemente esto esté sobreajustando los datos, pero no intenté verificar más.

Se basa en la biblioteca de scikit para calcular las probabilidades de movimiento del oponente (lo digo en caso de que haya leído mal las reglas y, de hecho, no estaba permitido).

Fácilmente gana contra bots que siempre toman la misma decisión. Sorprendentemente, es bastante efectivo contra el bot aleatorio con una tasa de ganancia del 93% (creo que esto se debe al hecho de que limita el número de puntos que puede obtener su oponente mientras maximiza su propio número de puntos posibles para cada ronda).

Intenté rápidamente con 100 turnos y solo un número limitado de bots, y esto es lo que obtuve de result_standing:

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

¡Lo cual no es tan malo!

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
fuente
¡Bienvenido a PPCG, y buena primera publicación!
Zacharý
¡Muchas gracias! Quería participar en PPCG por mucho tiempo. Ahora está arreglado!
lesibius
0

Ciclista

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

0 0

CalculadoraFeline
fuente
0

Conjunto

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)

Varios algoritmos competidores votan sobre la mejor solución.

Intercambiar

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)

Hace un movimiento aleatorio, pero sin repetir el último movimiento lo hizo.

Magenta
fuente
0

blodsocer

socery

Le di una solución, así que probablemente debería funcionar ahora, espero

Arruiné algo de nuevo, así que lo borré y lo borré. Estoy haciendo muchos problemas.

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]
Limón Destructible
fuente
1
if opp_history[1] == "S": return "R" elif opp_history[1] == "R": return "R" else: return "P"¿Qué clase de socery es este?
Robert Fraser
@DestructibleLemon Esto se divide por 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 lo arreglé.
Destructible Lemon
@RobertFraser, ¿qué es lo que sobresale de ese fragmento de código?
Destructible Lemon
@DestructibleLemon No estoy completamente seguro de lo que quería hacer aquí: "RPS"[my_loaded.index(max(my_loaded))+len(my_history)%2]pero parece fuera de rango (y también lo harán las líneas adicionales).
Masclins
0

Aleatorio ponderado

Al igual que RandomBot, pero elige solo 2 para lanzar cada vez que se invoca. A veces vencerá a Rockstar o Assassin, pero aumentará los puntajes del otro (por ejemplo, si vence a Rockstar, le da a Assassin un aumento de puntos).

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
fuente
0

Psicólogo codicioso

Nombrado eso porque por defecto es codicioso, pero si no puede decidir, contrarresta lo que sea que haga el oponente si usara la estrategia codiciosa. Si aún no puede decidir, va al azar.

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
fuente