En esta pregunta , se ideó un juego en el que los jugadores se enfrentarían par a par en el Dilema del prisionero, para determinar qué estrategia iterativa obtuvo el puntaje más alto contra los demás.
En esta pregunta , ideé una forma para que varias personas jueguen el Dilema de los Prisioneros entre sí al mismo tiempo. En esta variación, la matriz de pagos es innecesaria, y cada resultado entre cada par de dos jugadores es la suma de dos decisiones funcionalmente independientes.
Tu tarea es construir una IA para jugar esta versión simétrica y generalizada del Dilema del Prisionero multijugador que logrará la mayor puntuación posible.
Reglas del juego
En cada ronda de este multijugador, el dilema del prisionero de varias rondas, un jugador A
puede decidir "tomar 1" de otro jugador B
. En esta circunstancia, A
el puntaje aumenta en 1, mientras que B
el puntaje disminuye en 2. Se permite que esta decisión ocurra entre cada par ordenado de jugadores.
Esta es la única decisión tomada para cada jugador, ya sea "tomar 1" o no "tomar 1" del otro jugador, que son homólogos a la deserción y la cooperación, respectivamente. La matriz de pagos efectivos entre dos jugadores P1
y se P2
ve de la siguiente manera:
P1/P2 P1 Take1 P1 Don't
P2 Take1 -1/-1 -2/+1
P2 Don't +1/-2 0/ 0
Procedimiento de torneo
El juego consistirá en P * 25
rondas, donde P
está el número de jugadores participantes. Todos los jugadores comienzan con una puntuación de 0
. Cada ronda consistirá en el siguiente procedimiento:
Al comienzo de una ronda, a cada programa se le dará un historial de las rondas anteriores a partir de la entrada estándar , en el siguiente formato:
Una línea que contiene 3 números,
P
,D
, yN
.P
es el número total de jugadores en el juego. A cada jugador se le asigna aleatoriamente un número de identificación desde1
alP
principio del juego.D
es la ID del jugador actual.N
es la cantidad de rondas que se han jugado.
N
líneas, cada línea representa los resultados de una ronda. En la líneak
deN
, habrá un númeron_k
de pares ordenados(a, b)
, separados por espacios, que representan que el jugador con IDa
"tomó 1" del jugador con IDb
en esa ronda.Un número uniformemente aleatorio
R
desde0
hasta18446744073709551615
(2 64 - 1), para actuar como una semilla pseudoaleatoria. Estos números se leerán de un archivo pregenerado, que se publicará al final del torneo para que las personas puedan verificar los resultados por sí mismos.Una línea adicional que representa alguna forma de estado para leer en su programa, si su programa produjo tal salida en la ronda anterior. Al comienzo del juego, esta línea siempre estará vacía. Esta línea no será modificada ni por el código de puntuación ni por otros programas.
Luego, cada programa usará su estrategia para producir lo siguiente para la salida estándar :
Una lista de
K
números, que son los ID de los programas que "tomará 1" de esta ronda. Una salida vacía significa que no hará nada.Opcionalmente, una línea adicional que representa alguna forma de estado para pasar a rondas posteriores. Esta línea exacta se retroalimentará al programa en la próxima ronda.
A continuación se muestra un ejemplo de entrada para el comienzo del juego para un jugador de ID 3
en un juego de 4 jugadores:
4 3 0
4696634734863777023
A continuación se muestra una entrada de ejemplo para el mismo juego con algunas rondas ya jugadas:
4 3 2
(1, 2) (1, 3) (1, 4) (4, 2)
(1, 3) (2, 1) (2, 4) (3, 1) (4, 1)
4675881156406346380
Cada programa se alimentará exactamente con la misma entrada para una ronda, excepto por el número de identificación D
que es único para cada programa.
A continuación se muestra un ejemplo de salida en el que el jugador 3
toma 1 de todos los demás:
1 2 4
Al final de todas las rondas requeridas, el jugador con el puntaje final más alto será el ganador.
Cronograma
La codificación para este torneo durará un total de 7 días. La fecha límite para la presentación es 2014-05-09 00:00 UTC
.
No publique programas reales antes de esta fecha: publique el hash SHA256 del código fuente de su programa como un compromiso. Puede cambiar este hash en cualquier momento antes de la fecha límite, pero los compromisos publicados después de la fecha límite no serán aceptados para juicio. (Utilice la notación de base 64 para sus hashes, ya que mi programa de verificación escupe la base 64 y es una notación más compacta).
Una vez finalizado el plazo, tendrá 1 día (hasta 2014-05-10 00:00 UTC
) para publicar el código fuente real de su programa para su envío. Si el hash SHA256 de su código fuente publicado no coincide con ningún hash que haya publicado antes de la fecha límite, su código no será aceptado en el torneo.
Después de esto, descargaré todas las presentaciones en mi propia computadora y ejecutaré todas las entradas del torneo en esta batalla real, con la esperanza de publicar los resultados dentro de 2 días a partir de entonces 2014-05-12 00:00 UTC
.
Aceptaré la respuesta con el puntaje más alto y otorgaré una recompensa de +100 a esa respuesta si su puntaje final es mayor que 0
.
Después de que termine el torneo, publicaré el archivo semilla aleatorio utilizado para ejecutar la competencia, y la gente puede comenzar a publicar otras soluciones tratando de superar las utilizadas en el torneo. Sin embargo, no contarán para la aceptación o la recompensa.
La máquina host
Ejecutaré estas soluciones en una máquina virtual en mi computadora. Esta máquina virtual ejecutará Ubuntu Linux 14.04, con 2 gigabytes de RAM. Mi máquina base tiene un procesador Intel i7-2600K que funciona a 3.40 GHz.
Requisitos
Su programa debe estar escrito en un lenguaje para el cual exista un compilador o intérprete que compile su programa y esté disponible para la última versión de Ubuntu Linux, de modo que pueda ejecutar todos los envíos y juzgarlos en una máquina virtual.
Su programa no debe tomar más que 2.000 seconds
ejecutar cada ronda. Si su programa se queda sin tiempo o produce un error, su salida se considerará vacía para esa ronda.
Su programa debe ser determinista; es decir, siempre debe devolver la misma salida para la misma entrada. Se permiten soluciones pseudoaleatorias; sin embargo, su aleatoriedad debe depender de la semilla aleatoria dada como entrada y nada más. El archivo semilla se generó usando Python os.urandom
. Contiene un total de 500 líneas (se generarán más si es necesario), y su hash SHA256 es K+ics+sFq82lgiLanEnL/PABQKnn7rDAGmO48oiYxZk=
. Se cargará aquí una vez que termine el torneo.
Plantas
Para comenzar, habrá cuatro "plantas", que representan estrategias ingenuas iniciales. Estos se jugarán en el torneo junto con tus presentaciones. Sin embargo, en el caso improbable de que uno de ellos gane, el puntaje más alto obtenido por un jugador que no sea una planta se considerará ganador.
Para calcular el hash del archivo de cada planta, reemplace cada grupo de 4 espacios con una pestaña, ya que al formateador aquí no parece gustarle los caracteres de tabulación.
The Lazy - nunca hace nada.
n1bnYdeb/bNDBKASWGywTRa0Ne9hMAkal3AuVZJgovI=
pass
The Greedy : siempre toma 1 de todos los demás.
+k0L8NF27b8+Xf50quRaZFFuflZhZuTCQOR5t5b0nMI=
import sys
line1 = sys.stdin.readline()
n = [int(i) for i in line1.split()]
for i in range(n[0]):
if i+1 != n[1]:
print i+1,
print
The Wrathful : toma 1 de todos los demás en la primera ronda y toma 1 de todos los que tomaron 1 de ella en la ronda anterior posterior.
Ya2dIv8TCh0zWzRfzUIdFKWj1DF9GXWhbq/uN7+CzrY=
import sys
import re
line1 = [int(i) for i in sys.stdin.readline().split()]
players = line1[0]
pid = line1[1]
rounds = line1[2]
lines = []
if rounds == 0:
for i in range(players):
if i+1 != pid:
print i+1,
print
else:
for i in range(rounds):
lines.append(sys.stdin.readline())
lastline = lines[-1]
takes = re.findall(r'\([0-9]+, [0-9]+\)', lastline)
for take in takes:
sides = [int(i) for i in re.findall(r'[0-9]+', take)]
if sides[1] == pid:
print sides[0],
print
The Envious : toma 1 del 50% de los jugadores con el puntaje más alto actual excluyéndose, redondeando hacia abajo.
YhLgqrz1Cm2pEcFlsiIL4b4MX9QiTxuIOBJF+wvukNk=
import sys
import re
line1 = [int(i) for i in sys.stdin.readline().split()]
players = line1[0]
pid = line1[1]
rounds = line1[2]
lines = []
scores = [0] * players
if rounds == 0:
for i in range(players):
if i+1 != pid:
print i+1,
print
else:
for i in range(rounds):
takes = re.findall(r'\([0-9]+, [0-9]+\)', sys.stdin.readline())
for take in takes:
sides = [int(i) for i in re.findall(r'[0-9]+', take)]
scores[sides[0] - 1] += 1
scores[sides[1] - 1] -= 2
score_pairs = [(i+1, scores[i]) for i in range(players)]
score_pairs.sort(key=lambda x:(x[1], x[0]))
score_pairs.reverse()
taken = 0
j = 0
while taken < (players) / 2:
if score_pairs[j][0] != pid:
print score_pairs[j][0],
taken += 1
j += 1
En un torneo de 100 rondas solo entre estas cuatro, reciben puntuaciones de:
Lazy: -204
Greedy: -100
Wrathful: -199
Envious: -199
Programa de juzgamiento
He publicado el programa de jueces que usaré en Github . Descárgalo y pruébalo. (Y tal vez corrija un error o dos si encuentra uno.: P)
No tiene opciones de compilación para nada más que Python en este momento. Los incluiré más adelante: si la gente pudiera contribuir con scripts de compilación o interpretación para otros idiomas, estaría muy agradecido.
Fase 2: envío del código fuente
He publicado una nueva sucursal tournament
en el repositorio de Github para el concurso, que contiene el archivo pd_rand y otras entradas de la planta. Puede publicar su código fuente aquí o enviarlo a esa sucursal como una solicitud de extracción.
El orden de los concursantes será el siguiente:
'begrudger'
'regular'
'patient'
'lazy'
'backstab'
'bully'
'lunatic'
'envious'
'titfortat'
'greedy'
'wrathful'
'judge'
'onepercent'
Puntajes finales
La salida de mi programa de prueba:
Final scores:
begrudger -2862
regular -204
patient -994
lazy -2886
backstab -1311
bully -1393
lunatic -1539
envious -2448
titfortat -985
greedy -724
wrathful -1478
judge -365
onepercent -1921
Rankings:
1. regular -204
2. judge -365
3. greedy -724
4. titfortat -985
5. patient -994
6. backstab -1311
7. bully -1393
8. wrathful -1478
9. lunatic -1539
10. onepercent -1921
11. envious -2448
12. begrudger -2862
13. lazy -2886
Así que resulta que el ganador es un jugador: ¡es The Regular, con -204 puntos!
Desafortunadamente, su puntaje no fue positivo, pero difícilmente podemos esperar que en una simulación del Dilema del Prisionero Iterado donde todos estén jugando para ganar.
Algunos resultados sorprendentes (al menos eso pensé que eran sorprendentes):
El codicioso anotó más que Tit para Tat, y de hecho, generalmente más alto que la mayoría de los anotadores.
El Juez, que estaba destinado a ser una especie de personaje "ejecutor de la moralidad" (básicamente tomó 1 de quien había tomado 1 de alguien un número superior al promedio de veces) terminó obteniendo un puntaje bastante alto, mientras que en las pruebas de simulación, en realidad obtener un puntaje bastante bajo.
Y otros que (pensé) no fueron tan sorprendentes:
El paciente obtuvo 484 puntos más que The Wrathful. Realmente vale la pena cooperar esa primera vez.
Uno por ciento muy rápidamente no tenía a nadie a quien patear mientras estaban abajo. Parece que el 1% solo puede mantenerse así porque tienen más jugadores en el juego.
De todos modos, ahora que el torneo ha terminado, siéntase libre de publicar tantos jugadores adicionales como desee y pruebe con ellos utilizando el programa de jueces.
fuente
Respuestas:
El regular
La versión de esta entrada que elegí para el torneo (SHA-256 :)
ggeo+G2psAnLAevepmUlGIX6uqD0MbD1aQxkcys64oc=
usa la estrategia de " Random sucker " de Joey (aunque con un cambio menor y probablemente insignificante), que quedó en segundo lugar en el último concurso. Desafortunadamente, una versión más nueva y más efectiva enviada solo 3 minutos y 25 segundos antes de la fecha límite tiene un error grave, por lo que no se pudo usar. Sin embargo, a esta versión todavía le va relativamente bien.La versión con errores tiene un hash SHA-256 de
2hNVloFt9W7/uA5aQXg+naG9o6WNmrZzRf9VsQNTMwo=
:Para solucionarlo, realice estos reemplazos:
$hashOutput = rtrim(fgets(STDIN), "\n");
con$line .= fgets(STDIN);
(no es que eso realmente importe).if ($betterScore >= 3 * $myScore) {
conif ($betterScore >= 0.5 * $myScore && !psRand(0, $betterPlayer)) {
(esto es lo que lo mató).fuente
Uno porciento
Desprecia a los prisioneros que considera debajo de él.
Simplemente toma de todos los que tienen puntos menores o iguales a sí mismo. La suposición es que esos presos tienen menos probabilidades de recibir a cambio (o tendrían más). No sé qué tan buena es una suposición, pero eso es en lo que está operando.
También toma de todos en la última ronda. Literalmente, no hay inconveniente en esto, ya que nadie puede vengarse después de eso.
Si tiene problemas para obtener el hash debido a tab / espacios del código pegado, aquí hay un enlace al archivo en sí.
fuente
05-09 00:00
fecha límite.Aquí hay algunas plantas más que participarán en el juego. Estos son más avanzados y su código fuente no se revelará hasta el final de la fase de codificación.
Al igual que las cuatro plantas en la pregunta, si logran obtener un puntaje más alto que todos los demás jugadores, solo el puntaje más alto alcanzado por un concursante real se considerará ganador.
El acosador
29AGVpvJmDEDI5Efe/afmMJRLaJ+TpjwVcz1GkxgYZs=
Selecciones en las personas.
El juez
yjdCQ3uQ4YKe7xAKxdTFLF4d72fD4ACYpDLwkbzdISI=
Castiga a los malhechores.
El lunático
m3FsRPocekCcK6GDswgnobV2CYOxX8LquChnKxrx1Wo=
No tiene idea de lo que está haciendo.
El paciente
nd7Pt3bVpFnuvDVeHQ5T9EPTq7KjNraVzp/KGtI73Vo=
Nunca hace el primer movimiento.
fuente
Tit por tat
Similar a Wrathful, con algunos (con suerte) cambios que mejoran el rendimiento.
fuente
Puñalada
Python 3
A pesar del nombre, este bot es bastante amable. Pero no lo marques.
EDIT 2 : Fuente publicada. Hurra.
EDITAR : Después de algunas pruebas, solucioné algunos defectos que encontré. No son algorítmicos, solo algunos problemas al leer la entrada.
fuente
los Begrudger
Código
Admito que no pasé mucho tiempo en esto ...
fuente
o += a.split(', ')[0]
no deja espacio entre los números.