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.
fonte
'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.'
Respostas:
A fé cega - C ++ - parece ter uma pontuação acima de 800 (!) Em 2000 corridas
Genoma do código de cores com um feedback misterioso da faixa e um impedimento eficaz de bater na parede
Resultados da amostra:
Com base no teste involuntariamente longo do feersum, acho que 2000 execuções são suficientes para produzir um resultado aceitável estável.
Como meu controlador modificado exibe a média geométrica atual após cada execução, confirmei visualmente que a variação nas últimas 50 execuções era relativamente pequena (+ - 10 pontos).
O que faz com que essas criaturas estejam
Em vez de dar prioridades iguais para cada cor, considero estes possíveis valores:
Embora eu esteja com preguiça de renomeá-lo, esse é um "detector de perigo" indicando a (suposta) localização de uma armadilha real, uma parede, um teleportador esperando para enviar o viajante desavisado para um lugar desagradável ou até para a entrada de um morto -fim. Em suma, um lugar onde um rato sábio prefere não ir.
genes bons ou ruins levam apenas 2 bits para armazenar (por exemplo,
11
e10
), mas as armadilhas requerem 4 bits (0ttt
ondettt
representa um dos possíveis 8 locais "perigosos").Para manter cada gene coerente (ou seja, mantendo o seu significado depois foi misturado em um genoma completamente diferente, o que exige que cada gene de codificação de cores a ser num local fixo), todos os valores são codificados em 4 bits (de modo bom está codificada como
11xx
e mau quanto10xx
), para um total de 16 * 4 = 64 bits.Os 36 bits restantes são usados como "anti-bangers" (mais sobre isso posteriormente). As 25 cores circundantes são divididas em um índice desses 36 bits. Cada bit indica uma direção vertical preferida (para cima ou para baixo), usada quando há uma escolha possível entre duas células.
A estratégia é a seguinte:
Vós roedores, contemplai os inimigos de sua espécie
A pior coisa que pode acontecer a uma população é não ter produzido nenhum vencedor ainda, mas muitos ratos presos contra uma parede ou dentro de um loop infinito de teletransporte perto o suficiente do objetivo de ter uma chance dominante de serem selecionados para reprodução .
Ao contrário dos ratos serem esmagados em uma armadilha ou teleportados para as paredes, esses roedores só serão mortos pela velhice.
Eles não têm vantagem competitiva sobre seus primos presos em três células desde o início, mas terão tempo suficiente para reproduzir geração após geração de cretinas até que seu genoma se torne dominante, prejudicando gravemente a diversidade genética sem uma boa razão.
Para mitigar esse fenômeno, a idéia é tornar os filhos desses ratos ruins e ruins mais propensos a evitar seguir os passos de seus ancestrais.
A indicação da direção vertical tem apenas 1 bit de comprimento (basicamente dizendo "tente subir ou descer primeiro nesses ambientes") e é provável que alguns bits tenham efeito no caminho a seguir; portanto, mutações e / ou cruzamentos devem ter um impacto significante.
Muitos filhotes terão um comportamento diferente e não acabarão batendo a cabeça na mesma parede (entre os cadáveres de seus ancestrais famintos).
O subtítulo aqui é que essa indicação não é o fator dominante no comportamento do rato. A interpretação das cores ainda prevalecerá na maioria dos casos (a opção para cima / baixo será importante apenas se houver realmente duas "boas"e o que o rato vê como uma cor inofensiva não é um teleportador esperando para jogá-lo na parede).
Por que (parece) funcionar?
Ainda não sei exatamente o porquê.
O golpe absoluto da sorte que permanece um mistério não resolvido é a lógica do mapeamento de armadilhas. É sem dúvida a pedra angular do sucesso, mas funciona de maneira misteriosa.
Com a codificação utilizada, um genoma aleatório produzirá identificadores de cores 25% "bom", 25% "ruim" e 50% "armadilha".
os identificadores de "armadilha", por sua vez, produzirão estimativas "boas" e "ruins" em correlação com o ambiente 5x5.
Como resultado, um rato em um determinado local "verá" o mundo como uma mistura de cores estáveis e contextuais "vai / não vai".
Como o bem-sucedido mecanismo anti-golpe parece indicar, o pior tipo de elemento na pista é a parede temida (e sua prima no circuito de teletransporte, mas acho que são muito menos comuns).
A conclusão é que um programa bem-sucedido deve, acima de tudo, conseguir desenvolver ratos capazes de detectar posições que levarão a uma fome lenta sem atingir a meta.
Mesmo sem "adivinhar" as duas cores que representam as paredes, as cores da "armadilha" parecem contribuir para evitar paredes, permitindo que um rato contorne alguns obstáculos, não porque "viu" as paredes, mas porque a estimativa da "armadilha" descartou essas células da parede em particular nesse ambiente particular.
Embora o rato tente avançar em direção à meta (o que pode levar a pensar que os indicadores de armadilha mais "úteis" são os que indicam um perigo à frente), acho que todas as direções da armadilha têm aproximadamente a mesma influência: uma armadilha indicando "perigo por trás" "localizado 2 células na frente de um rato tem a mesma influência que uma indicando" perigo à frente "quando o rato está bem em cima dele.
Por que essa mistura tem a propriedade de fazer o genoma convergir com tanto sucesso está muito além da minha matemática, infelizmente.
Eu me sinto mais confortável com o impedimento de bater na parede. Isso funcionou como planejado, embora bem acima das minhas expectativas (a pontuação foi basicamente multiplicada por quatro).
Eu cortei o controlador fortemente para exibir alguns dados. Aqui estão algumas corridas:
Aqui, uma raça de super-ratos apareceu cedo (a pista provavelmente pode ser executada em linha reta e algum rato sortudo nas primeiras gerações tinha o DNA certo para tirar vantagem disso). O número de amostras no final é cerca da metade do máximo teórico de cerca de 100.000 ratos, o que significa que quase metade das criaturas adquiriu a capacidade de sobreviver indefinidamente a essa faixa em particular (!).
Obviamente, a pontuação resultante é simplesmente obscena - assim como o tempo de computação.
Aqui podemos ver o refinamento do genoma em ação. A linhagem entre os dois últimos genomas aparece claramente. As avaliações boas e ruins são as mais significativas. As indicações da armadilha parecem oscilar até estabilizarem-se em uma armadilha "útil" ou se transformarem em boas ou más .
Parece que os genes das cores têm algumas características úteis:
(uma cor específica deve ser tratada de uma maneira específica)
Cada código de cores pode ser jogado em um genoma completamente diferente sem alterar drasticamente o comportamento - a menos que a cor seja de fato decisiva (normalmente uma parede ou um teleporter levando a um loop infinito).
Esse é menos o caso de uma codificação prioritária básica, já que a cor mais prioritária é a única informação usada para decidir para onde se mover. Aqui todas as cores "boas" são iguais, portanto, uma determinada cor adicionada à lista "boa" terá menos impacto.
a codificação boa / ruim possui apenas 2 bits significativos em 4, e a localização da armadilha pode ser alterada na maioria das vezes sem alterar significativamente o comportamento do rato.
Um gene que muda para "bom" terá pouco efeito (se, por exemplo, corresponder a uma célula vazia, poderá permitir encontrar um caminho novo e mais curto, mas também pode levar o rato a uma armadilha) ou dramática (se a cor representar uma parede, é muito provável que o novo rato fique preso em algum lugar).
Um gene que se move para "prender" privará o rato de uma cor essencial ou não terá efeito perceptível.
Uma mutação de um local de armadilha só será importante se houver realmente uma armadilha (ou qualquer coisa prejudicial) à frente, que tenha uma probabilidade relativamente pequena (eu diria algo como 1/3).
Por fim, acho que os últimos 36 bits contribuem não apenas para evitar que os ratos fiquem presos, mas também para espalhá-los de maneira mais uniforme na pista, preservando assim a diversidade genética até que um genoma vencedor surja e se torne dominante na parte do código de cores.
Trabalho adicional
Devo dizer que acho essas criaturinhas fascinantes.
Mais uma vez obrigado a todos os colaboradores deste excelente desafio.
Estou pensando em abater ainda mais o controlador para exibir dados mais significativos, como a ascendência de um rato bem-sucedido.
Eu também gostaria muito de ver esses ratos em ação, mas essa linguagem C ++ b ** ch torna a criação - muito menos a animação - de imagens (entre muitas outras coisas) uma tarefa confusa.
No final, gostaria de produzir pelo menos uma explicação do sistema de armadilhas e possivelmente melhorá-lo.
Controle de hackers
Se alguém estiver interessado, posso publicar as modificações que fiz no controlador.
Eles são sujos e baratos, mas fazem o trabalho.
Eu não sou conhecedor do GitHub, então isso teria que passar por um mero post.
fonte
^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^
significa? O resto eu posso adivinhar, mas estou tendo problemas com isso?Crentes duros - C ++ - (teleportadores aprimorados): 10.000+ para 2000 corridas
(esta é uma evolução da fé cega , então você pode escalar outra parede de texto antes desta)
Episódio IV: nos orientando
Resultados
Eu mudei para g ++ / MinGW e 3 threads.
O código gerado pelo GNU é duas vezes mais rápido que o da Microsoft.
Não é de admirar, com a terrível implementação do STL.
Teleporters
O efeito Teleporter é altamente dependente da posição. Até agora, eu estava feliz em considerar um teletransportador como sempre bom (visto como um espaço vazio) ou sempre ruim (visto como uma parede para que nenhum roedor o aceitasse).
Este é um modelo muito grosso.
Um determinado teleportador pode impulsionar um rato para frente até algumas células do objetivo, mas uma vez lá, o mesmo teleportador pode jogá-lo fora do tabuleiro.
É provável que um teletransportador seja reconhecido como aceitável (uma vez que aumenta a aptidão mais rapidamente do que quando "caminha" para o mesmo local x), se torna parte do genoma dominante e mata quase todos os ratos que confiam nele como "sempre seguro".
Como os ratos não têm como saber sua posição X, a única solução para detectar esses teleportadores traiçoeiros é decidir se os pisará com base nos únicos dados contextuais disponíveis, ou seja, na grade de cores 5x5.
Para isso, defini 4 tipos de genes de cores:
A idéia é tentar distinguir um teletransportador olhando seus 8 vizinhos imediatos. Como a probabilidade de ter 8 vizinhos idênticos em um determinado local é muito baixa, isso deve permitir identificar uma instância única de cada teleportador.
As oito cores vizinhas podem ser combinadas para formar uma assinatura local, que é invariável, respectivamente, para a posição no labirinto. Infelizmente, os 8 vizinhos são visíveis apenas para células localizadas dentro do campo interno de 3x3 do campo de visão; portanto, as assinaturas serão imprecisas na borda do campo de visão.
No entanto, isso nos dará uma informação contextual constante na vizinhança imediata, o que é suficiente para aumentar a probabilidade de navegar com sucesso pelos teletransportadores.
genes de feixe têm um campo variável de 2 bits.
Para uma determinada assinatura local do teletransportador, há uma chance em quatro de que a célula do feixe seja considerada intransitável. Cada valor do campo seleciona uma dessas quatro possibilidades.
Como resultado, uma mutação no gene do feixe nesses 2 bits percorrerá 4 possíveis significados contextuais da cor.
Além disso, as cores mais importantes para adivinhar ainda são paredes e armadilhas. Isso significa que devemos permitir a detecção do teletransportador somente depois que os ratos souberem onde estão as paredes e as armadilhas.
Isso é feito atualizando as assinaturas locais apenas com moderação. O critério atual para a atualização de uma assinatura local é a proximidade de uma cor identificada como um potencial teleportador.
A codificação usa 5 bits por gene de cor e agrupa tipos para liberar os 3 bits menos significativos para codificar um valor 0..7:
Cada gene do feixe tem 1/4 de chance de ser considerado um bloco e 3/4 de chances de ser considerado vazio, portanto, 4 feixes representam em média 1 bloco e 3 vazios.
A proporção média representada por uma distribuição aleatória de 16 cores é assim:
Esse mix parece dar os melhores resultados até agora, mas não terminei de ajustá-lo.
Mutabilidade genética
Uma coisa é certa: os valores de código escolhidos para representar os tipos de genes são críticos. A inversão de dois valores pode custar 2000 pontos ou mais.
Aqui, novamente, a razão pela qual está além da minha matemática.
Meu palpite é que as probabilidades de mutação de um tipo para outro devem ser equilibradas; caso contrário, como em uma matriz de Markow, as probabilidades cumulativas tendem a restringir os valores ao subconjunto com as maiores probabilidades de transição de entrada.
Caminho para o resgate
O rastreamento reduzirá drasticamente o número de células visitadas, permitindo testar apenas os que têm maior probabilidade de levar ao objetivo. Assim, não apenas são evitados alguns becos sem saída frequentes, mas também é muito mais provável que códigos de cores errados sejam descobertos mais cedo.
Como resultado, o tempo de convergência é fortemente reduzido.
No entanto, isso não ajuda a resolver os mapas em que o genoma é incapaz de produzir uma representação adequada da trilha.
O que fazer com os idiotas?
Depois de olhar visualmente para a pista, entendi por que uma estratégia padrão que tenta avançar mesmo quando parece não haver nada além de muros na frente é realmente melhor do que reter.
"muros" podem ser, na realidade, teleportadores que produzem tantos resultados infelizes que o genoma os mapeia como obstáculos para nunca serem pisados, mas em raras ocasiões uma instância específica desse teletransportador impertinente pode ter um efeito positivo (ou pelo menos não letal) , portanto, levá-lo em vez de voltar atrás aumenta as chances de encontrar um caminho para a vitória.
Convergência precoce
Parece-me que a taxa de mutação é um pouco baixa (pelo menos para os meus roedores).
A configuração atual de 0,01 dá ao DNA 37% de chances de sobreviver intacto ao processo de mutação. Alterar o parâmetro para 0,0227 reduz essa probabilidade para cerca de 10%
Repeti exatamente o mesmo teste (usando uma sequência fixa de sementes aleatórias) com uma probabilidade de 10%.
Em muitos mapas, as falhas anteriores se transformaram em sucessos (limitados). Por outro lado, enormes explosões populacionais foram menores (o que teve o interessante efeito colateral de acelerar muito a computação).
Embora as pontuações muito altas (um milhão ou mais) fossem menos comuns, o número de execuções mais bem-sucedidas foi mais do que suficiente para compensar.
No final, a média aumentou de 1400+ para cerca de 2000.
Ajustar P para 5%, pelo contrário, produziu uma média de cerca de 600.
Suponho que a taxa de mutação foi tão alta que o genoma de ratos vencedores se transformou com frequência em variantes menos eficientes.
Como este funciona
Com os detectores de teleporter adicionados, o número de jogos com falha (pontuação <10) caiu significativamente.
Em um teste de 2000 execuções, houve apenas 1/3 das falhas.
A média geométrica aumentou apenas de 2900 para 3300, mas esse número não reflete a melhoria.
Cores vazias são frequentemente adivinhadas como vigas e perigos (geralmente 2 a 5). O genoma "usa" essas cores para bloquear caminhos que causariam problemas aos ratos.
O genoma é muito bom em adivinhar armadilhas (ou seja, quando os ratos conseguem atingir a meta, as cores que representam os detectores reais são adivinhadas cerca de 90% das vezes).
Ele também usa os novos códigos de feixe para teleportadores, embora mais raramente (provavelmente porque os teleportadores "traiçoeiros" são menos comuns que as armadilhas, e outras cores de feixe / perigo evoluem para bloquear o caminho para as últimas instâncias desses traidores).
A julgar pelo número de jogos em que um genoma vencedor surge após 5000 turnos ou mais, acho que essa nova raça se beneficiaria muito com o aumento da taxa de mutação.
fonte
ColorScorePlayer, pontuação preliminar ≈ 22
Este é o bot que você vê no trabalho no GIF no desafio.
Esse foi o nosso bot de teste durante toda a fase de desenvolvimento. Ele usa o genoma para armazenar um índice de qualidade para cada uma das 16 cores. Em seguida, faz o movimento para frente que a move com a melhor pontuação (e nunca se move
-1
). Em caso de empate, um movimento aleatório entre as células vinculadas é escolhido.Portamos este player para todos os idiomas do controlador, portanto, ele funciona como exemplo de como usá-los:
Pitão
Rubi
C ++
C #
Java
O jogador marca bastante inconsistentemente. Aqui estão 50 execuções aleatórias:
fonte
ColorFarSeeker, C ++ ≈ 74.7
Esse desafio é realmente muito divertido e simples se você tentar.
Não se deixe levar pela descrição longa.
Basta visitar o GitHub e conferir as coisas ... tudo ficará muito mais claro! :)
O simulador C ++ é altamente recomendado por sua velocidade. Mesmo depois de terminar de traduzir meu programa python para C ++, a simulação em python ainda não parou.
Essa é uma variante aprimorada do ColorScorePlayer. Para fazer bom uso de sua visualização 5x5, ele considera os passos 2 passos usando uma função ponderada. Os movimentos 1 passos à frente recebem um peso maior, pois têm um efeito mais imediato na sobrevivência. Dê 2 passos à frente com menor peso.
Tenta avançar, mas se nenhum movimento seguro for visto ... então tenta de lado ... e se tudo mais falhar, recua aleatoriamente.
Ponto:
Há um pouco de 1s ... o que pode ser um pouco deprimente quando você vê o console vomitando um após o outro. Como um planeta com todas as necessidades para a vida, mas sem sinais de civilizações avançadas de ratos ...
Depois, os picos ocasionais. :)
Hmm ... aparentemente tive sorte no meu primeiro lote de corridas, obtendo uma geométrica de mais de 300. As pontuações realmente variam bastante. Mas, de qualquer forma, com mais execuções do simulador, é provavelmente mais próximo de 74. (Thx feersum por me ajudar a simular e seu incrível programa rápido)
fonte
Bishop - Python, pontuação preliminar 1.901
O bispo sempre se move na diagonal, de modo que metade do tabuleiro fica inacessível em uma determinada jornada, mas isso significa menos movimentos potenciais a serem codificados, para que cada parte individual do genoma possa representar um movimento (o bispo nunca se retira). O bit a que se referir é decidido com base no bloco de quadrados 3x3 à frente (à direita) da amostra. A melhor jogada para uma dada situação é apenas uma mutação de distância.
Esse bot aprende rapidamente no início, mas geralmente atinge o teto antes de chegar ao final, provavelmente onde ocorre um dos dois problemas a seguir:
Código
Apesar dessas limitações, em raras ocasiões o Bispo se sai bem, com amostras individuais completando várias voltas do tabuleiro cada uma. Eu pensava que, em uma determinada volta, um espécime só pode se mover na metade do tabuleiro (equivalente apenas aos quadrados pretos ou apenas aos quadrados brancos de um tabuleiro de xadrez). No entanto, como Martin Büttner apontou, um teleportador pode mover um espécime de um quadrado preto para um quadrado branco ou vice-versa, de modo que na maioria dos painéis não haverá restrição.
(Existem dois pares de tipos de teleportadores correspondentes e cada um tem uma probabilidade de 0,5 de ter um deslocamento que move uma amostra para a outra metade dos quadrados preto e branco. Portanto, a probabilidade de uma placa ter apenas teleportadores que restringem a amostra a um metade do tabuleiro por volta é de apenas 0,25.)
As pontuações mostram que os triunfos ocasionais são intercalados por longos períodos aquém do final:
fonte
Jogador com bônus de execução: Média geométrica 50,35 (teste de 5000 jogos)
Esse bot marca quadrados pelas cores individuais, com base em uma seção de DNA de 6 bits, como o reprodutor de cores, mas com um sistema numérico diferente. Esse bot foi motivado pelo pensamento de que é bastante arbitrário que um dos bits altere o valor da pontuação em 32, enquanto outro o faz em apenas 1. Ele atribui o valor n (n + 1) / 2 a uma sequência de n 1 bits consecutivos. Além disso, ele adiciona um mecanismo de randomização na tentativa de evitar travamentos. Ele fará um avanço aleatório com uma chance de 1 em 30.
Para comparação, o jogador com pontuação de cores marcou de 30 a 35 em alguns testes de 1000 jogos. Curiosamente, a pontuação máxima do jogador em pontuação de cores estava entre 3 e 5 milhões, enquanto o máximo de bônus de corrida era de apenas 200 mil. O bônus de execução se beneficia do sistema de pontuação média logarítmica ao obter uma pontuação diferente de zero de forma mais consistente.
A execução de 5000 jogos levou cerca de 20 minutos com 6 threads no controlador C ++.
fonte
StarPlayer | C ++ Pontuação: 162 (baseado na corrida de 500 jogos)
Este jogador tenta usar A * para encontrar o melhor caminho a seguir. Ele atribui pesos da mesma maneira que o ColorScorePlayer e tenta encontrar o caminho para a borda direita da exibição. A implementação não é a mais bonita que já fiz, mas pelo menos não é muito lenta.
Pontuações da amostra:
fonte
WallGuesser - marcou 113.266 em um teste de 1000 jogos
Codificação
Eu fiz uma codificação de 6 bits / cores realmente simples. Para decodificar cores [n]
Ao espalhar os bits para uma cor em todo o genoma, estou aumentando a chance de que bits de ambos os pais sejam usados para cada cor.
Movimento
Uso uma pesquisa baseada em A * (tenho certeza que não é muito eficiente) para procurar o caminho de menor custo para qualquer um dos quadrados da borda direita. Se uma cor for mapeada para "bloqueada", nunca será inserida pela pesquisa. Se a pesquisa não conseguir encontrar um caminho, ele assume que esse rato não está apto para reproduzir e tenta finalizá-lo movendo um para a esquerda.
Reduzindo o número de ratos inaptos
Como meu genoma está efetivamente adivinhando quais quadrados são ratos teletransportadores de parede ou para trás que não têm palpites (não há cores que mapeiam para bloquear) não são muito adequados. Para tentar remover esses ratos se nenhuma cor for marcada como bloqueada, TODAS as cores serão marcadas como bloqueadas e o rato sempre moverá uma para a esquerda.
FAÇAM
Atualmente, não há aleatoriedade no comportamento, portanto é fácil para os ratos ficarem presos.
fonte
g++ -std=c++11 .\wallguesser.cpp -O2 -o .\wallguesser.exe
. Eu recebo muitos erros, mas o primeiro é.\wallguesser.cpp:47:19: error: 'dna_t' has no member named 'at' if (d.at(i) == true){
at
para[]
corrigi-lo.The FITTEST - Pontuação média geométrica: ~ 922 (2K corridas)
Minha abordagem é:
Testei mais de 2000 conjuntos de parâmetros com as mesmas 50 sementes. Os conjuntos mais promissores foram selecionados e pontuados com 250 sementes idênticas e os de maior classificação foram a entrada para a próxima rodada de teste. Então, eu consegui criar um algoritmo genético para encontrar o algoritmo genético ideal para esse problema, conforme sugerido pelo usuário mbomb007 .
O comportamento desejado:
Métodos de armazenamento de dados:
Queremos que as espécies aprendam coisas, se adaptem ao seu ambiente, se tornem as mais aptas. Inevitavelmente, isso só funciona, se as aprendizagens puderem ser armazenadas de alguma forma. O aprendizado será 'armazenado' nos 100 bits de DNA. É uma maneira estranha de armazenar, porque não podemos alterar o valor do nosso DNA. Portanto, assumimos que o DNA já armazena informações de movimentos ruins e bons. Se para uma determinada espécie a informação correta é armazenada em seu DNA, ele avança rapidamente e produz muitas novas espécies com seu DNA.
Eu descobri que a pontuação média geométrica é sensível à forma como as informações são armazenadas. Vamos supor que lemos os primeiros 4 bits dos 100 bits de DNA e queremos armazenar isso em uma variável inteira. Podemos fazer isso de várias maneiras:
dnarange
, exemplo: 4 bits1011
se tornará `1x2 ^ 3 + 0x2 ^ 2 + 1x2 ^ 1 + 1x2 ^ 0 = 15. Valores possíveis (para 4 bits): [0, 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]dnaStreakRange
função (definida abaixo), exemplo: 4bits 1011 se tornará1x1 + 0x1 + 1x1+ 1x2 = 4
. Valores possíveis (para 4 bits): [0, 1, 2, 3, 6, 10]dnaCountRange
função (definida abaixo), exemplo: 4bits 1011 se tornará1x1 + 0x1 + 1x1 + 1x1 = 3
. Valores possíveis (para 4 bits): [0, 1, 2, 3, 4]As diferenças entre os métodos de armazenamento são:
Priorize soluções.
Quando o ColorScorePlayer identifica dois movimentos avançados com pontuações idênticas, é feita uma escolha arbitrária. IMHO, você nunca deve usar a função de
v.rng.rint()
função aleatória . Em vez disso, você deve usar esta oportunidade de pontuações iguais como um gancho para avaliar soluções para efeitos de segunda ordem.O efeito de primeira ordem obtém a maior prioridade. Se forem alcançadas pontuações iguais, a solução com a prioridade 2 prevalecerá e assim por diante. Ajustando os parâmetros de uma solução, você pode influenciar a chance de pontuações iguais ocorrerem e, dessa forma, alterar o peso das soluções de prioridade 1 e prioridade 2.
Implementação do comportamento desejado
Saiba quais cores são seguras:
threshold = 63/3=21
, onde 63 é a pontuação máxima de 6 bits e 33% = 1/3 (pode ser consultado no gráfico acima).Se não houver boas jogadas disponíveis, mova-se na vertical ou para trás:
weightMove
variávelVeja o que está além:
x2
ey2
loops) qual é a melhor opção (através damainSubScore
variável). A coluna mais à direita nessa caixa 3x3 é a líder.Identifique armadilhas:
Examinei o DNA das espécies com a pontuação mais alta quando um jogo arbitrário terminou usando o armazenamento a bitsum4 (para que a pontuação da cor tenha um intervalo [0,4]):
A partir disso, pode-se concluir que os muros e teleportos obtêm uma pontuação correta. As armadilhas não são identificadas, pois dependem da direção e da cor de origem, enquanto a pontuação é feita na cor de destino. Portanto, é necessário também armazenar dados sobre a cor de origem
v(0,0)
. Em um mundo ideal, gostaríamos de armazenar informações para 16 cores x 8 direções x 3 bits = 384 bits.Infelizmente, existem apenas 100 bits disponíveis e não podemos usar tudo, pois também precisamos de memória para a solução explicada acima. Portanto, vamos fazer 4 caixas de cores:
e 4 posições de direção de movimento
Quando a pontuação decimal é 4 ou superior (100.101.110.111), presume-se que uma armadilha esteja associada a essa célula, como resultado, esse movimento não será selecionado quando surgirem pontuações iguais. Portanto, a identificação de armadilhas é um efeito de segunda ordem e o 'olhar o que está além' será uma terceira solução prioritária.
A suposição errada sobre a parede é duplicada muitas vezes nos recém-nascidos por idiotas:
Algumas espécies incorretamente assumem que as paredes são boas e tentam se mover para elas o tempo todo e, portanto, ficam presas na frente das paredes. Eles também podem ficar presos em loops infinitos de teleportadores. O efeito é o mesmo em ambos os casos.
O principal problema é que, depois de algumas centenas de iterações, alguns genes se tornam muito dominantes . Se esses são os genes "certos", você pode obter pontuações muito altas (> 1 milhão de pontos). Se eles estão errados, você está preso, pois precisa da diversidade para encontrar os genes 'certos'.
Idiotas lutando: Solução 1: inversão de cores
A primeira solução que tentei foi um esforço para utilizar uma parte da memória não utilizada que ainda é muito diversa. Vamos supor que você alocou 84 bits para a memória colorida e captura a memória de localização. Os 16 bits restantes serão muito diversos. Podemos preencher 2 variáveis decimais8 que possuem valores no intervalo [0,255] e são homogêneas, o que significa que cada valor tem uma chance de 1/256. As variáveis serão chamadas
inInverse
einReverse
.Se for
inInverse
igual a 255 (uma chance de 1/256), inverteremos a interpretação das pontuações de cores . Portanto, o muro que o idiota assume como seguro por causa de sua pontuação alta receberá uma pontuação baixa e, portanto, se tornará uma péssima jogada. A desvantagem é que isso também afetará os genes dos "direitos", portanto teremos pontuações menos altas. Além disso, essainInverse
espécie terá que se reproduzir e seus filhos também receberão partes do DNA dominante. A parte mais importante é que ela traz de volta a diversidade.Se for
inReverse
igual a 255 (uma chance de 1/256), reverteremos a ordem das posições de armazenamento das pontuações de cores . Portanto, antes da cor 0 ser armazenada nos bits 0-3. Agora a cor 15 será armazenada nessa posição. A diferença com ainInverse
abordagem é que ainReverse
vontade desfaz o trabalho realizado até agora. Estamos de volta à estaca zero. Criamos uma espécie que possui genes semelhantes aos de quando o jogo começou (exceto a armadilha que encontra memória)Através da otimização, é testado se é aconselhável usar o
inInverse
einReverse
ao mesmo tempo. Após a otimização concluída, a pontuação não aumentou. O problema é que temos uma população gen mais diversificada, mas isso também afeta o 'DNA correto'. Precisamos de outra solução.Idiotas lutando: Solução 2: código hash
A espécie tem 15 posições possíveis e, atualmente, há uma chance muito grande de que ele siga exatamente o mesmo caminho se começar na mesma posição inicial. Se ele é um idiota que ama paredes, ele ficará preso na mesma parede repetidamente. Se ele, por sorte, conseguiu alcançar um muro bem à frente, começará a dominar o conjunto de DNA com suas suposições erradas. O que precisamos é que seus filhos sigam um caminho ligeiramente diferente (já que para ele é tarde demais de qualquer maneira) e não ficarão presos na parede mais à frente, mas em uma parede mais próxima . Isso pode ser alcançado através da introdução de um código hash .
Um código de hash deve ter o objetivo de identificar e rotular exclusivamente a posição atual no quadro. O objetivo não é descobrir qual é a posição (x, y), mas responder às perguntas que meus ancestrais estiveram antes nesse local?
Vamos supor que você tenha o quadro completo à sua frente e criaria um jpg de cada quadrado de 5 por 5 células. Você terminaria com (53-5) x (15-5) = 380 imagens. Vamos dar a essas imagens números de 1 a 380. Nosso código de hash deve ser visto como um ID, com aquele diferente que não é executado de 1 a 330, mas possui IDS ausente, por exemplo, 563, 3424, 9424, 21245 etc.
Os números primos
17
e31
estão lá para impedir que a informação adicionada no início no circuito de desaparecer. Mais tarde, mais sobre como integrar nosso código de hash no restante do programa.Vamos substituir o mecanismo de sub-pontuação "veja o que está além" por outro mecanismo de sub-pontuação. Quando duas ou três células tiverem pontuações principais iguais, haverá 50% de chance da primeira ser selecionada, 50% de chance das células inferiores e 0% de chance da célula do meio. A chance não será determinada pelo gerador aleatório, mas por bits da memória , pois dessa maneira garantimos que na mesma situação seja feita a mesma escolha.
Em um mundo ideal (onde temos uma quantidade infinita de memória), calcularíamos um código de hash exclusivo para nossa situação atual, por exemplo, 25881, e iríamos para o local da memória 25881 e leríamos lá se escolhermos a célula superior ou inferior (quando houver é uma pontuação igual). Dessa forma, estaríamos exatamente na mesma situação (quando, por exemplo, viajamos pela diretoria pela segunda vez e começamos na mesma posição), tomamos as mesmas decisões. Como não temos memória infinita, aplicaremos um módulo do tamanho da memória disponível ao código hash . O código hash atual é bom no sentido de que a distribuição após a operação do módulo é homogênea.
Quando os filhos viajam na mesma prancha com DNA ligeiramente alterado, na maioria dos casos (> 99%) toma exatamente a mesma decisão. Mas, quanto mais ele chega, maior a chance de que o caminho seja diferente dos ancestrais. Portanto, a chance de ele ficar preso nessa parede à frente é pequena. Enquanto ele fica preso na mesma parede próxima de seu ancestral, é relativamente grande, mas isso não é tão ruim, pois ele não gera muitos filhos. Sem a abordagem de código hash , a chance de ficar preso na parede próxima e distante é quase a mesma
Otimização
Após a otimização, concluiu-se que a tabela de identificação de interceptação não é necessária e 2 bits por cor é suficiente. O restante da memória 100-2x16 = 68 bits é usado para armazenar o código hash. Parece que o mecanismo do código de hash é capaz de evitar traps.
Otimizei para 15 parâmetros. Este código incluiu o melhor conjunto de parâmetros ajustados (até agora):
Este é o meu primeiro programa C ++. Eu tenho como muitos de vocês agora experiência em análise de gnomos. Quero agradecer aos organizadores, pois gostei muito de trabalhar nisso.
Se você tiver algum comentário, deixe um comentário abaixo. Desculpas pelos textos longos.
fonte
Pathfinder, C ++, pontuação preliminar 35.8504 (50 rodadas)
Uma revisão completa! Portei meu algoritmo para C ++ e o aprimorei um pouco, mas a pontuação ainda não é muito alta, provavelmente porque os ratos continuam batendo com a cabeça nas paredes. Estou cansado de tentar melhorar isso, então vou deixar por enquanto.
Explicação
A idéia geral é classificar cada cor como uma armadilha ou não, depois atribuir direções para armadilhas e pesos para não armadilhas e tentar seguir o caminho de peso mínimo até a borda direita da grade de visão.
Nos primeiros 80 bits do genoma, cada cor é classificada usando 5 bits
abcde
. Seab = 01
, a cor é uma armadilha ecde
codifica sua direção (oito possibilidades). Seab ≠ 01
, a cor não é uma armadilha e seu peso éa + b + 2*(c + d + e)
.Em seguida, inicializamos uma grade 3x7, que representa o campo de visão do rato à sua direita, preenchido com cores "desconhecidas". Os bits 80-84 codificam o peso das células desconhecidas de maneira semelhante às cores não capturadas, e os bits 85-89 codificam um peso comum para os captadores. Enchemos a grade com os pesos, calculamos os caminhos mais curtos e adicionamos um peso extra (codificado nos bits 90 a 95) às células diretamente acima e abaixo do rato para desencorajar a evasão. Os bits 95-99 codificam um peso de meta. Se o peso mínimo de um caminho estiver abaixo dele, o rato provavelmente está preso em algum lugar e passa a se mover aleatoriamente (mas nunca recua). Caso contrário, segue o caminho do peso mínimo. Com uma pequena probabilidade, dependendo do peso que impede o desvio, o rato escolhe o caminho do peso do segundo ao mínimo. Isso é para evitar ficar preso às paredes (mas parece não funcionar muito bem agora).
fonte
LookAheadPlayer C ++ ≈ 89,904
Meu pensamento original era procurar 4 bits que correspondam à cor que eu estava procurando e usar os seguintes bits como pontuação. Essa acabou sendo uma péssima idéia, provavelmente por causa de mutações.
Então, pensei em maneiras de me proteger contra mutações e cruzamentos, e isso me lembrou do trabalho que fiz na decodificação de código QR. Nos códigos QR, os dados são divididos em blocos e distribuídos para evitar que erros destruam muito uma determinada parte dos dados.
Portanto, como o ColorScorePlayer, cortei o DNA em 16 pedaços e os usei como a pontuação fornecida. No entanto, as pontuações são distribuídas para que os bits individuais de cada pontuação não sejam adjacentes. Somamos então a pontuação dos movimentos atuais possíveis e dos próximos movimentos potenciais e escolho o melhor movimento a ser feito.
Nota: este foi codificado / testado no MinGW. Não seria compilado com otimizações ou com multithreading. Não tenho uma instalação real do Linux ou o Visual Studio à mão para usar um compilador onde eles funcionem. Vou testá-lo rapidamente amanhã de manhã, mas avise-me se encontrar algum problema.
fonte
SlowAndSteady C ++ (pontuação 9.7)
Não podemos confiar na interpretação de partes do genoma como números, porque um único bit-flip pode ter efeitos radicalmente diferentes, dependendo de sua posição. É por isso que eu simplesmente uso 16 segmentos de 6 bits e os pontuo no número de
1
s. Inicialmente,111111
era bom e000000
ruim, e, embora isso não importe a longo prazo (uma vez que o genoma esteja totalmente evoluído) na configuração inicial do DNA, a maioria dos segmentos possui de 2 a 4, por isso, mudei para o uso9 - (#1 - 3)^2
para pontuação. permite muito mais liberdade de movimento nas primeiras rodadas e evolução mais rápida.No momento, só olho para os 7 vizinhos mais próximos, adiciono um viés de direção à pontuação de cores e movo-me aleatoriamente em uma das direções mais altas.
Embora a pontuação em si não seja muito alta, minhas criaturas atingem a linha de chegada e pontuam> 1 em 3/4 dos casos.
E uma amostra de pontuação em 100 placas
Escore médio geométrico: 9.76557
fonte
WeightChooser | C # Pontuações: 220.8262 em 1520 Jogos
Calcula o peso para o possível próximo movimento (azul) com base no peso médio dos possíveis movimentos seguidos (amarelo)
fonte
RATOS EM AÇÃO (não uma resposta, mas uma ferramenta gráfica para bots em C ++)
Desde o início desse desafio, tive dificuldades para descobrir o que os ratos realmente estavam enfrentando na pista.
No final, cortei o controlador e escrevi uma ferramenta lateral para obter uma representação gráfica de uma faixa.
Acabei fazendo mais alguns hackers e adicionei uma visualização dos caminhos possíveis do DNA de um determinado rato.
O mapa é extremamente confuso e exige um pouco de adaptação, mas achei bastante útil entender como meus bots funcionavam.
Aqui está um exemplo:
Você provavelmente precisará aumentar o zoom para ver qualquer coisa, então aqui está apenas a primeira metade:
A princípio, vamos olhar os caminhos do rato. Há um caminho para cada local de partida possível (geralmente 15, às vezes um pouco menos). Geralmente eles tendem a se fundir, idealmente levando a um único local de vitória.
Os caminhos são representados por grandes setas retas. A cor descreve o resultado:
No exemplo, temos 12 posições iniciais vencedoras, uma levando a um loop infinito e duas a uma morte cansativa (sendo teleportada para uma armadilha, como parece).
As descontinuidades do caminho são devidas a teletransporte, que você pode seguir com as setas curvas correspondentes.
Agora, para os símbolos coloridos. Eles representam o significado das 16 cores (as cinzas representam o que um rato vê).
cores vazias são ... bem ... vazias.
Os teletransportadores têm setas de saída apontando para seu destino.
Os detectores de armadilhas também possuem setas indicando a armadilha, que é representada como um círculo vermelho.
Em um dos 9 casos, a armadilha está localizada na mesma célula que seu detector. Nesse caso, você verá o pequeno octogon no topo do círculo vermelho.
É o caso da armadilha amarela pálida neste exemplo.
Você também pode ver os detectores de armadilha malva apontando para a armadilha indicada.
Observe que às vezes o círculo vermelho de uma armadilha fica oculto sob uma parede. Ambos são letais, portanto o resultado é o mesmo em caso de teletransporte.
Observe também que uma armadilha pode estar localizada em um teleportador; nesse caso, o teleportador tem precedência (ou seja, o rato é teleportado antes de cair na armadilha, neutralizando a armadilha).
Por fim, os símbolos cinza representam o que meus ratos vêem (isto é, o significado que seu genoma atribui às cores).
Basicamente, todas as células situadas em um quadrado cinza são consideradas paredes pelo rato.
Os X grandes representam células consideradas armadilhas, com os octogons correspondentes indicando o detector que os relatou.
Neste exemplo, ambas as paredes são identificadas como tal, como é a armadilha amarela pálida (indicando de fato uma célula mortal, representando-a como uma parede está correta).
O detector de armadilha malva foi identificado como tal (fica em um octogonal cinza), mas o local da armadilha está incorreto (você pode ver que alguns círculos vermelhos não têm cruzes sob eles).
Dos 4 teleportadores, 2 são considerados paredes (turquesa e marrom) e 2 como células vazias (avermelhadas e amareladas).
Algumas células vazias são consideradas detectores de armadilhas ou paredes. Olhando atentamente, você pode ver que esses "detectores defeituosos" estão de fato proibindo a entrada em células que causariam problemas ao rato, portanto, mesmo que não correspondam às cores reais, eles têm um objetivo definido.
O código
Bem, está uma bagunça, mas funciona muito bem.
Visto pelo código do jogador, eu adicionei apenas uma interface: uma função de rastreamento usada para relatar o significado de um determinado DNA. No meu caso, usei 3 tipos (parede, detector de armadilha e vazio), mas você pode basicamente imprimir qualquer coisa relacionada à cor (ou nada, se você não quiser gráficos relacionados ao genoma).
Eu cortei o controlador para gerar uma enorme cadeia de caracteres, combinando a descrição da faixa e das cores com uma "execução a seco" do DNA do rato em todos os locais possíveis.
Isso significa que os resultados serão realmente significativos apenas se o bot não usar valores aleatórios. Caso contrário, os caminhos exibidos representarão apenas um resultado possível.
Por fim, todos esses rastreamentos são colocados em um grande arquivo de texto que é posteriormente lido por um utilitário PHP que produz a saída gráfica.
Na versão atual, tiro um instantâneo toda vez que um rato morre depois de ter atingido uma nova condição máxima (que mostra muito bem o refinamento progressivo do genoma sem exigir muitos instantâneos) e um instantâneo final no final do jogo (que mostra o DNA mais bem-sucedido).
Se alguém estiver interessado, posso publicar o código.
Claramente isso funciona apenas para bots C ++, e você precisará escrever uma função de rastreamento e possivelmente modificar o código PHP se desejar exibir alguns dados específicos do genoma (as figuras em cinza no meu caso).
Mesmo sem informações específicas do DNA, você pode ver os caminhos seguidos pelo seu DNA em um determinado mapa com muito pouco esforço.
Por que uma saída intermediária?
Antes de tudo, o C ++ não possui uma biblioteca gráfica portátil decente, principalmente quando se usa o MSVC. Mesmo que as compilações do Win32 estejam geralmente disponíveis, elas geralmente são uma reflexão tardia, e o número de bibliotecas externas, pacotes e outras gentilezas semelhantes a unix necessárias torna a escrita de um aplicativo gráfico rápido e simples uma dor terrível em uma parte do corpo que a decência impede me de nomear.
Eu considerei usar o Qt (sobre o único ambiente que torna o desenvolvimento gráfico / GUI portátil em C ++ uma tarefa simples e até agradável, IMHO - provavelmente porque adiciona um sistema de mensagens ao Objective C que C ++ carece muito e faz um trabalho incrível de limitação de memória gerenciamento ao mínimo possível), mas isso parecia um exagero para a tarefa em questão (e qualquer pessoa que quisesse usar o código teria que instalar o biggish SDK - dificilmente vale o esforço, eu acho).
Mesmo assumindo uma biblioteca portátil, não há um requisito de velocidade para falar (aproximadamente um segundo para produzir uma imagem é suficiente) e, com sua rigidez comprovada e confusão inerente, o C ++ certamente não é a melhor ferramenta para o trabalho.
Além disso, ter uma saída de texto intermediária adiciona muita flexibilidade. Quando os dados estiverem disponíveis, você poderá usá-los para outros fins (analisando o desempenho dos bots, por exemplo).
Por que PHP?
Acho a linguagem extremamente simples e adaptável, muito conveniente para a criação de protótipos. Fiz minha linguagem de estimação para desafios de código que não exigem desempenhos extremos.
É uma linguagem terrível para o golfe, no entanto, mas o golfe nunca foi minha xícara de chá.
Suponho que python ou Ruby seriam igualmente agradáveis de usar para o mesmo objetivo, mas nunca tive a oportunidade de fazer um trabalho sério com eles, e eu estava trabalhando em sites recentemente, então é o PHP.
Mesmo se você não conhece o idioma, não deve ser muito difícil modificar o código para atender às suas necessidades. Apenas não esqueça os
$
s antes das variáveis, assim como os bons e velhos dias básicos :).fonte
SkyWalker - Python - marca menos de 231 em 50 jogos
Então codifique primeiro e depois algumas explicações. Espero que nada tenha quebrado durante a cópia.
Alguma explicação
Na minha opinião, a principal diferença é que não codifico todas as cores. Em vez disso, tento salvar o número de cores importantes. Na minha opinião, essas cores são as armadilhas, paredes e teleportadores. A amostra não precisa saber a cor de uma boa célula. Portanto, meu genoma está estruturado da seguinte maneira.
Isso totaliza 52 bits usados. No entanto, uso apenas o primeiro bit dos três decisores de teleportador (verifico se o número é maior 3). Portanto, os outros 2 poderiam ser excluídos, deixando-me em 44 bits usados.
Em cada turno, verifico todos os campos da minha visão, se é uma das cores ruins (+ fora da prancha -1) e adiciono-a a uma lista de campos para os quais a amostra não deseja mudar. No caso de uma armadilha, adiciono o campo que está no deslocamento salvo para a cor da armadilha.
Com base na lista desses campos inválidos, a próxima jogada é calculada. A ordem dos campos preferenciais é:
Se dois campos de uma categoria são aplicáveis, um é escolhido aleatoriamente.
Resultados
Pensamentos
Não faço ideia se tive sorte com as 50 corridas ou se há realmente alguma sabedoria na minha estratégia.
Minhas corridas parecem nunca decolar e obter pontuações super altas, mas também tendem a encontrar pelo menos algumas vezes o objetivo
Alguma pequena aleatoriedade é boa para não ficar preso em uma armadilha em algum lugar perto do final da corrida
Eu acho que cores não especiais nunca são ruins. No entanto, instâncias delas podem ser ruins quando estão no deslocamento de uma armadilha. Assim, rotular uma cor como ruim se não for armadilha, parede ou mau teleportador não faz sentido.
Paredes são os maiores inimigos
Melhorias
Primeiro, mesmo que eu sinta falta de ver os quadrados pretos se aproximando cada vez mais do objetivo, é necessária uma porta C ++ para realizar mais testes e obter um resultado mais significativo.
Um dos principais problemas é que, se houver células ruins (ou aquelas que o espécime pensa mal) na frente do rato, ele facilmente começa a subir e descer em círculos. Isso pode ser interrompido ou reduzido, observando-se 2 movimentos à frente nesses casos e impedindo que ele se mova para um campo em que apenas volte novamente.
Muitas vezes, leva algum tempo até que um rato com bons genes chegue ao objetivo e comece a espalhá-lo. Talvez eu precise de alguma estratégia para aumentar a diversidade nesses casos.
Como os teletransportadores são difíceis de calcular, talvez eu deva dividir a população entre aqueles que são arriscados e sempre levar bons teletransportadores e aqueles que estão mais preocupados e levá-los apenas se não houver outra escolha.
Eu deveria usar a segunda metade do meu genoma de alguma forma.
fonte
self.bit_chunk(16, 4)
eself.bit_chunk(20, 4)
tiver o valor,0010
você efetivamente armazenou apenas informações sobre uma das duas armadilhas.itervalues
paravalues
.Python, NeighborsOfNeighbors, Score = 259.84395 em mais de 100 jogos
Esta é uma variação do ColorScorePlayer. A cada 6 bits armazena um índice de qualidade para um quadrado. Quando o bot está fazendo um movimento, ele marca cada um dos três quadrados à frente - diagonal para cima, para frente e diagonal para baixo. A pontuação é a qualidade do quadrado mais a metade da qualidade média dos próximos 3 quadrados. Isso dá ao bot um olhar para o futuro, sem prejudicar a qualidade do primeiro quadrado. O algoritmo é semelhante ao LookAheadPlayer, que eu não vi antes de escrever esta solução.
fonte
else None
paraelse 0
na linha anterior para calcular sua pontuação. Espero que isso mantenha sua lógica inalterada (não fiz alterações no seu código aqui no SE, além de adicionar o recuo perdido).ROUS (roedor de tamanho incomum), Java, pontuação = 0
Isso faz com que o ambiente decida para onde ir.
Devido ao controlador Java não funcionar, não tenho pontuações para isso. Isso só vai muito longe se encontrar alguns teleportadores para ajudá-lo.Isso tende a se extinguir e travar o controlador de vez em quando. Provavelmente, isso se deve ao fato de seu ambiente natural ser o Pântano de Fogo.fonte
Cabeça de impressão de cor cinza (C ++, ~ 1.35)
Este não está indo muito bem, em média, mas em raras ocasiões, ele apresenta um desempenho brilhante. Infelizmente, estamos sendo pontuados em média geométrica (1,35), e não na pontuação máxima (20077).
Esse algoritmo funciona usando apenas códigos de cinza de 4 bits para mapear a pontuação de cada cor em algum lugar de -2 a 2 (com um viés no intervalo [-1..1]) e calcula a pontuação do bloco de cada movimento e seus próximos movimentos . Ele também usa um código cinza de 2 bits para determinar o multiplicador para o bloco em si, bem como o fator de polarização para mover para a direita. (Os códigos cinza são muito menos suscetíveis a grandes saltos devido a mutações, embora eles realmente não façam nenhum favor para o crossover no meio do codepoint ...)
Também não faz absolutamente nada para tentar lidar com as armadilhas especialmente, e eu suspeito que possa ser a queda (embora eu não tenha adicionado nenhuma instrumentação ao controlador para testar essa teoria).
Para cada movimento possível, ele determina uma pontuação e, dentre todos os movimentos com a pontuação mais alta, escolhe aleatoriamente.
Na minha corrida mais recente, obtive pontuações: 1 1 1 1 1 1 1 46 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 20077 1 1 1 2 1 1 1 1 1
Eu gostaria de poder obter mais dos 20077s e menos dos 1s. :)
fonte
C ++, TripleScore, Pontuação: 100 ~ 400
Antes de tudo, minha pontuação varia muito em várias execuções (principalmente por causa do número de 1s).
O núcleo calcula a pontuação de 5 direções: cima, baixo, frente para cima, frente e frente para baixo. Primeiro, a pontuação de subir e descer é calculada, e os resultados são comparados ao valor de permanecer no local. Se permanecer no lugar é melhor do que subir ou descer, essas direções não serão escolhidas (portanto, elas devem seguir em frente). Isso evita quedas (cima, baixo, cima, baixo, ...) entre dois pontos.
Agora as três outras direções são pontuadas: para frente, para frente e para baixo. De todas as direções investigadas, as com maior pontuação são mantidas e 1 delas é escolhida aleatoriamente.
Pontuando uma direção: o TripleScore calcula a pontuação de um movimento usando 3 subescores:
Como em outras respostas, a pontuação depende muito do número de 1 pontuação retornada.
fonte
Ruby - ProbabilisticScorePlayer
Este rato altamente não determinístico calcula a probabilidade de ir a um espaço por sua vizinhança. Os 16 primeiros slots no genoma representam as 16 cores. 1 em um slot significa que a cor é boa para pisar, 0 significa ruim. Os próximos 16 são iguais para o espaço à frente do seu alvo, e assim por diante.
A principal vantagem da abordagem probabilística é que é quase impossível ficar preso atrás de uma parede por muito tempo. A desvantagem é que você quase nunca terá um rato quase perfeito.
fonte
c
um valor inicial? Não parece ser definido quando você o usa no primeiroif
.coords
não é uma lista, você usa, em&&
vez de,and
e esqueceu os parênteses, e mesmo depois de corrigir tudo isso, você não está delimitando os valores de RNG para obter uma direção vazia. Esse pseudocódigo ou algo para ser executado com algum tipo de dialeto Ruby?Java, RunningStar, Score = 1817.050970291959 em mais de 1000 jogos
Este bot usa o código de cores do Run-Bonus com a técnica do StarPlayer .
Atualização: Corrigido o controlador java.
fonte
LeapForward, Python 2
Não é particularmente inovador, mas é a minha única tentativa que foi bem-sucedida.
Basicamente, ele codifica quatro cores (cada 4 bits) para evitar no genoma. Em seguida, ele avança para uma cor que não está nessa lista. Se todas as cores são ruins, ainda pula para o desconhecido.
fonte
Java - IAmARobotPlayer - Pontuação 3.7
Acabei de criar este rato robô para comparação com outro programa (não muito interessante até agora) que fiz. Não obtém uma boa classificação geral, mas se obtiver pontuação em algum lugar, obterá muitos ratos. A idéia é que ele olhe apenas para as três células à sua frente, cada célula é boa ou ruim. Isso fornece um número binário. Então, ele procurará esse número em seu genoma, pegará os três bits consecutivos, também os converterá em um número e executará a ação que está armazenada sob esse número. Por isso, age sempre da mesma maneira quando encontra a mesma situação.
Resultado:
fonte
Amostras Cautelosas - C ++ - pontuam cerca de 2030 em mais de 200 execuções
Isso usa a parte colorida (16x4 bits) do DNA que codifica a Blind Faith, mas deixa o restante (36 bits) do DNA totalmente sem uso.
A codificação para uma cor é:
Onde X indica bits não utilizados. Dado que apenas 2 de 16 cores são armadilhas que usarão todos os 4 bits (e somente se a armadilha estiver deslocada, o que acontecerá 8 a 9 vezes), normalmente haverá 64 bits não utilizados - a teoria é que mutações que afetam qualquer um desses bits não utilizados não destruirão o genoma e a estabilidade é melhor do que qualquer solução sofisticada que possa usar esses bits restantes.
As amostras então usam isso para planejar uma rota segura dentro de uma grade 7x7 centrada em si mesmas (o 5x5 que sua visão permite mais 1 quadrado de cada lado para permitir armadilhas de deslocamento) priorizando o deslocamento da maior distância para a frente depois de 3 movimentos.
Inicialmente, comecei a construir algumas verificações para garantir que o fato de a cor em que a amostra está atualmente não seja letal corresponda ao genoma e sinalize quaisquer cores erradas como quadrados de segurança insegura (e seus quadrados adjacentes) - no entanto, acrescentou significante complicação por pouco ou nenhum ganho em comparação com a marcação desses quadrados como SEGURA e matando algumas amostras adicionais. Voltarei a isso se tiver tempo.
Amostras de pontuação:
Pontuação máxima durante o teste: 8.150.817 amostras salvas.
fonte