Este é o Desafio Quinzenal # 3. Tema: Algoritmos Genéticos
Esse desafio é um pouco de experiência. Queríamos ver o que poderíamos fazer, por desafio, com algoritmos genéticos. Nem tudo pode ser ideal, mas fizemos o possível para torná-lo acessível. Se isso der certo, quem sabe o que poderemos ver no futuro. Talvez um rei genético da colina?
A especificação é bastante longa! Tentamos separar as especificações em The Basics - o mínimo necessário para começar a brincar com a estrutura e enviar uma resposta - e The Gory Details - a especificação completa, com todos os detalhes sobre o controlador, com base nos quais você poderia escrever o seu próprio.
Se você tiver alguma dúvida, sinta-se à vontade para se juntar a nós no chat!
Você é um pesquisador em psicologia comportamental. É sexta-feira à noite e você e seus colegas decidem se divertir e usar seus ratos de laboratório para uma pequena corrida de ratos. De fato, antes de nos apegarmos emocionalmente demais a eles, vamos chamá-los de espécimes .
Você montou uma pequena pista de corrida para as amostras e, para torná-la mais interessante, colocou algumas paredes, armadilhas e teleportadores na pista. Agora, seus espécimes ainda são ratos ... eles não têm idéia do que é uma armadilha ou um teleportador. Tudo o que eles vêem é algumas coisas em cores diferentes. Eles também não têm nenhum tipo de memória - tudo o que podem fazer é tomar decisões com base no ambiente atual. Eu acho que a seleção natural escolherá os espécimes que sabem como evitar uma armadilha daqueles que não sabem (essa corrida vai demorar um pouco ...). Que os jogos comecem! †
† 84.465 espécimes foram prejudicados na realização deste desafio.
O básico
Este é um jogo para um jogador (você e seus colegas não querem misturar as populações para que cada um construa sua própria pista de corrida). A pista de corrida é uma grade retangular, com 15 células de altura e 50 células de largura. Você começa com 15 amostras em células aleatórias (não necessariamente distintas) na borda esquerda (onde x = 0 ). Suas amostras devem tentar atingir a meta que é qualquer célula em x ≥ 49 e 0 ≤ y ≤ 14 (as amostras podem ultrapassar a faixa para a direita). Cada vez que isso acontece, você ganha um ponto. Você também inicia o jogo com 1 ponto. Você deve tentar maximizar seus pontos após 10.000 turnos.
Várias amostras podem ocupar a mesma célula e não irão interagir.
A cada turno, cada espécime vê uma grade 5x5 de seu entorno (com ela mesma no centro). Cada célula dessa grade conterá uma cor -1
para 15
. -1
representa células que estão fora dos limites. Seu espécime morre se sair dos limites. Quanto às outras cores, elas representam células vazias, armadilhas, paredes e teleportadores. Mas seu espécime não sabe qual cor representa o que e nem você. Existem algumas restrições:
- 8 cores representam células vazias.
- 4 cores representarão um teleporter. Um teleportador envia a amostra para uma determinada célula dentro de sua vizinhança 9x9. Esse deslocamento será o mesmo para todos os teleportadores da mesma cor.
- 2 cores representarão paredes. Mover-se para uma parede é o mesmo que ficar parado.
- 2 cores representam uma armadilha. Uma armadilha indica que uma das 9 células em sua vizinhança imediata é letal (não necessariamente a própria célula). Esse deslocamento será o mesmo para todas as armadilhas da mesma cor.
Agora, sobre essa seleção natural ... cada espécime tem um genoma, que é um número com 100 bits. Novos espécimes serão criados através da criação cruzada de dois espécimes existentes e, em seguida, mutando ligeiramente o genoma. Quanto mais bem sucedido um espécime, maior sua chance de se reproduzir.
Então, aqui está sua tarefa: você escreverá uma única função, que recebe como entrada a grade 5x5 de cores que um espécime vê, bem como seu genoma. Sua função retornará um movimento (Δx, Δy) para a amostra, onde Δx e Δy serão cada um deles {-1, 0, 1}
. Você não deve manter nenhum dado entre as chamadas de função. Isso inclui usar seus próprios geradores de números aleatórios. Sua função receberá um RNG inicial que você poderá usar livremente como desejar.
A pontuação do seu envio será a média geométrica do número de pontos em 50 faixas aleatórias. Concluímos que essa pontuação está sujeita a uma pequena variação. Portanto, essas pontuações serão preliminares . Quando esse desafio acabar, um prazo será anunciado. No final do prazo, 100 painéis serão escolhidos aleatoriamente e todos os envios serão resgatados nesses 100 painéis. Sinta-se à vontade para colocar uma pontuação estimada em sua resposta, mas pontuaremos cada envio para garantir que ninguém trapaceie.
Nós fornecemos programas de controlador em vários idiomas. Atualmente, você pode escrever sua submissão em Python (2 ou 3), Ruby , C ++ , C # ou Java . O controlador gera as placas, executa o jogo e fornece uma estrutura para o algoritmo genético. Tudo o que você precisa fazer é fornecer a função de movimentação.
Espere, então o que exatamente eu faço com o genoma?
O desafio é descobrir isso!
Como as amostras não têm memória, tudo o que você tem em um determinado turno é uma grade de cores 5x5 que não significa nada para você. Então você terá que usar o genoma para alcançar a meta. A idéia geral é que você use partes do genoma para armazenar informações sobre as cores ou o layout da grade, e seu bot baseia suas decisões nas informações adicionais armazenadas no genoma.
Agora, é claro que você não pode realmente armazenar nada lá manualmente. Portanto, as informações reais armazenadas inicialmente serão completamente aleatórias. Mas o algoritmo genético selecionará em breve aqueles espécimes cujo genoma contém a informação correta, enquanto mata aqueles que possuem a informação errada. Seu objetivo é encontrar um mapeamento a partir dos bits do genoma e do seu campo de visão para uma mudança, que permita encontrar um caminho para o objetivo rapidamente e que evolua consistentemente para uma estratégia vencedora.
Essas informações devem ser suficientes para você começar. Se desejar, você pode pular a próxima seção e selecionar seu controlador de escolha na lista de controladores na parte inferior (que também contém informações sobre como usar esse controlador em particular).
Leia se quiser tudo ...
The Gory Details
Esta especificação está completa. Todos os controladores precisam implementar essas regras.
Toda aleatoriedade usa uma distribuição uniforme, salvo indicação em contrário.
Geração de faixas:
- A pista é uma grade retangular, X = 53 células de largura e Y = 15 células de altura. Células com x ≥ 49 são células de objetivo (onde x é baseado em zero).
- Cada célula possui uma única cor e pode ou não ser letal - as células não são letais, a menos que especificado por um dos tipos de células abaixo.
- Existem 16 cores de células diferentes, rotuladas de
0
para15
, cujo significado mudará de jogo para jogo. Além disso,-1
representa células que estão fora dos limites - são letais . - Escolha 8 cores aleatórias . Estas serão células vazias (que não têm efeito).
- Escolha mais 4 cores aleatórias . Estes são teleportadores. Para duas dessas cores, escolha um deslocamento diferente de zero no bairro 9x9 (de (-4, -4) a (4,4), exceto (0,0)). Para as outras duas cores, inverta essas compensações. Se uma amostra pisa em um teleportador, ela é imediatamente movida por esse deslocamento.
- Escolha mais 2 cores aleatórias . Estas são armadilhas. Para cada uma dessas cores, escolha um deslocamento na vizinhança 3x3 (de (-1, -1) a (1,1)). Uma armadilha indica que a célula nesse deslocamento é letal . Nota: A própria célula de armadilha não é necessariamente letal.
- As 2 cores restantes são paredes, que impedem o movimento. Tentar mover-se para uma célula da parede transformará o movimento em ficar parado. As próprias células da parede são letais .
- Para cada célula não-objetivo da grade, escolha uma cor aleatória. Para cada célula de objetivo, escolha uma cor vazia aleatória .
- Para cada célula na borda esquerda da pista, determine se a meta pode ser alcançada em 100 turnos (de acordo com as regras de ordem dos turnos abaixo). Nesse caso, essa célula é uma célula inicial admissível . Se houver menos de 10 células iniciais, descarte a faixa e gere uma nova.
- Crie 15 espécimes, cada um com um genoma aleatório e envelheça 0 . Coloque cada amostra em uma célula inicial aleatória.
Ordem de volta:
- As etapas a seguir serão executadas, em ordem, para cada amostra. As amostras não interagem ou não se vêem e podem ocupar a mesma célula.
- Se a idade da amostra for 100 , ela morre. Caso contrário, aumente sua idade em 1.
- O espécime recebe seu campo de visão - uma grade de cores 5x5, centralizada no espécime - e retorna um movimento em sua vizinhança 3x3. Movimentos fora desse intervalo farão com que o controlador termine.
- Se a célula de destino for uma parede, a movimentação será alterada para (0,0).
- Se a célula alvo for um teletransportador, a amostra é movida pelo deslocamento do teletransportador. Nota: Esta etapa é executada uma vez , não iterativamente.
- Se a célula atualmente ocupada pela amostra (potencialmente após o uso de um teleportador) for letal, a amostra morre. Este é o único momento em que as amostras morrem (além da etapa 1.1. Acima). Em particular, um novo espécime que gera uma célula letal não morre imediatamente, mas tem a chance de sair da célula perigosa primeiro.
- Se a amostra ocupa uma célula-alvo, marque um ponto, mova-a para uma célula inicial aleatória e redefina sua idade para 0.
- Se houver menos de duas amostras no tabuleiro, o jogo termina.
- Crie 10 novas amostras com a idade 0 . Cada genoma é determinado (individualmente) pelas regras de criação abaixo. Coloque cada amostra em uma célula inicial aleatória.
Reprodução:
Quando um novo espécime é criado, escolha dois pais distintos aleatoriamente, com um viés em relação aos espécimes que progrediram mais à direita. A probabilidade de uma amostra ser escolhida é proporcional ao seu escore de aptidão atual . A pontuação de aptidão de um espécime é
1 + x + 50 * número de vezes que atingiu a meta
onde x é o índice horizontal com base em 0. Espécimes criados no mesmo turno não podem ser escolhidos como pais.
Dos dois pais, escolha um aleatório para escolher o primeiro genoma.
- Agora, enquanto você caminha pelo genoma, troque os pais com uma probabilidade de 0,05 e continue recebendo bits do pai resultante.
- Mude o genoma totalmente montado: para cada bit, vire-o com probabilidade 0,01 .
Pontuação:
- Um jogo dura 10.000 turnos.
- Os jogadores iniciam o jogo com 1 ponto (para permitir o uso da média geométrica).
- Sempre que um espécime atinge a meta, o jogador marca um ponto.
- Por enquanto, a submissão de cada jogador será realizada por 50 jogos, cada um com uma faixa aleatória diferente.
- A abordagem acima resulta em mais variação do que o desejável. Quando esse desafio acabar, um prazo será anunciado. No final do prazo, 100 painéis serão escolhidos aleatoriamente e todos os envios serão resgatados nesses 100 painéis.
- A pontuação geral de um jogador é a média geométrica da pontuação desses jogos individuais.
Os controladores
Você pode escolher qualquer um dos seguintes controladores (pois eles são funcionalmente equivalentes). Nós testamos todos eles, mas se você encontrar um bug, quiser melhorar o código ou o desempenho ou adicionar um recurso como saída gráfica, envie um problema ou envie uma solicitação de recebimento no GitHub! Você também pode adicionar um novo controlador em outro idioma!
Clique no nome do idioma de cada controlador para chegar ao diretório correto no GitHub, que contém README.md
instruções de uso exatas.
Se você não conhece o git e / ou o GitHub, pode fazer o download de todo o repositório como um ZIP na primeira página (veja o botão na barra lateral).
Pitão
- Mais exaustivamente testado. Esta é a nossa implementação de referência.
- Funciona com o Python 2.6+ e o Python 3.2+!
- Está muito lento. Recomendamos executá-lo com PyPy para uma aceleração substancial.
- Suporta saída gráfica usando
pygame
outkinter
.
Rubi
- Testado com Ruby 2.0.0. Deve funcionar com versões mais recentes.
- Também é bastante lento, mas Ruby pode ser conveniente para criar uma idéia para uma submissão.
C ++
- Requer C ++ 11.
- Opcionalmente, suporta multithreading.
- De longe o controlador mais rápido do grupo.
C #
- Usa o LINQ, portanto, requer o .NET 3.5.
- Bastante lento.
Java
- Não é particularmente lento. Não é particularmente rápido.
Tabela de Classificação Preliminar
Todas as pontuações são preliminares. No entanto, se algo estiver errado ou desatualizado, entre em contato. Nosso envio de exemplo está listado para comparação, mas não na disputa.
Score | # Games | User | Language | Bot
===================================================================================
2914.13 | 2000 | kuroi neko | C++ | Hard Believers
1817.05097| 1000 | TheBestOne | Java | Running Star
1009.72 | 2000 | kuroi neko | C++ | Blind faith
782.18 | 2000 | MT0 | C++ | Cautious Specimens
428.38 | | user2487951 | Python | NeighborsOfNeighbors
145.35 | 2000 | Wouter ibens | C++ | Triple Score
133.2 | | Anton | C++ | StarPlayer
122.92 | | Dominik Müller | Python | SkyWalker
89.90 | | aschmack | C++ | LookAheadPlayer
74.7 | | bitpwner | C++ | ColorFarSeeker
70.98 | 2000 | Ceribia | C++ | WallGuesser
50.35 | | feersum | C++ | Run-Bonus Player
35.85 | | Zgarb | C++ | Pathfinder
(34.45) | 5000 | Martin Büttner | <all> | ColorScorePlayer
9.77 | | DenDenDo | C++ | SlowAndSteady
3.7 | | flawr | Java | IAmARobotPlayer
1.9 | | trichoplax | Python | Bishop
1.04 | 2000 | fluffy | C++ | Gray-Color Lookahead
Créditos
Esse desafio foi um enorme esforço colaborativo:
- Nathan Merril: Escreveu controladores Python e Java. Transformado o conceito de desafio de um King-of-the-Hill em uma corrida de ratos.
- trichoplax: Playtesting. Trabalhou no controlador Python.
- feersum: Escreveu o controlador C ++.
- VisualMelon: Escreveu o controlador C #.
- Martin Büttner: Conceito. Escreveu o controlador Ruby. Playtesting. Trabalhou no controlador Python.
- T Abraham: Teste de reprodução. Testou o Python e revisou o controlador C # e C ++.
Todos os usuários acima (e provavelmente esqueci mais alguns) contribuíram para o design geral do desafio.
Atualização do controlador C ++
Se você estiver usando o C ++ com Visual Studio e multithreading, deverá receber a atualização mais recente devido a um erro na propagação de gerador de número aleatório que permite a produção de placas duplicadas.
'In particular, a new specimen which spawns on a lethal cell will not die immediately, but has a chance to move off the dangerous cell first.'