Em esta questão , um jogo foi inventado em que os jogadores teriam de enfrentar um ao outro par a par no Dilema do Prisioneiro, para determinar qual estratégia iterativa obteve a maior pontuação contra os outros.
Em esta pergunta , eu desenvolveu uma maneira para várias pessoas para jogar Dilema dos Prisioneiros uns contra os outros, tudo ao mesmo tempo. Nesta variação, a matriz de pagamento é desnecessária, com cada resultado entre cada par de dois jogadores sendo a soma de duas decisões funcionalmente independentes.
Sua tarefa é criar uma IA para jogar esta versão simétrica e generalizada do dilema do prisioneiro multiplayer que alcançará a maior pontuação possível.
Regras do jogo
Em cada rodada deste Dilema do Prisioneiro, multijogador e multijogador, um jogador A
pode decidir "tirar 1" de outro jogador B
. Nessa circunstância, A
a pontuação aumenta em 1, enquanto B
a pontuação diminui em 2. Essa decisão pode acontecer entre cada par de jogadores ordenados.
Esta é a única decisão tomada para cada jogador - "tomar 1" ou não "tomar 1" um do outro jogador, que são homólogos à deserção e à cooperação, respectivamente. A matriz de pagamento efetiva entre dois jogadores P1
e P2
tem a seguinte aparência:
P1/P2 P1 Take1 P1 Don't
P2 Take1 -1/-1 -2/+1
P2 Don't +1/-2 0/ 0
Procedimento do Torneio
O jogo consistirá em P * 25
rodadas, onde P
é o número de jogadores participantes. Todos os jogadores começam com uma pontuação de 0
. Cada rodada consistirá no seguinte procedimento:
No início de uma rodada, cada programa receberá um histórico das rodadas anteriores a partir da entrada padrão , no seguinte formato:
Uma linha contendo 3 números,
P
,D
, eN
.P
é o número total de jogadores no jogo. Cada jogador recebe um número de identificação aleatoriamente de1
atéP
no início do jogo.D
é o ID do jogador atual.N
é o número de rodadas que foram jogadas.
N
linhas, cada linha representando os resultados de uma rodada. Na linhak
deN
, haverá um númeron_k
de pares ordenados(a, b)
, separados por espaços, que representam que o jogador com IDa
"tirou 1" do jogador com IDb
nessa rodada.Um número uniformemente aleatório
R
de0
a18446744073709551615
(2 64 - 1), para atuar como uma semente pseudo-aleatória. Esses números serão lidos a partir de um arquivo pré-gerado, que será lançado no final do torneio para que as pessoas possam verificar os resultados por si mesmas.Uma linha extra que representa alguma forma de estado a ser lida no seu programa, se o seu programa produziu essa saída na rodada anterior. No início do jogo, esta linha estará sempre vazia. Esta linha não será modificada pelo código de pontuação ou por outros programas.
Cada programa usará sua estratégia para produzir o seguinte para a saída padrão :
Uma lista de
K
números, que são os IDs dos programas que serão "retirados 1" desta rodada. Uma saída vazia significa que não fará nada.Opcionalmente, uma linha extra representando algum tipo de estado para repassar para as rodadas posteriores. Essa linha exata será retornada ao programa na próxima rodada.
Abaixo está um exemplo de entrada para o início do jogo para um jogador de ID 3
em um jogo para 4 jogadores:
4 3 0
4696634734863777023
Abaixo está um exemplo de entrada para o mesmo jogo com algumas rodadas já jogadas:
4 3 2
(1, 2) (1, 3) (1, 4) (4, 2)
(1, 3) (2, 1) (2, 4) (3, 1) (4, 1)
4675881156406346380
Cada programa será alimentado exatamente da mesma entrada para uma rodada, exceto o número de identificação D
exclusivo de cada programa.
Abaixo está um exemplo de saída em que o jogador 3
recebe 1 de todos os outros:
1 2 4
No final de todas as rodadas necessárias, o jogador com a maior pontuação final será o vencedor.
Linha do tempo
A codificação deste torneio durará um total de 7 dias. O prazo para envio é 2014-05-09 00:00 UTC
.
Não publique programas reais antes desta data - publique o hash SHA256 do código fonte do seu programa como um compromisso. Você pode alterar esse hash a qualquer momento antes do prazo final, mas os compromissos postados após o prazo final não serão aceitos para julgamento. (Use a notação base 64 para seus hashes, pois meu programa de verificação apresenta a base 64 e é uma notação mais compacta.)
Após o término do prazo, você terá 1 dia (até 2014-05-10 00:00 UTC
) para publicar o código-fonte real do seu programa para envio. Se o hash SHA256 do seu código-fonte postado não corresponder a nenhum hash que você postou antes do prazo, seu código não será aceito no torneio.
Depois disso, baixarei todos os envios para o meu próprio computador e executarei todas as entradas do torneio neste battle royale, postando os resultados dentro de dois dias a partir de então 2014-05-12 00:00 UTC
.
Aceitarei a resposta com a pontuação mais alta e atribuirei uma recompensa de +100 a essa resposta se sua pontuação final for maior que 0
.
Após o término do torneio, publicarei o arquivo de distribuição aleatória usado para executar a competição, e as pessoas poderão começar a postar outras soluções tentando superar as usadas no torneio. No entanto, eles não contarão para aceitação ou recompensa.
A máquina host
Eu executarei essas soluções em uma máquina virtual no meu computador. Esta máquina virtual executará o Ubuntu Linux 14.04, com 2 gigabytes de RAM. Minha máquina base possui um processador Intel i7-2600K rodando a 3,40 GHz.
Exigências
Seu programa deve ser escrito em um idioma para o qual exista um compilador ou intérprete que irá compilar seu programa e esteja prontamente disponível para a versão mais recente do Ubuntu Linux, para que eu possa executar todos os envios e julgá-los em uma máquina virtual.
Seu programa não deve demorar mais do que 2.000 seconds
para executar cada rodada. Se o seu programa ficar sem tempo ou produzir um erro, sua saída será considerada vazia para essa rodada.
Seu programa deve ser determinístico; isto é, sempre deve retornar a mesma saída para a mesma entrada. Soluções pseudo-aleatórias são permitidas; no entanto, sua aleatoriedade deve depender da semente aleatória fornecida como entrada e nada mais. O arquivo inicial foi gerado usando o Python os.urandom
. Ele contém um total de 500 linhas (mais será gerado, se necessário) e seu hash SHA256 é K+ics+sFq82lgiLanEnL/PABQKnn7rDAGmO48oiYxZk=
. Será carregado aqui assim que o torneio terminar.
Plantas
Para começar, haverá quatro "plantas", representando estratégias iniciais ingênuas. Eles estarão jogando no torneio junto com as suas inscrições. No entanto, no caso improvável de um deles vencer, a pontuação mais alta obtida por um jogador que não seja uma planta será considerada vencedora.
Para calcular o hash do arquivo de cada planta, substitua cada grupo de 4 espaços por uma tabulação, pois o formatador aqui não parece gostar de caracteres de tabulação.
O Preguiçoso - nunca faz nada.
n1bnYdeb/bNDBKASWGywTRa0Ne9hMAkal3AuVZJgovI=
pass
The Greedy - sempre tira 1 de todo mundo.
+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 - pega 1 de todos os outros no primeiro turno e pega 1 de todos que tiraram 1 dele na rodada anterior depois.
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 - pega 1 dos 50% de jogadores com a pontuação mais alta atual, excluindo-se, arredondando para baixo.
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
Em um torneio de 100 rodadas, dentre as quatro, eles recebem dezenas de:
Lazy: -204
Greedy: -100
Wrathful: -199
Envious: -199
Programa de Julgamento
Publiquei o programa de juízes que utilizarei no Github . Faça o download e teste. (E talvez conserte um bug ou dois, se você encontrar um.: P)
Ele não tem opções de compilação para nada além de Python no momento. Vou incluí-los mais tarde - se as pessoas pudessem contribuir com scripts de compilação ou interpretação para outros idiomas, ficaria muito grato.
Fase 2: Envio do Código Fonte
tournament
Publiquei uma nova ramificação no repositório do Github para o concurso, contendo o arquivo pd_rand e outras entradas da planta. Você pode postar seu código-fonte aqui ou enviá-lo para esse ramo como uma solicitação de recebimento.
A ordem dos competidores será a seguinte:
'begrudger'
'regular'
'patient'
'lazy'
'backstab'
'bully'
'lunatic'
'envious'
'titfortat'
'greedy'
'wrathful'
'judge'
'onepercent'
Pontuações finais
A saída do meu programa de teste:
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
Classificação:
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
Acontece que o vencedor é realmente um jogador - é o The Regular, com -204 pontos!
Infelizmente, sua pontuação não foi positiva, mas mal podemos esperar isso em uma simulação do Dilema do Prisioneiro Iterado, onde todos estão jogando para ganhar.
Alguns resultados surpreendentes (pelo menos eu pensei que eram surpreendentes):
Os gananciosos marcaram mais que Tit por Tat e, de fato, geralmente são mais altos do que a maioria dos artilheiros.
O juiz, que deveria ser uma espécie de personagem "impositor da moralidade" (basicamente levou 1 de quem tirou 1 de alguém um número acima da média de vezes) acabou pontuando bastante alto, enquanto nos testes de simulação, na verdade, obtenha uma pontuação bastante baixa.
E outros que (eu pensei) não eram tão surpreendentes:
O Paciente marcou 484 pontos a mais que The Wrathful. Realmente vale a pena cooperar nessa primeira vez.
Um por cento muito rapidamente não tinha quase ninguém para chutar enquanto caíam. Parece que o 1% só é capaz de permanecer assim, porque eles têm mais jogadores no jogo.
De qualquer forma, agora que o torneio terminou, fique à vontade para postar quantos jogadores extras desejar e testar com eles usando o programa de juízes.