1P5: Dilema del prisionero iterado

35

Esta tarea forma parte del primer empuje de programación periódica Premier Puzzle y está destinada a ser una demostración de la nueva propuesta de tipo desafío del .

La tarea es escribir un programa para jugar el dilema del prisionero iterado mejor que otros entrantes.

Mira, Vinny. Conocemos a tu compañero de celda --- ¿cómo se llama? Sí, McWongski, el mafioso nippo-irlandés-ucraniano, está tramando algo y ya sabes lo que es.

Estamos tratando de ser amables aquí, Vinnie. Dándote una oportunidad.

Si nos dice lo que está planeando, veremos que obtenga una buena asignación de trabajo.

Y si no lo haces ...

Las reglas del juego

  • El concurso consiste en un round-robin completo (todas las parejas posibles) de dos concursantes a la vez (incluidas las jugadas propias).
  • Hay 100 rondas jugadas entre cada par
  • En cada ronda se le pide a cada jugador que elija entre cooperar con el otro jugador o traicionarlo, sin conocer las intenciones de los otros jugadores en el asunto, pero con un recuerdo de los resultados de las rondas anteriores jugadas contra este oponente.
  • Se otorgan puntos por cada ronda en función de la elección combinada. Si ambos jugadores cooperan, cada uno obtiene 2 puntos. La traición mutua produce 1 punto cada uno. En el caso mixto, el jugador que traiciona recibe 4 puntos y el cooperador es penalizado por 1.
  • Una partida "oficial" se ejecutará a más tardar 10 días después de la publicación con todas las presentaciones que pueda poner a trabajar y se utilizará para seleccionar el ganador "aceptado". Tengo una caja Mac OS 10.5, por lo que las soluciones POSIX deberían funcionar, pero hay linuxismos que no funcionan. Del mismo modo, no tengo soporte para la API win32. Estoy dispuesto a hacer un esfuerzo básico para instalar cosas, pero hay un límite. Los límites de mi sistema de ninguna manera representan los límites de las respuestas aceptables, simplemente las que se incluirán en la coincidencia "oficial".

La interfaz del programador

  • Las entradas deben tener la forma de programas que se pueden ejecutar desde la línea de comandos; la decisión debe ser la salida (única) del programa en la salida estándar. La historia de rondas anteriores con este oponente se presentará como un argumento de línea de comando.
  • Salida es "c" (por callarse ) o "T" (para decir a todos ).
  • La historia es una sola cadena de caracteres que representan rondas previas con las rondas más recientes venir antes en la cadena. Los personajes son
    • "K" (por guardado la fe significa la cooperación mutua)
    • "R" (para rata b @ st @ rd me entradas agotadas )
    • "S" (por lechón! Lo que significa que se benefició de una traición)
    • "E" (para todo el mundo está mirando hacia fuera para el número uno en la traición mutua)

el soporte

Cuatro jugadores serán proporcionados por el autor

  • Ángel - coopera siempre
  • Diablo - siempre habla
  • TitForTat: coopera en la primera ronda y siempre hace lo que hizo en la última ronda
  • Aleatorio - 50/50

a lo que agregaré todas las entradas que pueda ejecutar.

El puntaje total será el puntaje total contra todos los oponentes (incluidas las jugadas individuales solo una vez y usando el puntaje promedio).

Entrantes

(vigente a partir del 2 de mayo de 2011 7:00)

El apretón de manos secreto | Anti-misiles T42T | La desconfianza (variante) | Anti-apretón de manos | El pequeño ceceoso | convergencia | tiburón | Probabimatic | Pavlov - Win Stay, interruptor Pierde | Honor entre ladrones | Ayuda vampiro | druida | Poco Schemer | Lo pasado | Dan las dos Tats | Simpleton |

Goleador

#! /usr/bin/python
#
# Iterated prisoner's dilemma King of Hill Script Argument is a
# directory. We find all the executables therein, and run all possible
# binary combinations (including self-plays (which only count once!)).
#
# Author: dmckee (https://codegolf.stackexchange.com/users/78/dmckee)
#
import subprocess 
import os
import sys
import random
import py_compile

###
# config
PYTHON_PATH = '/usr/bin/python' #path to python executable

RESULTS = {"cc":(2,"K"), "ct":(-1,"R"), "tc":(4,"S"), "tt":(1,"E")}

def runOne(p,h):
    """Run process p with history h and return the standard output"""
    #print "Run '"+p+"' with history '"+h+"'."
    process = subprocess.Popen(p+" "+h,stdout=subprocess.PIPE,shell=True)
    return process.communicate()[0]

def scoreRound(r1,r2):
    return RESULTS.get(r1[0]+r2[0],0)

def runRound(p1,p2,h1,h2):
    """Run both processes, and score the results"""
    r1 = runOne(p1,h1)
    r2 = runOne(p2,h2)
    (s1, L1), (s2, L2) = scoreRound(r1,r2), scoreRound(r2,r1) 
    return (s1, L1+h1),  (s2, L2+h2)

def runGame(rounds,p1,p2):
    sa, sd = 0, 0
    ha, hd = '', ''
    for a in range(0,rounds):
        (na, ha), (nd, hd) = runRound(p1,p2,ha,hd)
        sa += na
        sd += nd
    return sa, sd


def processPlayers(players):
    for i,p in enumerate(players):
        base,ext = os.path.splitext(p)
        if ext == '.py':
            py_compile.compile(p)
            players[i] = '%s %sc' %( PYTHON_PATH, p)
    return players

print "Finding warriors in " + sys.argv[1]
players=[sys.argv[1]+exe for exe in os.listdir(sys.argv[1]) if os.access(sys.argv[1]+exe,os.X_OK)]
players=processPlayers(players)
num_iters = 1
if len(sys.argv) == 3:
    num_iters = int(sys.argv[2])
print "Running %s tournament iterations" % (num_iters)
total_scores={}
for p in players:
    total_scores[p] = 0
for i in range(1,num_iters+1):
    print "Tournament %s" % (i)
    scores={}
    for p in players:
        scores[p] = 0
    for i1 in range(0,len(players)):
        p1=players[i1];
        for i2 in range(i1,len(players)):
            p2=players[i2];
#        rounds = random.randint(50,200)
            rounds = 100
            #print "Running %s against %s (%s rounds)." %(p1,p2,rounds)
            s1,s2 = runGame(rounds,p1,p2)
            #print (s1, s2)
            if (p1 == p2):
                scores[p1] += (s1 + s2)/2
            else:
                scores[p1] += s1
                scores[p2] += s2

    players_sorted = sorted(scores,key=scores.get)
    for p in players_sorted:
        print (p, scores[p])
    winner = max(scores, key=scores.get)
    print "\tWinner is %s" %(winner)
    total_scores[p] += 1
