KOTH - RPS carregado

12

Concurso aberto permanentemente - Atualizado em 10 de agosto de 2017

Embora em 5 de junho de 2017 tenha declarado vencedor (que será mantido como a melhor resposta), estarei criando novos bots e atualizando os resultados.

Resultados 5 de junho

Parabéns user1502040

Como não há empates, mostro apenas a% de partidas vencidas.

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%

Criei uma planilha do Google com a grade de resultados de cada pareamento: https://docs.google.com/spreadsheets/d/1KrMvcvWMkK-h1Ee50w0gWLh_L6rCFOgLhTN_QlEXHyk/edit?usp=sharing


Graças ao dilema de Petri , encontrei-me capaz de lidar com esse rei da colina.

O jogo

O jogo é uma simples "pedra-papel-tesoura" com um toque: os pontos ganhos a cada vitória aumentam durante a partida (seu R, P ou S são carregados).

  • Paper ganha Rock
  • Tesoura ganha Papel
  • Pedra vence Tesoura

O vencedor ganha tantos pontos quanto sua carga em seu jogo.

O perdedor aumenta em 1 a carga em seu jogo.

No caso de empate, cada jogador aumenta a carga em seu jogo em 0,5.

Após 100 jogadas, a que tiver mais pontos é o vencedor.

por exemplo: P1 possui cargas [10,11,12] (Pedra, papel, tesoura) e P2 [7,8,9]. P1 joga R, P2 joga P. P2 vence e ganha 8 pontos. As cargas P1 se tornam [11,11,12], as cargas P2 permanecem as mesmas.

Especificações do desafio

Seu programa deve ser escrito em Python (desculpe, não sei como lidar com isso de outra forma). Você deve criar uma função que aceite cada uma dessas variáveis ​​como argumento em cada execução:

my_points, opp_points, my_loaded, opp_loaded, my_history, opp_history

points - Pontos atuais (seu e seu opp)

loaded- Matriz com cargas (em ordem RPS) (sua e do seu opp)

history- String com todas as jogadas, o último personagem é a última jogada (sua e da sua opp)

Você deve retornar "R", "P"ou "S". Se você devolver algo diferente, seria uma perda automática da partida.

Regras

Você não pode alterar as funções internas.

Teste

Manterei um Git atualizado com o código e todos os bots competindo: https://github.com/Masclins/LoadedRPS

A julgar

O vencedor será decidido selecionando a pessoa com mais vitórias após 1000 rodadas completas. Os empates serão quebrados por jogos empatados. 1000 partidas estão sendo disputadas em vez de uma, porque espero muita aleatoriedade, e dessa forma a aleatoriedade seria menos relevante.

Você pode enviar até 5 bots.

O concurso termina no dia 4 de junho (que será o último dia em que eu aceito qualquer resposta) e, no dia 5 de junho, publicarei as classificações finais (tente publicar uma promoção antes).


Como este é o meu primeiro KOTH, estou 100% aberto a mudar qualquer coisa para melhorar, como o número de partidas disputadas contra cada bot.

Editado para 1000 correspondências, pois vejo que realmente existe muita aleatoriedade.

Masclins
fonte
com alguns bots aleatórios, você realmente quer fazer vários jogos de várias rodadas
Destrutível Lemon
@DestructibleLemon Pensei em fazer cada bot jogar três vezes um contra o outro em vez de uma vez. Vendo você pensar da mesma forma, eu farei isso.
Masclins
1
(realmente você precisa de um grande número justo, uma vez que alguns probabilites realmente estender-se por várias correspondências ver meu bot, onde poderia ficar arrasado, mas provavelmente não com uma boa quantidade de jogos.)
Destrutível Lemon
1
Estou feliz que minha pergunta tenha ajudado você a executar isso, @AlbertMasclans!
Gryphon
2
@AlbertMasclans Você pode publicar o teste completo (incluindo runcodee bots)?
CalculadoraFeline

Respostas:

8

Estatístico (não está mais jogando)

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

Alterna entre algumas estratégias simples com base no desempenho passado esperado

Estatí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 um equilíbrio aproximado de Nash por descida de gradiente.

