Essa tarefa faz parte do Primeiro envio periódico de quebra-cabeça de programação Premier e tem como objetivo demonstrar a nova proposta de desafio do tipo rei da colina .
A tarefa é escrever um programa para reproduzir melhor o dilema do prisioneiro iterado do que outros participantes.
Olha Vinny. Conhecemos seu colega de cela --- como é o nome dele? Sim, McWongski, o mafioso nippo-irlandês-ucraniano - está tramando algo e você sabe o que é.
Estamos tentando ser legais aqui, Vinnie. Dando uma chance a você.
Se você nos contar o que ele está planejando, veremos uma boa tarefa de trabalho.
E se você não ...
As regras do jogo
- O concurso consiste em um round-robin completo (todos os pares possíveis) de dois competidores por vez (incluindo jogadas automáticas).
- Há 100 rodadas disputadas entre cada par
- Em cada rodada, pede-se a cada jogador que escolha entre cooperar ou traí-lo, sem conhecer as intenções dos outros jogadores, mas com uma memória dos resultados das rodadas anteriores disputadas contra esse oponente.
- Os pontos são concedidos para cada rodada com base na escolha combinada. Se ambos os jogadores cooperam, cada um ganha 2 pontos. Traição mútua rende 1 ponto cada. No caso misto, o jogador traidor recebe 4 pontos e o cooperador é penalizado por 1.
- Uma partida "oficial" será realizada no máximo 10 dias após a postagem com todos os envios que eu possa começar a trabalhar e ser usado para selecionar o vencedor "aceito". Como tenho uma caixa do Mac OS 10.5, as soluções POSIX devem funcionar, mas existem linuxismos que não. Da mesma forma, não tenho suporte para a API win32. Estou disposto a fazer um esforço básico para instalar as coisas, mas há um limite. Os limites do meu sistema não representam de forma alguma os limites das respostas aceitáveis, simplesmente aquelas que serão incluídas na partida "oficial".
A interface do programador
- As entradas devem estar na forma de programas que podem ser executados na linha de comando; a decisão deve a saída (exclusiva!) do programa na saída padrão. O histórico das rodadas anteriores com esse oponente será apresentado como um argumento na linha de comando.
- A saída é "c" (para fechar ) ou "t" (para dizer tudo ).
- O histórico é uma única sequência de caracteres que representa as rodadas anteriores, com as rodadas mais recentes ocorrendo mais cedo na sequência. Os personagens são
- "K" (para manter a fé que significa cooperação mútua)
- "R" (para ratos b @ st @ rd me esgotou! )
- "S" (para otário! Significa que você se beneficiou de uma traição)
- "E" (para todo mundo está procurando o número um na traição mútua)
O suporte
Quatro jogadores serão fornecidos pelo autor
- Anjo - sempre coopera
- Diabo - sempre fala
- TitForTat - Coopera na primeira rodada e sempre faz como ele fez na última rodada
- Aleatório - 50/50
ao qual adicionarei todas as entradas que puder executar.
A pontuação total será a soma da soma de todos os oponentes (incluindo jogadas automáticas apenas uma vez e usando a pontuação média).
Participantes
(atual em 2 de maio de 2011 às 7:00)
O aperto de mão secreto | Míssil Anti-T42T | Desconfiança (variante) | Anti-aperto de mão | O pouco lisper | convergência | tubarão | Probabimatic | Pavlov - Ganhe estadia, perca o interruptor | Honra entre ladrões | Ajude o vampiro | Druid | Pequeno planejador | bygones | Peitos para duas tatuagens | simplório |
Marcador
#! /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
- Reclamações sobre o meu horrível python são bem-vindas, pois tenho certeza de que isso é uma droga mais do que uma maneira
- Bug fixes welcome
Registro de Mudanças do Artilheiro:
- Imprima jogadores e pontuações classificadas e declare um vencedor (29/4, Casey)
- Opcionalmente, execute vários torneios (
./score warriors/ num_tournaments)
) padrão = 1, detecte e compile fontes python (4/29, Casey) - Correção de um bug particularmente estúpido no qual o segundo jogador estava passando por um histórico incorreto. (30/4, dmckee; obrigado Josh)
Guerreiros iniciais
A título de exemplo, e para que os resultados possam ser verificados
Anjo
#include <stdio.h>
int main(int argc, char**argv){
printf("c\n");
return 0;
}
ou
#!/bin/sh
echo c
ou
#!/usr/bin/python
print 'c'
Diabo
#include <stdio.h>
int main(int argc, char**argv){
printf("t\n");
return 0;
}
Aleatória
#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;
}
Observe que o apontador pode re-invocar o guerreiro várias vezes em um segundo; portanto, um esforço sério deve ser feito para garantir a aleatoriedade dos resultados se o tempo estiver sendo usado para propagar o 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;
}
O primeiro que realmente faz algo com a história.
Executar o apontador apenas nos guerreiros fornecidos gera
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)
Aquele diabo, ele é um profissional, e caras legais aparentemente aparecem por último.
Resultados
da corrida "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
fonte
return (s1, L1+h1), (s2, L2+h1)
parareturn (s1, L1+h1), (s2, L2+h2)
[Note emL2+h2
vez deL2+h1
no final]? // Erro de cortar e colar ou algo igualmente idiota. Sheesh!Respostas:
Gradual
Essa estratégia é baseada em um artigo de Beaufils, Delahaye e Mathieu . Meu C realmente não é o melhor, por isso, se alguém tiver alguma sugestão para melhorar / acelerar o código, me avise!
[Editar] Vale a pena notar é que o Gradual foi projetado para ser uma estratégia que supera Tit for Tat. Tem propriedades semelhantes, pois está disposto a cooperar e retaliar contra um oponente desertor. Ao contrário do Tit for Tat, que só tem memória da última rodada jogada, o Gradual lembrará a interação completa e desertará o número de vezes que o oponente desertou até agora. No entanto, oferecerá cooperação mútua mais tarde.
Como sempre, o desempenho da estratégia depende um pouco da formação de outras estratégias. Além disso, o documento original não estava muito claro em alguns detalhes.
fonte
O aperto de mão secreto
A estratégia aqui é sacrificar as 10 primeiras rodadas para realizar um aperto de mão "secreto". Se eu estiver no meu quarto, reconheço a história dos 10 primeiros movimentos e ponho meu boné de anjo pelo resto do jogo. Assim que reconheço que meu colega de cela não sou eu, me transformo em um diabo na tentativa de tirar vantagem de colegas de cela excessivamente cooperativos.
Se o sacrifício das primeiras 10 rodadas me permitirá superar o próprio diabo depende fortemente de quantas entradas existem. Para minimizar os danos, apenas três cooperações aparecem no aperto de mão.
Edit: TAGMATCH dinâmico agora para evitar erros estúpidos, como alterar apenas um deles, para que eu possa tornar o TAG dinâmico em algum momento no futuro.
Edit 2: Agora gera aleatoriamente a tag na primeira execução e a armazena no arquivo especificado por
sys.argv[0]
(.pyc
substituído por.py
para que ele vá para o arquivo de código, não o código de bytes). Penso que esta é a única informação que todas as minhas instâncias têm e que ninguém mais tem, por isso parece ser a única opção para evitar parasitas.fonte
TAG
estava sendo jogado para trás. No entanto, você não deveriaTAGMATCH
ser 'KEEEKEEEKE'?"".join({('c', 'c'):'K', ('t', 't'): 'E'}[moves] for moves in zip(TAG, TAG))
.py
arquivos pelo seu código e extrair o TAG. Eu não vou fazer isso em C, embora ...The Little Lisper
O diabo
Considere o seguinte formato (Player1, Player2)
Supondo que P2 seja o diabo, não há como o diabo perder pontos; de fato, o pior que ele pode fazer é ganhar apenas um ponto. Portanto, contra um oponente puramente aleatório, a pior pontuação possível do diabo será exatamente (5/2) * n onde n é o número de "jogos" jogados. Seu pior caso absoluto é contra si mesmo, onde sua pontuação será n, e seu melhor caso é contra um anjo, que será 4 * n
Afirma: opt_strat = diabo
isso é um torneio. Dar um tapinha nas costas do meu companheiro de cela é uma estratégia muito melhor do que a cooperação, porque ajuda mais o MY SCORE (+4). BÔNUS - ele é atingido (-1)! Se eu esticar meu pescoço para ele, ganho (+2) e solto (-1). Portanto, estatisticamente, a punhalada é recompensada.
Mas é ideal?
Não há razão para NUNCA (sob este sistema de pontuação) cooperar.
No sistema KOTH, a maximização dos retornos é essencial. Mesmo que você tenha dois bots que são perfeitamente sincronizados e cooperam, as pontuações individuais ainda serão aumentadas em 200 pontos pelo espírito esportivo. Um diabo, por outro lado, ganhará pelo menos 100 pontos, com um caso médio de 200 e um máximo de 400, e custará aos seus oponentes até 100 pontos cada! Então, praticamente, o diabo realmente marca um jogo médio de 300, chegando a 500.
Bottom line - o tempo dirá
Para mim, parece que a pontuação deve ser repensada para que o diabo não tome o dia. Aumentar a pontuação da cooperação para 3, todos podem fazer isso. É possível, no entanto, detectar demônios e impedi-los de marcar todos os seus 400, como mostram pavlov e despeito. Posso provar que qualquer um deles receberá pontos suficientes para que sua cooperação justifique sua fé? não. Tudo isso depende do campo final dos candidatos.
E faça o seu pior para este post. Quero escrever meu artigo sobre isso quando tudo estiver dito e feito.
Histórico de versões
VERSÃO OFICIAL DO LISPER
VERSÃO DE DESENVOLVIMENTO LISPER
fonte
fink install clisp
:: batendo os dedos repetidamente ::There is no reason to EVER (under this scoring system) co-operate
é apenas meio correto. Se você sabe que seu oponente não leva em consideração a história (anjo, demônio, aleatório), deve sempre desertar. Se o seu oponente levar em consideração o histórico e você puder sincronizar, poderá fazer melhor. Eu tenho algumas idéias que giram em torno de detectar se o oponente é racional ou super-racional.(random 20)
der 2, 5 ou 8,(/ (+1 rand-num) 10)
é 0,3, 0,6, 0,9, e o restante da divisão com 0,3 é 0; então(floor *dbag* *margin*)
morre.Desconfiança (variante)
Este saiu primeiro em meus próprios testes anos atrás (naquela época eu estava no 11º ano e fiz uma pequena tese sobre exatamente isso, usando estratégias criadas por outros alunos também). Começa com a sequência
tcc
(e depois toca como Tit for Tat).Desculpas pelo código horrível; se alguém puder fazer isso mais curto sem jogar exatamente, eu ficaria grato :-)
fonte
case 1: case2: printf(...); break;
. E o gcc quer uma declaração explícita destring.h
usostrlen
. De qualquer forma, eu o tenho em execução.Popen(p+" "+h,stdout=subprocess.PIPE,shell=True)
quandoh = ''
. Eu estou supondoargc=1
.Míssil Anti-T42T
Faz razoavelmente bem contra o conjunto básico de guerreiros: mata Angel, levemente derrotado por Devil (mas mantém sua pontuação baixa), geralmente vence RAND com facilidade e apenas vence Tit por Tat. Faz mal quando joga contra si mesmo.
fonte
Convergência
Inicialmente agradável, depois joga aleatoriamente com um olho na história do oponente.
Tentei diminuir o peso da história, mas não a otimizei adequadamente.
fonte
Tubarão
Faz muito bem contra a lista de base.
fonte
Pavlov - Ganhe estadia, perca o interruptor
No primeiro turno, ele coopera e, em seguida, coopera se e somente se ambos os jogadores optaram pela mesma escolha na jogada anterior.
fonte
hist[0]
(hist[-1]
é sempre o primeiro passo da rodada)?Honra entre ladrões
Observe que
THIEVES_CANT
é essencialmente um aperto de mão, embora só surja quando estiver jogando contra um cooperador. No entanto, evita o problema do parasita, verificando se há cruzamentos posteriores. Faz muito bem contra a lista de base.fonte
"Probabimatic"
Começa por cooperar e, em seguida, escolhe a opção que oferece o maior valor esperado. Simples.
Costumava começar a cooperar, mas agora parece que desertar realmente funciona melhor. EDIT: Oh espera, na verdade não.
fonte
for (char c = *str;
para ochar c; for (c = *str;
gcc, ele será compilado sem reclamar que ele precisa ser colocado no modo C99.Vespa hiper-racional
Implementado em Java porque não tinha certeza de quão complexas as estruturas de dados terminariam. Se isso é um problema para alguém, acho que posso portá-lo para o bash sem muitos problemas, porque no final, ele realmente usa apenas matrizes associativas simples.
Nota : removi isso de um pacote alinhado com a versão mais recente do meu patch para o apontador para manipular Java. Se você deseja publicar uma solução Java que utiliza classes internas, precisará corrigir o patch.
As alterações que fiz no apontador para executar isso são:
Obrigado a @rmckenzie por desistir da minha função de desafiador.
fonte
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)
Soft_majo
Ah, bem, outra das estratégias padrão, apenas para completar a formação.
Este escolhe o movimento que o oponente mais fez; se igual, coopera.
fonte
Otário aleatório
Este irá desertar se o oponente desertar com muita freqüência (limite), mas tentará aleatoriamente dar uma punhalada de vez em quando.
Funciona razoavelmente bem contra todos, exceto os players Java e Lisp (que eu não consigo executar, devido a Java nem Lisp na máquina de teste); na maioria das vezes, pelo menos.
fonte
BYGONES
Ainda não calculamos o melhor valor
bygones
. Não espero que seja uma estratégia vencedora , mas estou interessado no desempenho de uma estratégia algo como o que eu acho que é "bom" na vida real. Uma revisão futura também pode incluir a verificação do número de deserções mútuas.fonte
Help Vampire
Tem um resultado divertidamente assimétrico quando lançado contra si mesmo. Se apenas essa solução pudesse ser aplicada na vida real.
fonte
druida
Faz razoavelmente bem contra a lista de base.
fonte
Simplório
Está bem contra a lista de base.
fonte
Little Schemer
Faz mal contra o conjunto de base, mas muito bem contra seu alvo. Obviamente, não está escrito em Scheme.
fonte
Peitos para duas tatuagens
outro velho favorito
fonte
sys.exit(0)
? Ou apenas deixe terminar. Editar: Além disso, a primeira chamada ao seu programa é sem histórico que causaIndexError
quando você o fazargv[1]
.len(history)<2
cláusula, porque essa última se parece com aelse
parte.return
especialmente!