print '-'*10
print "Final Results:"
players_sorted = sorted(total_scores,key=total_scores.get)
for p in players_sorted:
    print (p, total_scores[p])
winner = max(total_scores, key=total_scores.get)
print "Final Winner is " + winner
  • Las quejas sobre mi horrible pitón son bienvenidas, ya que estoy seguro de que esto apesta en más de una forma
  • correcciones de errores dan la bienvenida

Anotador de cambios:

  • Imprima jugadores y puntajes ordenados y declare un ganador (29/04, Casey)
  • Opcionalmente, ejecute torneos múltiples ( ./score warriors/ num_tournaments)) predeterminado = 1, detecte y compile fuentes de Python (4/29, Casey)
  • Se corrigió un error particularmente tonto en el que al segundo jugador se le pasaba un historial incorrecto. (30/4, dmckee; gracias Josh)

Guerreros iniciales

A modo de ejemplo, y para que los resultados puedan verificarse

Ángel

#include <stdio.h>
int main(int argc, char**argv){
  printf("c\n");
  return 0;
}

o

#!/bin/sh
echo c

o

#!/usr/bin/python
print 'c'

Diablo

#include <stdio.h>
int main(int argc, char**argv){
  printf("t\n");
  return 0;
}

Aleatorio

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char**argv){
  srandom(time(0)+getpid());
  printf("%c\n",(random()%2)?'c':'t');
  return 0;
}

Tenga en cuenta que el anotador puede volver a invocar al guerrero muchas veces en un segundo, por lo que se debe hacer un esfuerzo serio para asegurar la aleatoriedad de los resultados si se usa el tiempo para sembrar el PRNG.

TitForTat

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char**argv){
  char c='c';
  if (argv[1] && (
          (argv[1][0] == 'R') || (argv[1][0] == 'E')
          ) ) c='t';
  printf("%c\n",c);
  return 0;
}

El primero que realmente hace algo con la historia.

Ejecutar el anotador solo en los rendimientos de guerreros provistos

Finding warriors in warriors/
Running warriors/angel against warriors/angel.
Running warriors/angel against warriors/devil.
Running warriors/angel against warriors/random.
Running warriors/angel against warriors/titfortat.
Running warriors/devil against warriors/devil.
Running warriors/devil against warriors/random.
Running warriors/devil against warriors/titfortat.
Running warriors/random against warriors/random.
Running warriors/random against warriors/titfortat.
Running warriors/titfortat against warriors/titfortat.
('warriors/angel', 365)
('warriors/devil', 832)
('warriors/random', 612)
('warriors/titfortat', 652)

Ese demonio, es un artesano, y los buenos chicos aparentemente son los últimos.

Resultados

de la carrera "oficial"

('angel', 2068)
('helpvamp', 2295)
('pavlov', 2542)
('random', 2544)
('littleschemer', 2954)
('devil', 3356)
('simpleton', 3468)
('secrethandshake', 3488)
('antit42t', 3557)
('softmajo', 3747)
('titfor2tats', 3756)
('convergence', 3772)
('probabimatic', 3774)
('mistrust', 3788)
('hyperrationalwasp', 3828)
('bygones', 3831)
('honoramongthieves', 3851)
('titfortat', 3881)
('druid', 3921)
('littlelisper', 3984)
('shark', 4021)
('randomSucker', 4156)
('gradual', 4167)
        Winner is ./gradual
dmckee
fuente
2
Si mi compañero de celda es nippo-irlandés-ucraniano, ¿por qué su nombre parece hiberno-chino-ruso?
Peter Taylor
2
@Peter: LOL. ¿La verdad? Bueno, (1) las genealogías no son claras, pero probablemente me tope con mi micheed por medio del escocés-irlandés; (2) después de escribir "Nippo" probé varios fragmentos de los nombres de mis amigos de la tierra del sol naciente y no me gustó la forma en que escanearon, así que seguí adelante y usé un apellido chino que sonaba bueno en cambio, y (3) no sabría la diferencia si se turnaran para golpearme con planchas de neumáticos. Lo que parece probable bajo las circunstancias.
dmckee
1
@ Josh: ¿Sería sencillo cambiar return (s1, L1+h1), (s2, L2+h1)a return (s1, L1+h1), (s2, L2+h2)[Nota en L2+h2lugar de L2+h1al final]? // Error de cortar y pegar o algo igualmente idiota. Sheesh!
dmckee
2
Pasé algún tiempo en el script de prueba y me complace anunciar una actualización aquí . Esta actualización agrega un shell simple al script de prueba, que permite al usuario ejecutar manualmente este bot contra ese bot, ejecutar torneos con campos restringidos y algunas otras cosas interesantes. ¡Siéntete libre de hacer sugerencias! Oh. Y le debo a @josh la idea bot-vs-bot. Realmente es solo una implementación más elegante de su script de "entrenador".
llega
2
Interesante: hubo 23 concursantes, por lo que cada uno jugó 22 rondas. Si todos hubieran jugado "Ángel", cada puntaje hubiera sido 4400, pero incluso el mejor puntaje de 4167 no coincidió con eso. Si tan solo
viviéramos

Respuestas:

11

Gradual

Esta estrategia se basa en un artículo de Beaufils, Delahaye y Mathieu . Mi C realmente no es la mejor, así que si alguien tiene alguna sugerencia para mejorar / acelerar el código, ¡hágamelo saber!

[Editar] Vale la pena señalar que Gradual fue diseñado para ser una estrategia que supera a Tit para Tat. Tiene propiedades similares en el sentido de que está dispuesto a cooperar y tomar represalias contra un oponente defectuoso. A diferencia de Tit for Tat, que solo tiene un recuerdo de la última ronda jugada, Gradual recordará la interacción completa y desertará la cantidad de veces que el oponente ha desertado hasta ahora. Sin embargo, ofrecerá cooperación mutua luego nuevamente.

Como de costumbre, el desempeño de la estrategia depende un poco de la alineación de otras estrategias. Además, el documento original no era realmente claro sobre algunos detalles.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[]) {
    if(argc == 1){
        printf("c\n");
        return 0;
    }

    size_t l = strlen(argv[1]);
    int i;
    size_t currentSequence = 0;
    size_t totalDefects = 0;
    size_t lastDefects = 0;

    for(i = l-1; i >= 0; i--){
        if(argv[1][i] == 'E' || argv[1][i] == 'R'){
            totalDefects++;
            currentSequence = 0;
        } else if(argv[1][i] == 'S') {
            currentSequence++;
        }
    }

    if(currentSequence < totalDefects)
        // continue defect sequence
        printf("t\n");
    else if(argv[1][0] == 'S' || argv[1][0] == 'E' ||
            argv[1][1] == 'S' || argv[1][1] == 'E')
        // blind cooperation
        printf("c\n");
    else if(argv[1][0] == 'R')
        // start new defect sequence
        printf("t\n");
    else
        printf("c\n");

    return 0;
}
Ventero
fuente
11