user1502040
fonte
1
Eu realmente gosto dessa abordagem e entendo por que você gostaria de manter o estado entre as rodadas. Mesmo que eu considere um grande problema alterá-lo, devido ao número de envios. Levarei isso em consideração para outros desafios (o que espero fazer quando isso terminar).
Masclins
5

Pesador

Perdi a noção do raciocínio ao experimentar o código, mas a idéia básica é estimar a probabilidade de movimento do oponente pelos últimos 3 movimentos usando alguns pesos e multiplicá-los por outro peso que depende das cargas. Pensei que também pudesse usar de alguma forma my_loaded, mas não consegui decidir como, então deixei de fora.

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ás

Provavelmente será desqualificado, porque é meio trapaceiro e faz algumas suposições sobre a função de teste (ele precisa ter a função do oponente em uma variável no quadro da pilha), mas tecnicamente não quebra nenhuma regra atual - ele não redefinir ou reescrever qualquer coisa. Simplesmente usa magia negra para executar a função oponente e ver qual turno eles fizeram / farão. Não pode lidar com a aleatoriedade, mas os robôs deterministas não têm chance de derrotar 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
Nome em Exibição
fonte
Sem dúvida, o melhor em termos de simplicidade de resultados
Masclins
A propósito, para usar, my_loadedvocê pode adicionar um peso que valorize o movimento que perderia contra o (s) seu (s) último (s) movimento (s). É como assumir que seu oponente fará algo semelhante ao que você fez e, portanto, puni-lo por assumir que você continuará jogando da mesma forma. Algo como:for i, m in enumerate(reversed(my_history[-3:])): sc[(idx[m]+1)%3] += (K / (1 + i))
Masclins
@AlbertMasclans adicionou outra solução
Nome para exibição
1
Eu realmente gosto do de Satanás. Mas, como você disse, acredito que não deve se qualificar: mesmo que não quebre nenhuma regra explícita, é claramente contra o espírito do jogo. Ainda assim, parabéns pela sua ideia!
Masclins 25/05
4

Ajustador

Este bot melhora o Pattern e o funde com o Economist (o Pattern and Economist não participará mais)

A melhoria do Pattern é que o Bot agora procura dois dois tipos de padrões: Oponente reagindo à sua última jogada e o oponente reagindo à minha última jogada. Em seguida, avalia as duas previsões para usar a que se encaixa melhor.

A partir desse padrão, o Bot tem agora a probabilidade de R, P e S. Levando isso em conta e o valor esperado de cada jogada (como Economist fez), o Bot joga o que dá mais 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]

Aqui estão os dois códigos antigos

Padrão (não está mais sendo reproduzido)

O Padrão tenta encontrar padrões no seu oponente. Parece o que o oponente havia jogado após a última jogada que ele fez (dando mais peso às últimas jogadas). Com isso, adivinha o que o oponente jogará e joga a contra-partida.

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 (não está mais jogando)

O Economist faz o seguinte: Adivinha a probabilidade de cada jogada do oponente observando o que ele havia jogado nas últimas 9 jogadas. A partir disso, calcula o benefício esperado de cada jogada e segue com a que tem o melhor 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
fonte
4

Yggdrasil

Isso se chama "Yggdrasil" porque olha para o futuro na árvore do jogo. Esse bot não realiza nenhuma previsão do oponente, apenas tenta manter uma vantagem estatística se lhe for dada (equilibrando os lucros atuais e futuros). Ele calcula uma estratégia mista aproximadamente ideal e retorna um movimento selecionado aleatoriamente com esses pesos. Se esse bot fosse perfeito (o que não é, porque a função de avaliação de estado é muito ruim e não parece muito à frente), seria impossível vencê-lo mais de 50% do tempo. Eu não sei o quão bem esse bot vai se sair na prática.

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
fonte
por favor remover comentários que não fazem o código mais compreensível
Exibição Nome
@SargeBorsch done
PhiNotPi
1
@PhiNotPi Estou ciente de que não postei limitação de tempo, mas o Yggdrasil está levando mais de um minuto contra cada oponente. Seria possível otimizá-lo um pouco?
Masclins
sim, é insuportavelmente lento
Nome para exibição
@AlbertMasclans por minuto por oponente, você quer dizer 1 minuto no total para todos os jogos contra um oponente? Também posso tentar acelerar, mas realmente não sei como fazê-lo, ele apenas parece avançar 1 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"

Coleta de papel no primeiro turno, após o qual retorna o que for melhor do que o adversário fez mais, escolhendo papel em 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]

Simplesmente copia o último lance do oponente.

Anti-Anti-Ganancioso

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

Seleciona o que perde para a escolha mais pesada do oponente.

Um pouco com fome

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
fonte
3

O mensageiro

def themessengerfunc (eu não preciso destes argumentos): retorne "P"

Estrela do rock

def rockstarfunc (eu, não preciso, destes argumentos): retorne "R"

Assassino

def assassinfunc (eu, não preciso, desses argumentos): retorne "S"

Explicação

Agora, você pode pensar que esses robôs são totalmente estúpidos.

não é inteiramente verdade, eles são baseados na idéia de acumular um bônus enorme, e o inimigo dando um passo em falso e sendo atacado com ele.

agora, esses robôs jogam de maneira muito semelhante à gananciosa, no entanto, são mais simples e não pegam aleatoriamente até obter uma carga em uma arma, eles ficam com a arma de sua escolha.

Outra coisa a ser observada: cada uma delas vence a ganância por metade do tempo, atraindo um terço do tempo e perdendo um sexto do tempo. quando vencerem, tenderão a ganhar muito. por que é isso?

Ganancioso, até que ele perca uma rodada, escolherá aleatoriamente uma arma. isso significa que quando ele não vencer uma rodada, ele escolherá uma arma aleatoriamente novamente, o que poderia ser uma vitória novamente. se ganancioso empata ou perde, ele fica com essa arma. se ganancioso vence pelo menos uma rodada, escolhe a mesma arma que o bot, ganancioso ganha. se ganancioso escolher a arma perdida em algum momento, nosso bot vence, porque a carga em nossa arma teria sido maior do que a pontuação gananciosa.

Assumindo que ganancioso nem sempre escolhe a arma vencedora com grandes chances, isso significa que as chances são:

1/3: {1/2 vitória (1/6 total). 1/2 perda (1/6 total). }

1/3 empate

Vitória 1/3

então: 1/3 de chance de empate, 1/6 de chance de derrota, 1/2 de chance de ganhar.

isso provavelmente mostra que você precisa fazer vários jogos de várias rodadas

estes são principalmente para começar o desafio

Limão destrutível
fonte
3

Reator

Faz a jogada que teria vencido a rodada 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
fonte
1
Você pode substituir opp_history[len(opp_history)-1]por opp_history[-1].
CalculatorFeline
3

Criança artística

Esse bot age como uma criança que pratica artes e ofícios, começa com papel e usa papel ou tesoura aleatoriamente, mas não usa tesoura após pedra ou tesoura porque ela precisa usar a tesoura no papel. Jogará uma pedra de volta para quem jogar uma pedra nela.

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
fonte
2

Aqui estão os três Bots que construí para testar:


RandomBot

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

Ávido

Simplesmente escolhe a opção mais carregada.

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

Assume que o oponente jogará ganancioso e jogará a alternativa vencedora.

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

Not Hungry

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

Isso é literalmente o inverso de Greedy, escolhe a opção de pontos mais baixos disponível.

Pelicano-verde
fonte
1

Usar o favorito do 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 o primeiro turno, escolhe um item aleatório. Para todos os outros turnos, usa a escolha mais comum do oponente. Se houver um empate, o padrão é a primeira escolha mais comum.

// Eu roubei o código daqui


Ganhar é bom

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

Retorna a escolha do vencedor da rodada anterior. Se a rodada anterior foi um empate, verifica recursivamente a rodada antes disso. Se foram apenas empates, ou é a primeira rodada, retorna uma escolha aleatória.

Stephen
fonte
1

Melhor dos dois mundos