El apretón de manos secreto

#!/usr/bin/python
import sys
import random

def main():
    if len(sys.argv) == 1:
        hist = ""
    else:
        hist = sys.argv[1]
    if len(hist) <= len(TAG) and hist == TAGMATCH[len(TAG) - len(hist):]:
        print TAG[len(TAG) - len(hist) - 1]
        return
    if hist[-len(TAG):] == TAGMATCH:
        print 'c'
        return
    print "t"

def getTag():
    global TAG
    filename = sys.argv[0]
    filename = filename.replace(".pyc", ".py")
    f = open(filename, 'r')
    code = f.read().split('\n')
    f.close()
    if len(code[1]) == 0 or code[1][0] != '#':
        random.seed()
        newtag = 't' * 10
        cs = 0
        while cs < 3:
            pos = random.randint(0, 8)
            if newtag[pos] == 't':
                newtag = newtag[:pos] + 'c' + newtag[pos+1:]
                cs += 1
        code.insert(1, '#%s' % newtag)
        f = open(filename, 'w')
        f.write('\n'.join(code))
        f.close()
        TAG = newtag
    else:
        TAG = code[1][1:]
    global TAGMATCH
    TAGMATCH = TAG.replace('c', 'K').replace('t', 'E')

if __name__ == "__main__":
    getTag()
    main()

La estrategia aquí es sacrificar las primeras 10 rondas para realizar un apretón de manos "secreto". Si estoy celulado conmigo mismo, entonces reconozco la historia de los primeros 10 movimientos y me pongo mi gorra de Ángel por el resto del juego. Tan pronto como reconozco que mi compañero de celda no soy yo mismo, me transformo en un Demonio en un intento de aprovechar a los compañeros de celda demasiado cooperativos.

Si sacrificar las primeras 10 rondas me permitirá superar al Diablo, depende en gran medida de cuántas entradas haya. Para minimizar el daño, solo 3 cooperaciones aparecen en el apretón de manos.

Editar: TAGMATCH dinámico ahora para evitar errores estúpidos como cambiar solo uno de ellos y así puedo hacer que TAG sea dinámico en algún momento en el futuro.

Edición 2: ahora genera aleatoriamente la etiqueta en la primera ejecución y la almacena en el archivo especificado por sys.argv[0] ( .pycreemplazado por .pylo que va al código, no al código de bytes, archivo). Creo que esta es la única información que tienen todas mis instancias que nadie más tiene, por lo que parece ser la única opción para evitar los parásitos.

Aaron Dufour
fuente
Pero, ¿cómo sabe tu doble para convertirse en un demonio?
llega el
1
(Me siento como un loro, diciendo "Tit for Tat" todo el tiempo ...) Tenga en cuenta que T4T vencerá su estrategia en un emparejamiento contra: T4T (coopera antes) y Devil (menos resultados de Rata), y se unirá a su estrategia. Por supuesto, el gran total, no el total de emparejamiento, es lo que cuenta al final. Como dices, la población es importante.
Josh Caswell el
1
Oh, no, obtienes una S extra de Tit por Tat. Agradable. No me di cuenta de que TAGse estaba jugando al revés. Sin embargo, ¿no deberías TAGMATCHser 'KEEEKEEEKE'? "".join({('c', 'c'):'K', ('t', 't'): 'E'}[moves] for moves in zip(TAG, TAG))
Josh Caswell el
@John Buen punto: originalmente tenía un TAG diferente y cuando lo cambié para minimizar la cooperación, olvidé actualizar TAGMATCH. @Arrdem La idea es que si estoy jugando contra mí mismo, lo mejor que puedo hacer es que ambos cooperen todo el tiempo para maximizar la suma de sus puntajes.
Aaron Dufour
1
Aww, maldita sea. Así que ahora tengo que buscar en todos los .pyarchivos su código y extraer la etiqueta. Sin embargo, no haré eso en C ...
Joey
6

The Little Lisper

(setf *margin* (/ (+ 40 (random 11)) 100))
(setf *r* 0.0)
(setf *s* 0.0)
(setf *k* 0.0)
(setf *e* 0.0)

;; step 1 - cout up all the games results

(loop for i from 1 to (length(car *args*)) do
    (setf foo (char (car *args*) (1- i)))
    (cond 
        ((equal foo #\R) (setf *r* (1+ *r*)))
        ((equal foo #\S) (setf *s* (1+ *s*)))
        ((equal foo #\K) (setf *k* (1+ *k*)))
        ((equal foo #\E) (setf *e* (1+ *e*)))
    )
)

(setf *sum* (+ *r* *s* *k* *e*))

;; step 2 - rate trustworthiness
(if (> *sum* 0)
    (progn
        (setf *dbag* (/ (+ *r* *e*) *sum*)) ; percentage chance he rats
        (setf *trust* (/ (+ *s* *k*) *sum*)); percentage chance he clams
    )
    (progn
        (setf *dbag* 0) ; percentage chance he rats
        (setf *trust* 0); percentage chance he clams
    )
)



;; step 3 - make a decision (the hard part....)

(write-char
    (cond
        ((> *sum* 3) (cond 
                    ((or (= *dbag* 1) (= *trust* 1)) #\t) ; maximizes both cases
                                                          ; takes advantage of the angel, crockblocks the devil
                    ((> (+ *dbag* *margin*) *trust*) #\t) ; crockblock statistical jerks
                    ((< *dbag* *trust*) #\c)              ; reward the trusting (WARN - BACKSTABBING WOULD IMPROVE SCORE)
                    ((and
                        (= (floor *dbag* *margin*) (floor *trust* *margin*))
                        (not (= 0 *dbag* *trust*)))
                        #\t)                              ; try to backstab a purely random opponent, avoid opening w/ a backstab
                    )
        )
        (t #\c)                                            ; defalt case - altruism
    )
)

El diablo

Considere el siguiente formato (Player1, Player2)

  • (C, T) - P2 gana CUATRO PUNTOS por su traición, mientras que P1 pierde UNO
  • (T, T) - P2 Y P1 GANANCIA 1

Suponiendo que P2 es el diablo, no hay forma de que el diablo pueda perder puntos, de hecho, lo peor que puede hacer es ganar solo un punto. Por lo tanto, frente a un oponente puramente aleatorio, la peor puntuación posible del diablo será exactamente (5/2) * n donde n es el número de "juegos" jugados. Su peor caso absoluto es contra sí mismo, donde su puntaje será n, y su mejor caso es contra un ángel, que será 4 * n

Afirmar: optical_strat = devil

Este es un torneo. Apuñalar a mi compañero de celda es una estrategia mucho mejor que la cooperación porque ayuda a MI PUNTUACIÓN más (+4). BONIFICACIÓN: ¡se estrelló (-1)! Si le saco el cuello por él, puedo ganar (+2) y perder (-1). Por lo tanto, estadísticamente la puñalada es recompensada.

¿Pero es óptimo?

No hay ninguna razón para cooperar NUNCA (bajo este sistema de puntuación).

  • Si elige el momento equivocado para callarse, pierde.
  • Si ratas, al menos no pierdes nada.
  • Si ratas y él es tonto, ganas 2 veces más que si hubieras sido un buen pálido.

En el sistema KOTH, la maximización de los retornos es esencial. Incluso si tiene dos bots que se sincronizan perfectamente y cooperan, sus puntajes individuales solo se verán aumentados en 200 puntos por su espíritu deportivo. Un demonio, por otro lado, ganará al menos 100 puntos, con un caso promedio de 200 y un máximo de 400, ¡y le costará a sus oponentes hasta 100 puntos cada uno! Entonces, prácticamente, el diablo realmente anota un juego promedio de 300, llegando a 500.

En pocas palabras: el tiempo dirá

Para mí, parece que la puntuación debería reconsiderarse para que el diablo no se tome el día. Aumentar el puntaje de cooperación a 3 todos podría hacerlo. Sin embargo, es posible detectar demonios y evitar que obtengan sus 400 completos, como muestran pavlov y rencor. ¿Puedo probar que cualquiera de los dos obtendrá suficientes puntos para su cooperación para justificar su fe? no. Todo eso depende del campo final de los contendientes.

GL, HF!

Y por favor, haz lo peor con esta publicación. Quiero escribir mi trabajo principal sobre esto cuando todo esté dicho y hecho.

Historial de versiones

  1. Se agregó una variable de margen que cambia la tolerancia de Lisper para duchebaggery al azar.
  2. Lisper actualizado para almejarse durante las dos primeras rondas para comenzar con el pie derecho con oponentes cooperativos
  3. Usó un algoritmo genético para encontrar los valores más robustos para el generador de umbral aleatorio basado en su puntaje acumulativo máximo contra un conjunto estándar de oponentes. Actualización publicada que los incluye.

VERSIÓN OFICIAL DE LISPER

DESARROLLAR LA VERSIÓN DE LISPER

arrdem
fuente
La puntuación varía en diferentes variantes del juego. Me hice jugar un poco con el aumento de los incentivos de cooperación, y estoy de acuerdo que tendrá un efecto sobre las estrategias elegidas. La buena noticia: puedes agarrar al anotador, establecer tus propias reglas y probarlo. En principio, incluso podría ofrecer una recompensa.
dmckee
fink install clisp :: tocando los dedos repetidamente ::
dmckee
1
@josh: gracias por el enlace. Leí algunas otras páginas de wikipedia sobre este dilema, pero me perdí esa sección. Un error de reglas que acabo de notar, no hay reglas contra las entradas que usan el sistema de archivos. Esto crea el potencial para una cooperación mucho más eficiente en la línea del apretón de manos.
Arrdem
3
There is no reason to EVER (under this scoring system) co-operatees solo medio correcto. Si sabes que tu oponente no tiene en cuenta el historial (ángel, demonio, aleatorio), entonces siempre debes desertar. Si tu oponente toma en cuenta el historial y puedes sincronizar, entonces puedes hacerlo mejor. Tengo un par de ideas que giran en torno a detectar si el oponente es racional o superracional.
Peter Taylor
1
¿No obtiene errores de división por cero 3/20 de las veces con la última versión? Siempre que (random 20)da 2, 5 u 8, (/ (+1 rand-num) 10)es 0.3, 0.6, 0.9, y el resto de la división con 0.3 es 0; Entonces (floor *dbag* *margin*)muere.
Josh Caswell
5

Desconfianza (variante)

Este salió por primera vez en mis propias pruebas hace años (en aquel entonces estaba en el 11 ° grado e hice una pequeña tesis sobre exactamente esto, usando estrategias diseñadas por otros estudiantes también). Comienza con la secuencia tcc(y juega como Tit para Tat después de eso.

Disculpas por el horrible código; si alguien puede hacer eso más corto sin jugar golf exactamente, estaría agradecido :-)

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
    if (argc == 1)
        printf("t\n");
    else switch (strlen(argv[1])) {
        case 0:
            printf("t\n");
            break;
        case 1:
        case 2:
            printf("c\n");
            break;
        default:
            if (argv[1][0] == 'R' || argv[1][0] == 'E')
                printf("t\n");
            else
                printf("c\n");
            break;
    }

    return 0;
}
Joey
fuente
No hay necesidad de código duplicado en longitud 1 y 2. Uso caída a través de: case 1: case2: printf(...); break;. Y gcc quiere una declaración explícita de string.huso strlen. En cualquier caso lo tengo funcionando.
dmckee
Ah, cierto eso. Sin embargo, no estaba seguro de cómo detectar la primera ronda, si hay un primer argumento vacío (historial) o simplemente ninguno.
Joey
No estoy seguro. Es lo que sea que haga Python con Popen(p+" "+h,stdout=subprocess.PIPE,shell=True)cuándo h = ''. Supongo argc=1.
dmckee
1
Esa secuencia inicial es una buena idea, apuntando directamente a Tit por la debilidad de Tat. Obtiene una pequeña ventaja y luego juega a su manera.
Josh Caswell el
1
@ Josh, ¿dónde está el pequeño plomo? Contra T4T, esto comienza con SRK y luego continúa con K. Pero SR vale 3 puntos para cada jugador.
Peter Taylor
5

Misil anti-T42T

#!/usr/bin/python

"""
Anti-T42T Missile, by Josh Caswell

That Tit-for-two-tats, what a push-over!
  T42T: ccctcctcc...
AT42TM: cttcttctt...
        KSSRSSRSS...
"""
import sys
try:
    history = sys.argv[1]
except IndexError:
    print 'c'
    sys.exit(0)

if history[:2] == 'SS':
    print 'c'
else:
    print 't'

Lo hace razonablemente bien contra el conjunto base de guerreros: mata a Angel, ligeramente derrotado por Devil (pero mantiene su puntaje bajo), generalmente vence a RAND fácilmente y apenas le gana a Tit por Tat. Hace mal cuando juega contra sí mismo.

Josh Caswell
fuente
Envié una edición que hace que este realmente funcione :) Necesita ser aprobado.
Casey
@Casey: buen señor, ¡estoy cometiendo tantos errores estúpidos en mi entusiasmo por este problema! Gracias, pero ¿por qué eliminaste el sh-bang?
Josh Caswell el
Er, eso fue un accidente. Lo agregaré de nuevo.
Casey
@Casey: no hay problema. Lo haré. Necesito agregar una cadena de documentos de todos modos.
Josh Caswell el
4

Convergencia

Inicialmente agradable, luego juega al azar con un ojo en la historia del oponente.

/* convergence
 *
 * A iterated prisoners dilemma warrior for
 *
 * Strategy is to randomly chose an action based on the opponent's
 * history, weighting recent rounds most heavily. Important fixed
 * point, we should never be the first to betray.
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char**argv){
  srandom(time(0)+getpid()); /* seed the PRNG */
  unsigned long m=(1LL<<31)-1,q,p=m;
  if (argc>1) {
    size_t i,l=strlen(argv[1]);
    for (i=l; --i<l; ){
      switch (argv[1][i]) {
      case 'R':
      case 'E':
    q = 0;
    break;
      case 'K':
      case 'S':
    q = m/3;
    break;
      }
      p/=3;
      p=2*p+q;
    }
  }
  /* printf("Probability of '%s' is %g.\n",argv[1],(double)p/(double)m); */
  printf("%c\n",(random()>p)?'t':'c'); 
  return 0;
}

He intentado incluir la ponderación en la historia, pero no la he optimizado correctamente.

dmckee
fuente
4

Tiburón

#!/usr/bin/env python

"""
Shark, by Josh Caswell

Carpe stultores.
"""

import sys

HUNGER = 12

try:
    history = sys.argv[1]
except IndexError:
    print 'c'
    sys.exit(0)

if history.count('S') > HUNGER:
    print 't'
else:
    print 'c' if history[0] in "SK" else 't'

Lo hace bastante bien contra la lista base.

Josh Caswell
fuente
... agarrar la almeja?
llega
:) Agarra a los tontos.
Josh Caswell
+1 por mantener un 2do lugar consistente en el campo actual.
llega
3

Pavlov - Gana la estancia, pierde el interruptor

En el primer turno coopera, y luego coopera si y solo si ambos jugadores optaron por la misma opción en el movimiento anterior.

#!/usr/bin/python
import sys

if len(sys.argv) == 1:
    print 'c'
else:
    hist = sys.argv[1]
    if hist[0] == 'K' or hist[0] == 'E':
        print 'c'
    else:
        print 't'
Casey
fuente
¿No debería este uso hist[0]( hist[-1]es siempre el primer movimiento de la ronda)?
Josh Caswell el
Oh wow, tienes razón. Supuse que la cadena de entrada tenía las rondas más recientes al final de la cadena, no al principio. Fijo.
Casey
3

Honor entre ladrones

#!/usr/bin/env python

"""
Honor Among Thieves, by Josh Caswell

I'd never sell out a fellow thief, but I'll fleece a plump mark,
and I'll cut your throat if you try to cross me.
"""

from __future__ import division
import sys

PLUMPNESS_FACTOR = .33
WARINESS = 10

THIEVES_CANT = "E" + ("K" * WARINESS)

try:
    history = sys.argv[1]
except IndexError:
    history = ""

if history:
    sucker_ratio = (history.count('K') + history.count('S')) / len(history)
    seem_to_have_a_sucker = sucker_ratio > PLUMPNESS_FACTOR


# "Hey, nice t' meetcha."
if len(history) < WARINESS:
    #"Nice day, right?"
    if not set(history).intersection("RE"):
        print 'c'
    # "You sunnuvab..."
    else:
        print 't'

# "Hey, lemme show ya this game. Watch the queen..."
elif len(history) == WARINESS and seem_to_have_a_sucker:
    print 't'

# "Oh, s#!t, McWongski, I swear I din't know dat were you."
elif history[-len(THIEVES_CANT):] == THIEVES_CANT:

    # "Nobody does dat t' me!"
    if set(history[:-len(THIEVES_CANT)]).intersection("RE"):
        print 't'
    # "Hey, McWongski, I got dis job we could do..."
    else:
        print 'c'

# "Do you know who I am?!"
elif set(history).intersection("RE"):
    print 't'

# "Ah, ya almos' had da queen dat time. One more try, free, hey? G'head!"
elif seem_to_have_a_sucker:
    print 't'

# "Boy, you don't say much, do ya?"
else:
    print 'c'

Tenga en cuenta que THIEVES_CANTes esencialmente un apretón de manos, aunque solo surgirá cuando juegue contra un cooperador. Sin embargo, evita el problema del parásito al buscar cruces posteriores. Lo hace bastante bien contra la lista base.

Josh Caswell
fuente
+1 por ser la primera estrategia en derrotar confiablemente a lisper. Margen promedio de victoria - 300 pts.
llega
Parece ser el más fuerte en una carrera de torneos del campo actual.
Peter Taylor
En realidad, no, Druida es ahora que he arreglado el error en el anotador.
Peter Taylor
@rmckenzie, @Peter: Geez, ¿en serio? Solo iba por la personalidad.
Josh Caswell
@josh - no más ... en el nuevo código de puntuación @ casey código de puntuación Lisper está de vuelta en la parte superior seguido de tiburón.
llega
3

"Probabimatic"

Comienza cooperando, luego elige la opción que le dé el valor más alto esperado. Simple.

#include <stdio.h>

void counts(char* str, int* k, int* r, int* s, int* e) {
    *k = *r = *s = *e = 0;
    char c;
    for (c = *str; c = *str; str++) {
        switch (c) {
            case 'K': (*k)++; break;
            case 'R': (*r)++; break;
            case 'S': (*s)++; break;
            case 'E': (*e)++; break;
        }
    }
}

// Calculates the expected value of cooperating and defecting in this round. If we haven't cooperated/defected yet, a 50% chance of the opponent defecting is assumed.
void expval(int k, int r, int s, int e, float* coop, float* def) {
    if (!k && !r) {
        *coop = .5;
    } else {
        *coop = 2 * (float)k / (k + r) - (float)r / (k + r);
    }
    if (!s && !e) {
        *def = 2.5;
    } else {
        *def = 4 * (float)s / (s + e) + (float)e / (s + e);
    }
}

int main(int argc, char** argv) {
    if (argc == 1) {
        // Always start out nice.
        putchar('c');
    } else {
        int k, r, s, e;
        counts(argv[1], &k, &r, &s, &e);
        float coop, def;
        expval(k, r, s, e, &coop, &def);
        if (coop > def) {
            putchar('c');
        } else {
            // If the expected values are the same, we can do whatever we want.
            putchar('t');
        }
    }
    return 0;
}

Solía ​​comenzar por cooperar, pero ahora parece que los defectos funcionan mejor. EDITAR: Oh, espera, en realidad no lo hace.

Lowjacker
fuente
1
¡Otro estadístico! ¡Veamos cómo esto juega contra sus compañeros calculadoras !
Josh Caswell el
Por cierto, si cambia for (char c = *str;a char c; for (c = *str;gcc, compilará esto sin quejarse de que debe ponerse en modo C99.
Peter Taylor
3

Avispa hiperracional

Implementado en Java porque no estaba seguro de cuán complejas iban a terminar las estructuras de datos. Si esto es un problema para alguien, entonces creo que puedo portarlo a bash sin demasiados problemas porque al final solo usa matrices asociativas simples.

Nota : He eliminado esto de un paquete en línea con la última versión de mi parche al anotador para manejar Java. Si desea publicar una solución Java que use clases internas, entonces deberá parchear el parche.

import java.util.*;

public class HyperrationalWasp
{
    // I'm avoiding enums so as not to clutter up the warriors directory with extra class files.
    private static String Clam = "c";
    private static String Rat = "t";
    private static String Ambiguous = "x";

    private static final String PROLOGUE = "ttc";

    private static int n;
    private static String myActions;
    private static String hisActions;

    private static String decideMove() {
        if (n < PROLOGUE.length()) return PROLOGUE.substring(n, n+1);

        // KISS - rather an easy special case here than a complex one later
        if (mirrorMatch()) return Clam;
        if (n == 99) return Rat; // This is rational rather than superrational

        int memory = estimateMemory();
        if (memory == 0) return Rat; // I don't think the opponent will punish me
        if (memory > 0) {
            Map<String, String> memoryModel = buildMemoryModel(memory);
            String myRecentHistory = myActions.substring(0, memory - 1);
            // I don't think the opponent will punish me.
            if (Clam.equals(memoryModel.get(Rat + myRecentHistory))) return Rat;
            // I think the opponent will defect whatever I do.
            if (Rat.equals(memoryModel.get(Clam + myRecentHistory))) return Rat;
            // Opponent will cooperate unless I defect.
            return Clam;
        }

        // Haven't figured out opponent's strategy. Tit for tat is a reasonable fallback.
        return hisAction(0);
    }

    private static int estimateMemory() {
        if (hisActions.substring(0, n-1).equals(hisActions.substring(1, n))) return 0;

        int memory = -1; // Superrational?
        for (int probe = 1; probe < 5; probe++) {
            Map<String, String> memoryModel = buildMemoryModel(probe);
            if (memoryModel.size() <= 1 || memoryModel.values().contains(Ambiguous)) {
                break;
            }
            memory = probe;
        }

        if (memory == -1 && isOpponentRandom()) return 0;

        return memory;
    }

    private static boolean isOpponentRandom() {
        // We only call this if the opponent appears not have have a small fixed memory,
        // so there's no point trying anything complicated. This is supposed to be a Wilson
        // confidence test, although my stats is so rusty there's a 50/50 chance that I've
        // got the two probabilities (null hypothesis of 0.5 and observed) the wrong way round.
        if (n < 10) return false; // Not enough data.
        double p = count(hisActions, Clam) / (double)n;
        double z = 2;
        double d = 1 + z*z/n;
        double e = p + z*z/(2*n);
        double var = z * Math.sqrt(p*(1-p)/n + z*z/(4*n*n));
        return (e - var) <= 0.5 * d && 0.5 * d <= (e + var);
    }

    private static Map<String, String> buildMemoryModel(int memory) {
        // It's reasonable to have a hard-coded prologue to probe opponent's behaviour,
        // and that shouldn't be taken into account.
        int skip = 0;
        if (n > 10) skip = n / 2;
        if (skip > 12) skip = 12;

        Map<String, String> memoryModel = buildMemoryModel(memory, skip);
        // If we're not getting any useful information after skipping prologue, take it into account.
        if (memoryModel.size() <= 1 && !memoryModel.values().contains(Ambiguous)) {
            memoryModel = buildMemoryModel(memory, 0);
        }
        return memoryModel;
    }

    private static Map<String, String> buildMemoryModel(int memory, int skip) {
        Map<String, String> model = new HashMap<String, String>();
        for (int off = 0; off < n - memory - 1 - skip; off++) {
            String result = hisAction(off);
            String hypotheticalCause = myActions.substring(off+1, off+1+memory);
            String prev = model.put(hypotheticalCause, result);
            if (prev != null && !prev.equals(result)) model.put(hypotheticalCause, Ambiguous);
        }
        return model;
    }

    private static boolean mirrorMatch() { return hisActions.matches("c*ctt"); }
    private static String myAction(int idx) { return myActions.substring(idx, idx+1).intern(); }
    private static String hisAction(int idx) { return hisActions.substring(idx, idx+1).intern(); }
    private static int count(String actions, String action) {
        int count = 0;
        for (int idx = 0; idx < actions.length(); ) {
            int off = actions.indexOf(action, idx);
            if (off < 0) break;
            count++;
            idx = off + 1;
        }
        return count;
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            hisActions = myActions = "";
            n = 0;
        }
        else {
            n = args[0].length();
            myActions = args[0].replaceAll("[KR]", Clam).replaceAll("[SE]", Rat);
            hisActions = args[0].replaceAll("[KS]", Clam).replaceAll("[RE]", Rat);
        }

        System.out.println(decideMove());
    }

}

Los cambios que hice al anotador para ejecutar esto son:

17a18
> import re
22a24
> GCC_PATH = 'gcc'                #path to c compiler
24c26
< JAVA_PATH = '/usr/bin/java'   #path to java vm
---
> JAVA_PATH = '/usr/bin/java'     #path to java vm
50,55c52,59
<         elif ext == '.java':
<             if subprocess.call([JAVAC_PATH, self.filename]) == 0:
<                 print 'compiled java: ' + self.filename
<                 classname = re.sub('\.java$', '', self.filename)
<                 classname = re.sub('/', '.', classname);
<                 return JAVA_PATH + " " + classname
---
>         elif ext == '.class':
>             # We assume further down in compilation and here that Java classes are in the default package
>             classname = re.sub('.*[/\\\\]', '', self.filename)
>             dir = self.filename[0:(len(self.filename)-len(classname))]
>             if (len(dir) > 0):
>                 dir = "-cp " + dir + " "
>             classname = re.sub('\\.class$', '', classname);
>             return JAVA_PATH + " " + dir + classname
196c200,201
<         if os.path.isdir(sys.argv[1]):
---
>         warriors_dir = re.sub('/$', '', sys.argv[1])
>         if os.path.isdir(warriors_dir):
198,200c203,211
<             for foo in os.listdir("./src/"): # build all c/c++ champs first.
<                 os.system(str("gcc -o ./warriors/" + os.path.splitext(os.path.split(foo)[1])[0] + " ./src/" + foo ))
<                 #print str("gcc -o ./warriors/" + os.path.splitext(os.path.split(foo)[1])[0] + " ./src/" + foo )
---
>             for foo in os.listdir("./src/"): # build all c/c++/java champs first.
>                 filename = os.path.split(foo)[-1]
>                 base, ext = os.path.splitext(filename)
>                 if (ext == '.c') or (ext == '.cpp'):
>                     subprocess.call(["gcc", "-o", warriors_dir + "/" + base, "./src/" + foo])
>                 elif (ext == '.java'):
>                     subprocess.call([JAVAC_PATH, "-d", warriors_dir, "./src/" + foo])
>                 else:
>                     print "No compiler registered for ", foo
202,203c213,214
<             print "Finding warriors in " + sys.argv[1]
<             players = [sys.argv[1]+exe for exe in os.listdir(sys.argv[1]) if os.access(sys.argv[1]+exe,os.X_OK)]
---
>             print "Finding warriors in " + warriors_dir
>             players = [warriors_dir+"/"+exe for exe in os.listdir(warriors_dir) if (os.access(warriors_dir+"/"+exe,os.X_OK) or os.path.splitext(exe)[-1] == '.class')]

Gracias a @rmckenzie por desplegar mi función de desafío.

Peter Taylor
fuente
Solo es una cuestión de estilo ... si el archivo .java se considera "fuente" y se mueve al directorio ./src y la clase final se coloca en la carpeta ./warriors por el mismo subíndice utilizado en los archivos .c, o ¿Se interpreta Java y, como tal, .java y .class permanecen juntos? En cualquier caso, los cambios agradables en el anotador ... los tendrán en la estadística de repositorio.
arrdem
@rmckenzie, buen punto: sí, técnicamente está compilado. La razón por la que tenía el archivo fuente en el directorio de warriors es que los archivos python también están allí, y están compilados. Si lo desea, puedo verificar qué cambios son necesarios para compilarlo de ./src a ./warriors, pero requerirá algunos argumentos del compilador, porque Java asume de forma predeterminada que la estructura del directorio refleja el paquete (espacio de nombres).
Peter Taylor
@peter, me preguntaba ... los guerreros se encuentran en ./warriors en virtud de ser * nix 777, o de otra manera ejecutables. Las secuencias de comandos Python y Lisp se compilan NOMINALMENTE para el rendimiento, pero son ejecutables en su estado natural (fuente). A MI CONOCIMIENTO COMO PERSONA NO JAVA, los archivos .java no tienen esos permisos y, por lo tanto, no se mostrarán. Para eso existe el hack c ... porque la compilación es un paso separado. Así que sí. Le agradecería mucho que considerara hacer ese cambio. REPO LINK
llega
Usando su código y una avispa chmod 777'd, la JVM lanzó esta belleza. Exception in thread "main" java.lang.NoClassDefFoundError: //warriors/HyperrationalWasp Caused by: java.lang.ClassNotFoundException: ..warriors.HyperrationalWasp at java.net.URLClassLoader$1.run(URLClassLoader.java:217) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:205) at java.lang.ClassLoader.loadClass(ClassLoader.java:321) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294) at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
llega
@rmckenzie, eso es extraño. De todos modos, creo que pronto tendré un parche para ti. Tuve que hackear el código de carga, porque los archivos de clase no son ejecutables. Y si alguna otra entrada de Java usa clases internas, se romperá.
Peter Taylor
3

Soft_majo

Ah, bueno, otra de las estrategias estándar, solo para completar la alineación.

Éste elige el movimiento que más ha hecho el oponente; si es igual coopera.

#include <stdio.h>
#include <string.h>

int main(int argc, char * argv[]) {
    int d = 0, i, l;

    if (argc == 1) {
        printf("c\n");
    } else {
        l = strlen(argv[1]);

        for (i = 0; i < l; i++)
            if (argv[1][i] == 'R' || argv[1][i] == 'E')
                d++;

        printf("%c\n", d > l/2 ? 't' : 'c');
    }
}
Joey
fuente
Su código es soft_majo, pero su descripción es hard_majo.
Peter Taylor
Peter: Eek, lo siento; fijo.
Joey
3

Lechón aleatorio

Éste fallará si el oponente lo hace con demasiada frecuencia (umbral), pero de vez en cuando intentará apuñalar por la espalda.

Funciona bastante bien contra todos excepto los jugadores de Java y Lisp (que no puedo ejecutar, ya que ni Java ni Lisp están en la máquina de prueba); la mayoría de las veces al menos.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define THRESHOLD 7
#define RAND 32

int main(int c, char * a []) {
    int r;
    char * x;
    int d = 0;

    srandom(time(0) + getpid());

    if (c == 1) {
        printf("c\n");
        return 0;
    }

    for (x = a[1]; *x; x++)
        if (*x == 'R' || *x == 'E') d++;

    if (d > THRESHOLD || random() % 1024 < RAND || strlen(a[1]) == 99)
        printf("t\n");
    else
        printf("c\n");

    return 0;
}
Joey
fuente
Contra HyperrationalWasp generalmente será más o menos como contra el diablo. Comienza cooperando todo el tiempo, así que supongo que es el ángel y voy al ataque. Luego, cuando llegue al umbral, cambiarás al modo diablo y yo cambiaré a t4t. Si al azar apuñala por la espalda en los primeros 6 movimientos, cambiaré a t4t antes de que cambies a diablo, pero las probabilidades de eso no son altas.
Peter Taylor
1
Peter: Bueno, rara vez pruebo las estrategias directamente entre sí, ya que el campo general tiene bastante influencia en el rendimiento de la estrategia. Actualmente lucha principalmente con druida y gradual por el primer lugar en mis pruebas.
Joey
Gradual y druida ambos puntúan alrededor de 200 contra Wasp; lechón al azar anotará alrededor de 83.
Peter Taylor
2

Pasado

#!/usr/bin/env python

"""
BYGONES, entry to 1P5 Iterated Prisoner's Dilemma, by Josh Caswell

Cooperates at first, plays as Tit for Tat for `bygones * 2` rounds, then checks 
history: if there's too much ratting, get mad and defect; too much 
suckering, feel bad and cooperate.
"""

bygones = 5

import sys

# React to strangers with trust.
try:
    history = sys.argv[1]
except IndexError:
    print 'c'
    sys.exit(0)

replies = { 'K' : 'c', 'S' : 'c',
            'R' : 't', 'E' : 't' }

# Reply in kind.
if len(history) < bygones * 2:
    print replies[history[0]]
    sys.exit(0)

# Reflect on past interactions.
faithful_count = history.count('K')
sucker_count = history.count('S')
rat_count = history.count('R')

# Reprisal. 
if rat_count > faithful_count + bygones:
    # Screw you!
    print 't'
    sys.exit(0)

# Reparation.
if sucker_count > faithful_count + bygones:
    # Geez, I've really been mean.
    print 'c'
    sys.exit(0)

# Resolve to be more forgiving.
two_tats = ("RR", "RE", "ER", "EE")
print 't' if history[:2] in two_tats else 'c'

No he encontrado el mejor valor por el bygonesmomento. No espero que esto sea un estrategia ganadora , pero estoy interesado en el desempeño de una estrategia algo así como lo que creo que es "bueno" en la vida real. Una revisión futura también puede incluir verificar el número de deserciones mutuas.

Josh Caswell
fuente
2

Vampiro de ayuda

#!/usr/bin/env python

"""
Help Vampire, entry to 1P5 Iterated Prisoner's Dilemma,
by Josh Caswell.

1. Appear Cooperative 2. Acknowledge Chastisement 
3. Act contritely 4. Abuse charity 5. Continual affliction
"""

import sys
from os import urandom

LEN_ABASHMENT = 5

try:
    history = sys.argv[1]
except IndexError:
    print 'c'    # Appear cooperative
    sys.exit(0)

# Acknowledge chastisement
if history[0] in "RE":
    print 'c'
# Act contritely
elif set(history[:LEN_ABASHMENT]).intersection(set("RE")):
    print 'c'
# Abuse charity
elif history[0] == 'S':
    print 't'
# Continual affliction
else:
    print 't' if ord(urandom(1)) % 3 else 'c'

Tiene un resultado divertido y asimétrico cuando se enfrenta a sí mismo. Si tan solo esta solución pudiera aplicarse en la vida real.

Josh Caswell
fuente
2

druida

#!/usr/bin/env python

"""
Druid, by Josh Caswell

Druids are slow to anger, but do not forget.
"""

import sys
from itertools import groupby

FORBEARANCE = 7
TOLERANCE = FORBEARANCE + 5

try:
    history = sys.argv[1]
except IndexError:
    history = ""

# If there's been too much defection overall, defect
if (history.count('E') > TOLERANCE) or (history.count('R') > TOLERANCE):
    print 't'
# Too much consecutively, defect
elif max([0] + [len(list(g)) for k,g in     # The 0 prevents dying on []
                groupby(history) if k in 'ER']) > FORBEARANCE:
    print 't'
# Otherwise, be nice
else:
    print 'c'

Funciona razonablemente bien contra el roster base.

Josh Caswell
fuente
2

Simplón

#!/usr/bin/env python

"""
Simpleton, by Josh Caswell

Quick to anger, quick to forget, unable to take advantage of opportunity.
"""

import sys
from os import urandom

WHIMSY = 17

try:
    history = sys.argv[1]
except IndexError:
    if not ord(urandom(1)) % WHIMSY:
        print 't'
    else:
        print 'c'
    sys.exit(0)

if history[0] in "RE":
    print 't'
elif not ord(urandom(1)) % WHIMSY:
    print 't'
else:
    print 'c'

Está bien contra la lista base.

Josh Caswell
fuente
2

Pequeño Schemer

#!/usr/bin/env python

"""
The Little Schemer, by Josh Caswell

No relation to the book. Keeps opponent's trust > suspicion 
by at least 10%, trying to ride the line.
"""

from __future__ import division
import sys
from os import urandom

out = sys.stderr.write

def randrange(n):
    if n == 0:
        return 0
    else:
        return ord(urandom(1)) % n

try:
    history = sys.argv[1]
except IndexError:
    print 'c'
    sys.exit(0)

R_count = history.count('R')
S_count = history.count('S')
K_count = history.count('K')
E_count = history.count('E')

# Suspicion is _S_ and E because it's _opponent's_ suspicion
suspicion = (S_count + E_count) / len(history)
# Likewise trust
trust = (K_count + R_count) / len(history)

if suspicion > trust:
    print 'c'
else:
    projected_suspicion = (1 + S_count + E_count) / (len(history) + 1)
    projected_trust = (1 + K_count + R_count) / (len(history) + 1)

    leeway = projected_trust - projected_suspicion
    odds = int(divmod(leeway, 0.1)[0])

    print 't' if randrange(odds) else 'c'

Lo hace mal contra el conjunto base, pero bastante bien contra su objetivo. Obviamente, no está escrito en Scheme.

Josh Caswell
fuente
¿Por qué siento un desafío?
llega
Derrotó a este cabrón ... aleatorizó el umbral en Lisper.
llega
@rmckenzie: ¿pero cómo afectó eso tu juego contra el resto del campo? Con suficientes cooperadores trabajando entre ellos, las estrategias paranoicas o envidiosas comenzarán a empeorar. Todavía tienes un límite superior fijo, que podría ser explotado.
Josh Caswell
Si lees el lisper actual, es más defensivo que envidioso. Intenta detectar a los oponentes que persiguen estadísticas estadísticamente traicioneras como esta, y solo entonces devuelve el fuego. Su apertura CC está diseñada para comenzar con el pie derecho con Thieves, y tiene el beneficio adicional de convencer a la mayoría de las estrategias cooperativas para que jueguen.
llega
@rmckenzie: ¡Muy bien! Le daré una vuelta.
Josh Caswell
1

Teta para dos tatuajes

otro viejo favorito

#!/usr/bin/env python

"""
Tit For Two Tats, entry to 1P5 Iterated Prisoner's Dilemma, 
    by Josh Caswell (not an original idea).

Cooperates unless opponent has defected in the last two rounds.
"""

import sys
try:
    history = sys.argv[1]
except IndexError:
    history = ""

two_tats = ("RR", "RE", "ER", "EE")

if len(history) < 2:
    print 'c'
else:
    print 't' if history[:2] in two_tats else 'c'
Josh Caswell
fuente
No puede hacer una devolución a menos que esté dentro de una función. Tal vez usar sys.exit(0)? O simplemente déjalo terminar. Editar: también la primera invocación a su programa es sin ningún historial, lo que provoca un IndexErrorcuando lo hace argv[1].
Casey
Es posible que haya omitido la len(history)<2cláusula, porque esa última se parece a la elseparte.
dmckee
@Casey @dmckee Gracias por las correcciones de errores. "Duh" en mí, returnespecialmente!
Josh Caswell el
@dmckee: Esto comenzó como parte de una cosa más complicada, y luego me di cuenta de que había reescrito Tit for Two Tats y decidí ingresarlo. Copiar-pegar error del usuario.
Josh Caswell el
@Josh: Vi tu entrada de Bygones brevemente, ¿la borraste? Parecía interesado.
Casey