Este bot basicamente combina Anti-Greedy e Greedy (daí o 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
clismique
fonte
Este é o Antigreedy, já postado como exemplo.
Masclins
@AlbertMasclans Alterou para outro bot.
Clismique 25/05
findé para cordas. my_loadede opp_loadedsão as duas listas. indexdeve ser bom para o que você quer.
Masclins 25/05
@AlbertMasclans Whoops, corrigido agora. Obrigado pela captura! Espero que este não seja outro truque ... Não quero excluir esta postagem novamente.
Clismique 25/05
Tudo bem, obrigado por jogar.
Masclins 25/05
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"

Escolhe aleatoriamente entre as três opções, de forma que o oponente estatisticamente não tenha preferência entre os movimentos em relação ao valor em que está marcado; em outras palavras, tanto Greedy quanto Not Hungry devem ter a mesma pontuação média esperada.

Neil
fonte
1

Expectedbayes

Editar: Ranking Atualizado

Este é o novo ranking superior após a inclusão do Expectedbayes:

  • Statistician2func 91.89%
  • fitterfunc 85.65%
  • nashfunc 80,40%
  • weigherfunc 76.39%
  • 73% esperado
  • antirepeaterfunc 68.52%
  • ...

Explicações

(Nota: envio após 05/06/2017)

Este bot tenta maximizar o valor esperado de sua próxima jogada:

  • Calculando a probabilidade de cada próximo movimento possível do oponente
  • Usando essa figura e as cargas para calcular o valor esperado para cada um de R, P e S
  • Selecionando a movimentação que tem o maior valor esperado
  • Selecionando aleatoriamente um valor se a previsão falhou

As probabilidades são atualizadas a cada dez movimentos. O número de movimentos anteriores usados ​​para calcular as probabilidades foi definido como 10 para cada bot (portanto, 20 recursos no geral). Provavelmente, isso está ajustando demais os dados, mas não tentei verificar mais.

Ele se baseia na biblioteca do scikit para calcular as probabilidades de movimentação do oponente (estou dizendo isso no caso de eu ter lido errado as regras e, de fato, não era permitido).

Vence facilmente contra bots que sempre fazem a mesma escolha. Surpreendentemente, é bastante eficaz contra o bot aleatório com uma taxa de vitória de 93% (acredito que isso se deva ao fato de limitar o número de pontos que seu oponente pode obter e maximizar seu próprio número de pontos possíveis para cada rodada).

Fiz uma tentativa rápida com 100 turnos e apenas um número limitado de bots, e é isso que recebi de result_standing:

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

O que não é tão ruim assim!

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
fonte
Bem-vindo ao PPCG e bom primeiro post!
Zacharý 7/08
Muito obrigado! Eu queria participar do PPCG por um longo tempo. Agora está consertado!
lesibius
0

Cycler

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

0 0

CalculatorFeline
fonte
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)

Vários algoritmos concorrentes votam na melhor solução.

Troca

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)

Faz um movimento aleatório, mas sem repetir o último movimento que fez.

Magenta
fonte
0

blodsocer

socery

Eu dei uma correção, então provavelmente deve funcionar agora, espero

Eu estraguei algo de novo, então excluí e desfiz a exclusão. Estou fazendo muitas bagunças.

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ão destrutível
fonte
1
if opp_history[1] == "S": return "R" elif opp_history[1] == "R": return "R" else: return "P"que tipo de sociedade é essa?
Robert Fraser
@DestructibleLemon Isso 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 Eu consertei isso.
Destructible Lemon
@RobertFraser, o que exatamente há de destacado nesse trecho de código?
Destructible Lemon
@DestructibleLemon Não tenho muita certeza do que você queria fazer aqui: "RPS"[my_loaded.index(max(my_loaded))+len(my_history)%2]mas parece fora de alcance (e também as demais linhas).
Masclins
0

Aleatório ponderado

Como o RandomBot, mas ele escolhe apenas 2 para lançar cada vez que é invocado. Às vezes, vence o Rockstar ou o Assassin, mas aumenta as pontuações do outro (por exemplo, se vencer o Rockstar, aumenta o Assassin em pontos).

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

Psicólogo ganancioso

Nomeado como padrão, por ser ganancioso, mas se não puder decidir, será contrário ao que o adversário faria se usasse a estratégia gananciosa. Se ainda não puder decidir, será aleatoriamente.

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
fonte