Lab Rat Race: um exercício em algoritmos genéticos


113

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!

Imagem de exemplo da placa em uso

† 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 -1para 15. -1representa 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 0para 15, cujo significado mudará de jogo para jogo. Além disso, -1representa 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:

  1. 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.
    1. Se a idade da amostra for 100 , ela morre. Caso contrário, aumente sua idade em 1.
    2. 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.
    3. Se a célula de destino for uma parede, a movimentação será alterada para (0,0).
    4. Se a célula alvo for um teletransportador, a amostra é movida pelo deslocamento do teletransportador. Nota: Esta etapa é executada uma vez , não iterativamente.
    5. 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.
    6. 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.
  2. Se houver menos de duas amostras no tabuleiro, o jogo termina.
  3. 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.mdinstruçõ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 pygameou tkinter.

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.


3
Alguém não poderia simplesmente criar um algoritmo genético genético para encontrar o algoritmo genético ideal para esse problema?
mbomb007

11
@ anon3202 Bem, isso obviamente forneceria mais informações sobre o layout da pista, já que você pode avaliar onde está. Essencialmente, queríamos manter a interface dos bots simples e torná-la um problema puramente local, onde você precisará do genoma para descobrir qual solução local é mais benéfica para o seu progresso global.
Martin Ender

11
@matovitch Veja a seção 5 da seção Ordem dos turnos dos Detalhes Gory (especificações completas):'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.'
trichoplax

11
Ajustei o código C ++ para mostrar a média da amostra, stddev, stderr e intervalo de confiança de 99% (antes do seu log / exp "geométrico") e fiz uma descoberta surpreendente. A resposta "Fé cega" teve "Média da amostra de 116529 + - 2,78337e + 010 (99%) stddev = 7,77951e + 010" após 50 execuções. "A queda do intervalo de confiança para 50% não melhora notavelmente as coisas. A média geométrica foi mais estável: "Média de 159,458 + - 117262 (99%) stddev = 32,6237" (Antes de sua atualização de 800 notas)
Mooing Duck

11
Fiz um experimento com a taxa de mutação e acho que o desafio seria mais interessante (e os controladores seriam muito mais rápidos) se a probabilidade fosse aumentada de 0,01 para 0,0227, o que dá ao DNA apenas 10% de chances de passar mutação inalterada em vez de 37% com o valor atual. Isso evita explosões populacionais ridículas (que, por sua vez, economizam muito tempo de computação) e evitam muitas falhas devido à diversidade insuficiente. As pontuações individuais são mais baixas, mas, como mais corridas produzem vencedores, a média global tende a aumentar.

Respostas:


37

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

#include "./gamelogic.cpp"

#define NUM_COLORS 16

// color meanings for our rats
typedef enum { good, bad, trap } colorType_t;
struct colorInfo_t {
    colorType_t type;
    coord_t offset; // trap relative location
    colorInfo_t() : type(good) {} // our rats are born optimists
};

// all 8 possible neighbours, carefully ordered
coord_t moves_up  [] = { { 1, 0 }, { 1,  1 }, { 1, -1 }, { 0,  1 }, { 0, -1 }, { -1, 0 }, { -1,  1 }, { -1, -1 } };  // toward the goal, going up   first
coord_t moves_down[] = { { 1, 0 }, { 1, -1 }, { 1,  1 }, { 0, -1 }, { 0,  1 }, { -1, 0 }, { -1, -1 }, { -1,  1 } };  // toward the goal, going down first

// map of the surroundings
struct map_t {
    static const size_t size = 5;
    static const int max = size / 2;
    static const int min = -max;
    colorType_t map[size*size];
    colorType_t & operator() (int x, int y) { return map[(x + max)*size + y + max]; }
    colorType_t & operator() (coord_t pos) { return operator()(pos.x, pos.y); }
    bool is_inside(int x, int y) { return abs(x) <= max && abs(y) <= max; }
    bool is_inside(coord_t pos) { return is_inside(pos.x,pos.y); }
};

// trap mapping info
struct trap_t {
    coord_t detector;
    colorInfo_t color;
    trap_t(int x, int y, colorInfo_t & color) : color(color) { detector.x = x; detector.y = y; }
    trap_t() {}
};

coord_t blindFaith(dna_t d, view_t v)
{
    colorInfo_t color[NUM_COLORS]; // color informations

    // decode colors
    for (size_t c = 0; c != 16; c++)
    {
        size_t base = c * 4;
        if (d[base])
        {
            color[c].type = d[base+1] ? good : bad;
        }
        else // decode trap location
        {
            color[c].type = trap;
            int offset = d[base+1] + 2 * d[base+2] + 4 * d[base+3];
            color[c].offset = moves_up[offset]; // the order is irrelevant as long as all 8 neighbours are listed
        }
    }

    // build a map of the surrounding cells
    map_t map;
    unsigned signature = 0;
    int i = 0;
    for (int x = map.min; x <= map.max; x++)
    for (int y = map.min; y <= map.max; y++)
    {
        int c = v(x, y);
        map(x, y) = (c == -1) ? bad : color[c].type;
        if (c != -1) signature ^= v(x, y) << ((i++) % 28);
    }

    // map traps
    for (int x = map.min; x <= map.max; x++)
    for (int y = map.min; y <= map.max; y++)
    {
        if (map(x, y) != trap) continue;
        const colorInfo_t & trap = color[v(x, y)];
        int bad_x = x + trap.offset.x;
        int bad_y = y + trap.offset.y;
        if (!map.is_inside(bad_x, bad_y)) continue;
        map(bad_x, bad_y) = bad;
        map(x, y) = good;
    }

    // pick a vertical direction according to surroundings signature
    int go_up = d[64 + signature % (DNA_BITS - 64)];

    // try to move to a good cell nearer the goal
    for (const coord_t &move : go_up ? moves_up : moves_down) if (map(move.x, move.y) == good) return move;

    // try not to increase fitness of this intellectually impaired specimen
    return{ -1, 0 };
}

int main() {
    time_t start = time(NULL);
    double score = runsimulation(blindFaith);
    slog << "Geometric mean score: " << score << " in " << time(NULL) - start << " seconds";
}

Resultados da amostra:

Scores: 15 4113306 190703 1 1 44629 118172 43594 63023 2 4 1 1 205027 1 455951 4194047 1 5 279 1 3863570 616483 17797 42584 1 37442 1 37 1 432545 5 94335 1 1 187036 1 4233379 1561445 1 1 1 1 35246 1 150154 1 1 1 1 90141 6 1 1 1 26849 1 161903 4 123972 1 55 988 7042063 694 4711342 90514 3726251 2 1 383389 1 593029 12088 1 149779 69144 21218 290963 17829 1072904 368771 84 872958 30456 133784 4843896 1 2 37 381780 14 540066 3046713 12 5 1 92181 5174 1 156292 13 1 1 29940 66678 125975 52714 1 5 3 1 101267 69003 1 1 10231 143110 282328 4 71750 324545 25 1 22 102414 1 3884626 4 28202 64057 1 1 1 1 70707 4078970 1623071 5047 1 1 549040 1 1 66 3520283 1 6035495 1 79773 1 1 1 218408 1 1 15 33 589875 310455 112274 1 1 4 1 3716220 14 180123 1 2 12785 113116 12 2 1 59286 822912 2244520 1840950 147151 1255115 1 49 2 182262 109717 2 9 1049697 59297 1 11 64568 1 57093 52588 63990 331081 54110 1 1 1537 3 38043 1514692 360087 1 260395 19557 3583536 1 4 152302 2636569 12 1 105991 374793 14 3934727 1 2 182614 1 1675472 121949 11 5 283271 207686 175468 1 1 173240 1 138778 1 1 59964 3290382 1 4 1757946 1 23520 1 2 94 1 124577 497071 1749760 39238 1 301144 3 1 2871836 1 1 10486 1 11 8 1 111421 11 1807900 1 587479 1 42725 116006 3 1 6 5441895 1 1 22 52465 952 1 18 1 1 46878 2 1 1 1994 4 593858 123513 4692516 820868 4247357 1 1 2 1 2 8770 2 1 95371 4897243 2 22741 1 1 1 1 325142 6 33650 4 51 102993 1 182664 1 4040608 18153 2045673 462339 1 1 617575 2 2551800 3 7760 1 108012 76167 143362 1148457 1 53460 1 71503 1 1 1 1 81482 3208 62286 69 139 1 3503941 1 253624 101903 3081954 80123 84701 9 16 1 1070688 71604 613064 2076 15009 9 1 1 1 199731 1 2 1 63132 1 1843855 27808 1 3569689 273144 1 460524 2703719 22443 10876 51242 1 6972678 4591939 1 140506 43981 45076 2 1 91301 5 1 1874615 1758284 608 13 1 96545 75161 1 618144 4 2056133 1 1 2 57401 1394307 6 188116 83545 1 41883 1 1 467189 371722 1 1122993 1 17912 159499 1 5 3355398 33 1 2 246304 1 2 168349 1 50292 12 141492 2723076 3 1 6 3060433 223360 171472 106409 1 2 1 102729 8814 1 285154 1 11 1 65 930 2 689644 3271116 1 5 4 60 77447 1 1 1477538 256023 100403 2480335 1 39888 1 1 70052 66090 1 250 1 2 8 115371 1523106 1424 168148 1 1 1 42938 17 1 364285 185080 1 1 36 4903764 13 51987 1106 276212 67460 1 251257 2 6867732 1 1 1890073 1 1 8 5 2118932 210 0 3792346 5209168 1 1 1 1 51 1 4621148 1 37 337073 3506096 1 1 1 1 458964 2 16 52930 1 15375 267685 1 1 1259646 14930 3248678 527105 1 103 24 1 3252685 6009 1 1 176340 3971529 121 1722808 1 31483 194232 2314706 95952 3625407 3 216755 56 1 8 1 1 1 1 885 229 9056 172027 31516 2526805 1 76076 1589061 1 1 8 90812 1 21 72036 1681271 2 212431 1581814 85993 79967 4 7 514708 1070070 1 71698 1 23478 15882 94453 1 27382 495493 277308 12127 91928 248593 1 1 1 26540 1709344 2119856 1 1 48867 107017 251374 64041 15924 15 87474 8 1 23 9 48 1 1 1 51793 2 61029 84803 15 689851 1 1 873503 10 140084 420034 87087 82223 1 163273 12 1 5 570463 19 26665 1 170311 1 39983 1 475306 1 2 36417 746105 11 141345 1 3 1 30 3 1 1 1 1 1312289 408117 1 42210 273871 561592 1 1 1 1 4448568 48448 7 378508 1 351858 278331 1 79515 1169309 3670107 14711 4686395 1156554 33 2528441 24537 76 335390 63545 122108 76675 21929 34 1 861361 83000 417781 1 90487 1 1 85116 7 2 1 60129 647991 79 1 2755780 726845 244217 50007 187212 1 3674051 286071 44068 3 307427 26973 1 26059 1957457 230783 58102 545318 1 4 172542 168365 1 89402 1 4 1 1 1 1 2 3 16 62935 5643183 117961 109942 85762 5 117376 118883 1 61 23893 122536 70185 1 64252 208409 179269 55381 1579240 3434491 1 4964284 3356245 3 21 2197119 346542 44340 59976 772220 5590844 199721 90858 63785 125989 57219 129737 81836 1 3671 16810 1 4151040 1 15 40108 1 443679 3224921 2 27498 2 3 146529 169409 19 1 1 1 1 41627 1 3 2722438 1 2013730 1 1649406 1 1 6943 125772 58652 1 1 1 2413522 1 2 48 36067 253807 2 146464 1 248 07 3359223 139896 395985 65241 43988 594638 69033 275085 1 17973 1 1 1 594835 1 1 4468341 3496274 222854 94769 55 161056 36185 8793 277592 3 1 6746 1 138151 66 37365 1 2729315 1 3 57091 22408 249875 246514 85058 1 20 5463152 1 3 1 45293 1 70488 2792458 461 441 951926 2236205 2 171980 1 1 48 3893009 1 458077 1 268203 1 70005 7 19299 1 278978 1 45286 26 2 1883506 274393 342679 1 1 913722 911600 12688 1 1 115020 1249307 1529878 53426 1 226862 3721440 23537 86033 397433 1 1 1 161423 96343 94496 1 1 1 2 1 111576 1 4039782 1 1 1 5742393 3569 46072 1 1 2 1 1 85335 219988 1 78871 115876 43405 1 300835 1 166684 53134 1 3 111487 6 3 3 77 1 115971 3 205782 10 1932578 356857 43258 47998 1 27648 127096 573939 32650 523906 45193 1 2 128992 1 10144 1 257941 1 19841 5077836 14670 5 3 6 1 1 21 14651 2906084 37942 45032 9 304192 3035905 6214026 2 177952 1 51338 1 65594 46426 553875 2676479 245774 95881 3 216364 3064811 1198509 223982 3 6 1 533254 1 590363 264940 68346 127284 1 7 1 1 4617874 5 45400 1 1 3097950 360274 1 3 1 8421 14 469681 418563 3 1 6 1 1 575766 405239 11 2631108 152667 1 1 1 467383 1 1 775499 1 157998 2 1 143351 92021 1 1 1173046 3636579 1 70635 162303 1 1534876 834682 2 1 1 11981 346908 245124 607794 17 1570641 126995 13 57050 1 2 33731 29739 1 1 35460 1 33716 168190 214704 1 443156 701674 2636870 108081 1604895 1 1 11 115901 23 571891 360680 1 1 35 1 2036975 1 1 2555536 4742615 5 360553 287044 1 1814255 7 59632 1 216 41546 1 540920 353424 2625301 223744 1 1 1 15717 3 429871 1 4 2329632 18 11 1 2 4 1 3905 5 1 1 1 2 5431442 1 859628 1 3 338378 15236 13764 1 3384362 1 15 65293 24 619599 152620 2 189921 35854 16647 7 2 404790 360096 1 2 189459 1097768 191610 1 1 470254 1 12 2 330299 364219 2365542 312023 2273374 2 10527 1 115453 1 2 3845592 52388 913449 1 14695 1 44 37352 90302 1 1 1 233577 51639 3474983 44010 1650727 31 2 2 1 8 7 1 3 5 25603 17799 45630 758457 1 4571839 37 4 3 2 1 1 1351271 196673 12 2880765 263886 2926173 1 2 1 241502 5 6 1 278576 9 7 290722 42749 143391 82753 21771 57887 1 1 60400 1766903 1 296392 1 5 2861787 125560 1 9 199218 1 1 308226 517598 2246753 12 1168981 3 98447 1 488613 9 842865 202108 10 1 238493 1 1523706 5383982 29435 1 1 207071 1 8 4 125742 70531 253135 72207 124291 23364 184376 2 40034 9569353 194109 102854 2 3247153 58313 85995 1 598 63 1 2676692 10 3573233 1 36651 118016 2486962 65456 46760 1 5813 723 178120 2 153305 1 1 2 1 2354413 3 1 17126 132953 437123 299778 3070490 1 6490 403704 2261 511439 1 39 33410 173045 1 1 120970 641346 132042 1 44906 1 33940 132124 467702 45472 9 44 1 1 1 107008 1 46635 1 121431 130760 1 7 3 1 56251 1299306 3 1 1 1 15 2147678 215169 1374943 1 332995 231089 269310 1 7816944 1 1 1 46 134426 1 1 1 2 76112 1 1 30438 299927 25 139373 76048 278757 71 3474997 1 294046 1 3126554 2518019 2 1 6 1 3054393 1 1 1 2 525 96 419528 1 1 154718 233 207879 26 1 6 57436 3 5944942 1 1 318198 147536 1 22 420557 1 1 120938 1 1 167412 4082969 73299 1 11 3557361 1 4 330028 269051 1 2569546 2 1 1 4 1 1 377412 1 1 1 213800 58131 1422177 54 109617 117751 12432 3830664 419046 3 6821 741 919 1 22335 1 1 15069 80694 488809 2389 2308679 145548 51411 115786 110984 107713 1 12 6 1 5 8365 1 2001874 210250 4674015 14 1 1204101 314354 89066 1 1 2438200 68350 1 1575329 5593838 2743787 151670 57 16 5948210 597158 128060 189160 23628 1 1 15 4171774 1 8206 4157492 1 2 315607 1618680 24736 18520 4787225 33842 134431 1 1 1 1 1 1115809 17759 1 33016 123117 1 77322 169633 219091 1 321593 57231 135536 175401 4 1 435702 1 253132 100707 114547 1 119324 6382967 1472898 3 72567 1707408 177958 26 208719 1 27083 74 12 576410 19375 177069 4 3 1 31 507048 2 1 1 2 1 2 1 40 7 99892 95202 60649 241396 232370 1 136579 70649 1 2877 280695 13603 102860 404583 29717 112769 1 54089 1 97579 40819 2 868629 64848 2 63432 5 1 1888426 99623 2 1 7911 53646 3047637 1 2 3 152910 1 3244662 105187 1 1 1 1 8966 200347 1 1 22 302654 6 17 1 10 328150 55259 1016 117291 2 1 224524 23846 74645 1 1 1 1 1 3117394 10847 33976 144613 4 201584 1 1 26959 3 4410588 27019 6 66749 55935 23 4126812 4089989 99959 1 1 1 1 55490 1 4275599 13652 33967 2 8126062 337093 320653 128015 4 1 7729132 1 10594 116651 20990 3046630 1 353731 132989 2066431 4 80 15575 147430 1 621461 3100943 2306122 5 33439 407945 25634 1 2911806 32511 2174235 298281 15159 54125 1 2 3063577 2205013 1 407984 1 319713 1 22171 1 2763843 1 2607606 1 100015 3096036 1 55905 1 1 635265 2890760 1 1 1 1 35854 1 352022 2652014 1 2 274366 1 4 1 602980 4 83828 602270 2816 2 59116 25340 1 11 1 5162051 34 8 218372 1186732 142966 1 1 170557 503302 1 84924 5 1 1350329 1 1 1 130273 78055 902762 1 8581 5 1 3635882 1 1 1 224255 44044 61250 2 438453 8 1 2729357 28 1 17658 82640 1 31809 10 1 33 1 1 45495 5798 5000217 40018 588787 67269 1 12 83512 2798339 1 609271 1 3 1 7 67912 189808 3388775 60961 81311 1167 24939 433791 405306 85934 1 1170651 2 1 66 552579 122985 515363 2188340 1 1 1 3807012 1502582 4 13 149593 1 1 2108196 3 34279 24613 1282047 27 1 2 1 1 584435 27487 1 1 5 33278 1 1 1202843 1 1 1 6 3649820 3100 2 266150 13 164117 10 53163 3295075 1 1 1 1 77890 1 286220 90823 18866 3139039 481826 1 3994676 23 116901 132290 6 3927 84948 1 1 1 1 256310 1 11 8 1 102002 8392 887732 98483 444991 1 1 49408 409967 1158979 1 1 1 81469 189764 3960930 296231 64258 1 1 176030 4 1 2 1 486856 1 1135146 31 2 13112 227077 31
Geometric mean score: 831.185 in 14820 seconds

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:

  1. bom -> o rato pensa que pode ir com segurança para lá
  2. ruim -> o rato não vai lá
  3. armadilha -> o rato considerará a posição da armadilha ruim e a célula indicando a armadilha boa .
    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, 11e 10), mas as armadilhas requerem 4 bits ( 0tttonde tttrepresenta 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 11xxe mau quanto 10xx), 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:

  • decodificar cada cor de acordo com o genoma (ou relatório direto do controlador para células "ruins" fora da faixa)
  • construir um mapa das redondezas imediatas (3x3 células, 8 possíveis vizinhos)
  • calcular uma assinatura do ambiente (um hash das 25 cores, exceto células fora da pista)
  • escolha uma direção vertical preferida da assinatura (entre 36 baldes de hash)
  • tente se mover para um vizinho sugerido como "bom", começando pelos que estão mais próximos da meta e indo primeiro na direção vertical preferida
  • se nenhum vizinho "bom" puder ser encontrado, tente mover uma célula para trás (possivelmente sendo vítima de um acidente infeliz e evitando aumentar a forma física)

Vós roedores, contemplai os inimigos de sua espécie

a parede temida laço de teletransporte

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:

Turns:2499 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^v^^^v^vv^^v^^^ Max fitness: 790 Specimens: 1217 Score: 2800
Turns:4999 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^v^^^v^vv^^v^^^ Max fitness: 5217 Specimens: 15857 Score: 685986
Turns:7499 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^vvvvv^^v^v^^^^ Max fitness: 9785 Specimens: 31053 Score: 2695045
Turns:9999 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^vvvvv^^v^v^^^^ Max fitness: 14377 Specimens: 46384 Score: 6033904
Scored 6035495 in game 146 current mean 466.875

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.

Turns:2499 best rat B  T0 G  B  T7 B  G  B  T6 T0 T3 B  G  G  G  T4 ^v^^^^^v^^v^v^^^^^^^^v^v^v^^vvv^v^^^ Max fitness: 18 Specimens: 772 Score: 1
Turns:4999 best rat T7 G  G  G  G  T7 G  B  T6 T0 T3 T5 G  G  B  T4 ^vvvvvvv^^^vvv^^v^v^^^^^^^^^^^^^v^^^ Max fitness: 26 Specimens: 856 Score: 1
Turns:7499 best rat G  T0 G  T3 G  T0 G  B  T6 T0 T2 B  T4 G  B  T4 ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^ Max fitness: 55 Specimens: 836 Score: 5
Turns:9999 best rat T6 T0 G  T5 B  T1 G  B  T6 T0 T3 B  T4 G  B  T4 ^^vv^^^^vv^^v^v^^v^^vvv^vv^vvv^^v^^v Max fitness: 590 Specimens: 1223 Score: 10478
Scored 10486 in game 258 current mean 628.564

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:

  • eles têm um significado independente
    (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.
  • eles são relativamente resistentes a mutações,
    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.
  • eles são pequenos (4 bits), portanto a probabilidade de ser destruído por um crossover é muito baixa.
  • mutações produzem alterações inofensivas ou significativas
    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.


16
Obteve uma pontuação de 208,14 em 10.000 jogos. Eu estava tentando testá-lo para 1000, mas nunca percebi que havia digitado um 0 extra, por isso demorou mais de 7 horas.
precisa saber é

LOL obrigado tudo a mesma coisa. Comparando com minhas duas mil corridas, parece que cerca de 2000 corridas podem produzir um resultado estável.

O que ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^significa? O resto eu posso adivinhar, mas estou tendo problemas com isso?
Mooing Duck

Eu estava pensando em fazer um controlador "debug" separado, que executa um rato por vez, e cada vez que um novo rato é gerado, ele mostra o DNA dos pais e da criança (através de alguma função personalizável). Isso tornaria muito mais fácil examinar como o rato está funcionando.
Mooing Duck

2
que representa os 36 bits indicadores "para cima / para baixo", mas nesses exemplos o DNA vencedor já se tornou dominante para que eles não variem muito.

18

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)

#ifndef NDEBUG
#define NDEBUG
#include "./gamelogic.cpp"
#endif // NDEBUG
#include <cassert>

#define NUM_COLORS 16
#define BITS_OFFSET  3
#define BITS_TYPE    2
#define BITS_SUBTYPE 2
#define BITS_COLOR (BITS_TYPE+BITS_OFFSET)

// how our rats see the world
typedef unsigned char enumSupport_t;
typedef unsigned char trapOffset_t;
typedef enum : enumSupport_t {
    danger,   // code      trap detector
    beam,     // code      safe teleporter
    empty,    // code      empty
    block,    // code      wall, pit or teleporter
    trap,     // computed  detected trap
    pit,      // read      off-board cell
} colorType_t;

// color type encoding (4 first bits of a color gene)
// the order is critical. A single block/empty inversion can cost 2000 points or more
const colorType_t type_decoder[16] = {
    /*00xx-*/
    danger,
    empty,
    beam,
    block,
    /*01xx-*/
    beam,
    danger,
    empty,
    block,
    /*10xx-*/
    empty,
    beam,
    block,
    danger,
    /*11xx-*/
    block,
    empty,
    danger,
    beam,
};

// all 8 possible neighbours, carefully ordered
typedef coord_t neighborhood_t[8];
neighborhood_t moves_up =   { { 1, 0 }, { 1,  1 }, { 1, -1 }, { 0,  1 }, { 0, -1 }, { -1, 0 }, { -1,  1 }, { -1, -1 } };  // toward the goal, going up   first
neighborhood_t moves_down = { { 1, 0 }, { 1, -1 }, { 1,  1 }, { 0, -1 }, { 0,  1 }, { -1, 0 }, { -1, -1 }, { -1,  1 } };  // toward the goal, going down first

// using C++ as a macro-assembler to speedup DNA reading
/*
Would work like a charm *if* a well-paid scatterbrain at Microsoft had not defined
std::bitset::operator[] as

bool operator[](size_t _Pos) const
{   // subscript nonmutable sequence
return (test(_Pos));
}

Bounds checking on operator[] violates the spec and defeats the optimization.
Not only does it an unwanted check; it also prevents inlining and thus generates
two levels of function calls where none are necessary.
The fix is trivial, but how long will it take for Microsoft to implement it, if
the bug ever makes it through their thick layer of tech support bullshit artists?
Just one of the many reasons why STL appears not to live up to the dreams of
Mr Stroustrup & friends...
*/
template<size_t BITS> int DNA_read(dna_t dna, size_t base)
{
    const size_t offset = BITS - 1;
    return (dna[base + offset] << offset) | DNA_read<offset>(dna, base);
}
template<> int DNA_read<0>(dna_t, size_t) { return 0; }

// color gene
struct colorGene_t {
    colorType_t  type;
    trapOffset_t offset;  // trap relative location
    colorGene_t() : type(empty) {} // our rats are born optimists
};

// decoded DNA
class dnaInfo_t {
private:
    const dna_t & dna;
    static const size_t
        direction_start = NUM_COLORS*(BITS_TYPE + BITS_OFFSET),
        direction_size = DNA_BITS - direction_start;

public:
    colorGene_t color[NUM_COLORS];
    int         up_down; // anti-wall-banger

    // decode constant informations during construction
    dnaInfo_t(const dna_t & d) : dna(d)
    {
        for (size_t c = 0; c != NUM_COLORS; c++)
        {
            unsigned raw = DNA_read<BITS_COLOR>(d, c * BITS_COLOR);
            color[c].type = type_decoder[raw >> 1];
            if      (color[c].type == danger) color[c].offset = raw & 7;
            else if (color[c].type == beam  ) color[c].offset = raw & 3;
        }
    }

    // update with surroundings signatures
    void update(size_t signature)
    {
        // anti-blocker
        up_down = (direction_size > 0) ? dna[direction_start + signature % direction_size] : 0;
    }
};

// map of the surroundings
class map_t {
    struct cell_t {
        coord_t pos;
        int     color;
    };

    static const size_t size = 5;
    static const int max = size / 2;
    static const int min = -max;

    size_t local_signature[size*size]; // 8 neighbours signatures for teleporters
    cell_t track_cell[size*size]; // on-track cells
    size_t cell_num;
    colorType_t map[size*size];
    size_t raw_index(int x, int y) { size_t res = x * size + y + max + max * size; assert(res < size*size); return res; }
    size_t raw_index(coord_t pos) { return raw_index(pos.x, pos.y); }

    bool is_inside(int x, int y) { return abs(x) <= max && abs(y) <= max; }

public:
    size_t compute_signatures(view_t v, dnaInfo_t genome)
    {
        cell_num = 0;
        size_t signature = 0;
        memset (local_signature, 0, sizeof(local_signature));
        int i = 0;
        for (int x = min; x <= max; x++)
        for (int y = min; y <= max; y++)
        {
            int c = v(x, y);
            if (c == -1)
            {
                (*this)(x, y) = pit; continue;
            }
            track_cell[cell_num++] = { { x, y }, c };
            signature ^= c << (4 * (i++ & 1));

            if (genome.color[c].type == beam)
            {
                int in = 0;
                for (coord_t n : moves_up)
                {
                    coord_t pn = {x+n.x,y+n.y};
                    if (!is_inside(pn)) continue;
                    int cn = v(pn.x, pn.y);
//                    if (cn == -1) continue;
                    local_signature[raw_index(pn.x,pn.y)] ^= cn << (4 * (in++ & 1));
                }
            }
        }
        return signature;
    }

    void build(dnaInfo_t genome)
    {
        coord_t traps[size*size];
        size_t t_num = 0;

        // plot color meanings
        for (size_t c = 0; c != cell_num; c++)
        {
            const cell_t& cell = track_cell[c];
            const colorGene_t& color = genome.color[cell.color];
            (*this)(cell.pos) = (color.type == beam && (local_signature[raw_index(cell.pos.x,cell.pos.y)] % 4) == color.offset)
                    ? block
                    : color.type;

            // build a list of trap locations
            if (color.type == danger)
            {
                coord_t location = cell.pos + moves_up[color.offset];
                if (is_inside(location)) traps[t_num++] = location;
            }
        }

        // plot trap locations
        while (t_num) (*this)(traps[--t_num]) = trap;
    }

    // quick & dirty pathing
    struct candidate_t {
        coord_t pos;
        candidate_t * parent;
        candidate_t() {} // default constructor does not waste time in initializations
        candidate_t(int) : parent(nullptr) { pos.x = pos.y = 0; } // ...this is ugly...
        candidate_t(coord_t pos, candidate_t * parent) : pos(pos), parent(parent) {} // ...but so much fun...
    };

    coord_t path(const neighborhood_t & moves)
    {
        candidate_t pool[size*size]; // private allocation for express garbage collection...
        size_t alloc;

        candidate_t * border[size*size]; // fixed-size FIFO 
        size_t head, tail;

        std::bitset<size*size>closed;

        // breadth first search. A* would be a huge overkill for 25 cells, and BFS is already slow enough.
        alloc = head = tail = 0;
        closed = 0;
        closed[raw_index(candidate_t(0).pos)] = 1;
        border[tail++] = new (&pool[alloc++]) candidate_t(0);
        while (tail > head)
        {
            candidate_t & candidate = *(border[head++]); // FIFO pop
            for (const coord_t move : moves)
            {
                coord_t new_pos = candidate.pos + move;
                if (is_inside(new_pos))
                {
                    size_t signature = raw_index(new_pos);
                    if (closed[signature]) continue;
                    closed[signature] = 1;
                    if ((*this)(new_pos) > empty) continue;
                    if (new_pos.x == 2) goto found_exit; // a path to some location 2 cells forward
                    assert(alloc < size*size);
                    assert(tail < size*size);
                    border[tail++] = new(&pool[alloc++]) candidate_t(new_pos, &candidate); // allocation & FIFO push
                    continue;
                }
                // a path out of the 5x5 grid, though not 2 cells forward
            found_exit:
                if (candidate.parent == nullptr) return move;
                candidate_t * origin;
                for (origin = &candidate; origin->parent->parent != nullptr; origin = origin->parent) {}
                return origin->pos;
            }
        }

        // no escape
        return moves[1]; // one cell forward, either up or down
    }

    colorType_t & operator() (int x, int y) { return map[raw_index(x, y)]; }
    colorType_t & operator() (coord_t pos) { return operator()(pos.x, pos.y); }
    bool is_inside(coord_t pos) { return is_inside(pos.x, pos.y); }
};

std::string trace_DNA(const dna_t d, bool graphics = false)
{
    std::ostringstream res;
    dnaInfo_t genome(d);
    for (size_t c = 0; c != NUM_COLORS; c++)
    {
        if (graphics)
        {
            res << "tbew--"[genome.color[c].type];
            if (genome.color[c].type == danger) res << ' ' << moves_up[genome.color[c].offset].x << ' ' << moves_up[genome.color[c].offset].y;
            if (genome.color[c].type == beam) res << ' ' << genome.color[c].offset << " 0";
            if (c != NUM_COLORS - 1) res << ',';
        }
        else switch (genome.color[c].type)
        {
        case danger: res << "01234567"[genome.color[c].offset]; break;
        case beam  : res <<     "ABCD"[genome.color[c].offset]; break;
        default: res << "!*-#X@"[genome.color[c].type]; break;
        }
    }
    return res.str();
}

coord_t hardBelievers(dna_t d, view_t v)
{
    dnaInfo_t genome(d); // decoded DNA
    map_t     map;       // the surroundings seen by this particular rodent

    // update genome with local context
    genome.update(map.compute_signatures(v, genome));

    // build a map of the surrounding cells
    map.build(genome);

    // move as far to the right as possible, in the contextually preffered direction
    return map.path(genome.up_down ? moves_up : moves_down);
}

int main() {
    time_t start = time(NULL);
    double score = runsimulation(hardBelievers, trace_DNA);
    slog << "Geometric mean score: " << score << " in " << time(NULL) - start << " seconds";
}

Episódio IV: nos orientando

Resultados

Scores: 309371 997080 1488635 1 19 45832 9 94637 2893543 210750 742386 1677242 206614 111809 1 1738598 1 1 342984 2868939 190484 3354458 568267 280796 1 1 1 679704 2858998 1 409584 3823 200724 1 973317 849609 3141119 1 1987305 1 1 57105 245412 1223244 2 1603915 2784761 9 12 1 1839136 1 298951 2 14 138989 501726 1365264 308185 707440 22 772719 17342 63461 3142044 19899 3 409837 48074 3549774 138770 32833 1 1 1184121 67473 310905 1996452 4201 1701954 2799895 2041559 218816 174 433010 51036 1731159 1871641 1 23 2877765 1 127305 27875 626814 142177 2101427 167548 2328741 4 8433 2674119 2990146 466684 1 2 8 83193 388542 2350563 1 1140807 100543 1313548 31949 73117 73300 121364 1899620 1280524 1 10726 12852 7 2165 1 3 44728 2 122725 41 2 1902290 3 1 8581 70598 1148129 429767 1 112335 1931563 521942 3513722 1 2400069 1 3331469 141319 220942 205616 57033 63515 34 6 1419147 1983123 1057929 1 599948 2730727 2438494 5586 268312 1728955 1183258 95241 1537803 11 13 1157309 1750630 1 1 2690947 101211 3463501 1 258589 101615 212924 137664 19624 251591 509429 510302 1878788 1 4045925 1 21598 459159 118663 7 3606309 3 13016 17765 640403 1 72841 695439 1 135297 2380810 1 43 31516 14 1442940 1001957 95903 194951 1 238773 773431 1 1 975692 2 4990979 52016 3261784 2 413095 12 3 420624 7905 60087 760051 2702333 2572405 1 1717432 1 12 3040935 1 1 31787 60114 513777 1 3270813 9639 581868 127091 270 164228 274393 1275008 261419 597715 138913 28923 13059 1848733 2895136 7754 14 1 107592 1 3557771 2067538 147790 112677 119004 1 13791082842974 249727 838699 4067558 6 470799 695141 1 3 1 1276069 23691 831013 5 165142 1236901 1 187522 2599203 1 67179 81345 44111 2909946 94752 7 406018 991024 4 1 3 573689 6 748463 2166290 33865 670769 322844 5657 1131171 1990155 5 4536811 1785704 3226501 2030929 25987 3055355 192547 1761201 433330 27235 2 312244 13203 756723 81459 12 1 1 54142 307858 2 25657 30507 1920292 3945574 1 191775 3748702 3348794 4188197 366019 1540980 3638591 1 1840852 1 26151 2888481 112861 8 11 2 1 27231 1 74 106853 3 173389 2390495 25 1 83116 3238625 75443 1 1 2125260 1 49626 1 6 312084 159735 358268 54351 367201 2868856 5779 172554 119016 141728 3 1 6 9 1 1504011 1 168968 1868493 1 5 1 244563 2 2887999 3144375 1598674 1 1578910 45313 176469 30969 8 127652 1911075 9 1300092 224328 168752 8 1619669 292559 9090 2040459 705819 1852774 10 139217 16 1221670 355060 339599 3 2184244 2546028 1 1 11 70958 242187 1 80737 1 190246 3 1 1 577711 150064 1 1047154 3851461 92399 224270 612237 1 3 3330053 1 1 1192533 615756 267923 144724 2 1 150018 4621881 1 6 299247 115996 2 10 6 185495 76351 465554 178786 1802565 257101 56 2491615 1 24547 1 1203267 32 5741149 541203 11393 1 368082 540534 16167 113481 2004136 13045 17 1 12 333803 14 1955075 1 4 38034 1286203 2382725 26777 1 180312 1 87161 4773392 1244024 1146401 3 80598 2983715 1 63741 1 1 2561436 16 1 1 1807854 1239680 200398 2 46153 1400933 11 5058787 8787 1 98841 89162 1106459 112566 1 4138891 2858906 101835 81375 539485 6587808 1 5359988 1 1 869106 443452 120748 436156 2 2 3944932 1 1875599 2 3081185 733911 447824 1 1 23187 3082414 33 3 1 1 2053904 410824 104571 885952 1946162 2 294773 364169 1 101310 2166548 1177524 2192461 12 4 3457016 90975 2356374 573234 53746 187527 7837 1441335 458407 52139 3387239 2030900 38 1648216 215105 212589 8278 1201586 244282 1 1 1897515 3957343 46 1 134481 1 1 2041785 3 1 37593 163173 1565457 3 1026885 1 34530 4655639 2 18 1940645 1550444 593209 1 2270700 706918 1 1 610113 9 1287883 3 1472134 1998685 1916822 1 296017 2 1 1737607 4155665 1510560 553342 56130 14436 13240604 4025888 1 4253261 174177 2043316 504151 2370989 420666 155232 1 219327 3752236 130062 571247 24 1 29015 31392 1020196 3 1117502 460873 7 1 228 8 133656 1 147008 1 93471 1 1 1 513410 4834094 1 14 1875636 182714 1504903 95263 4418053 1 357853 1135536 3698641 3 239316 4237884 131730 3878724 2158931 55650 1906785 1 26372 32 99217 1645677 379838 1 450352 7329657 112909 1 897980 2114198 308917 126215 1 53839 539997 238036 2 2270000 5 2388928 1668820 519153 58227 347528 1 1 2339954 10 5 2031341 54 2341529 2189774 112731 1 21918 748662 2068921 2 2232504 2923457 97740 3858 16604 398940 388755 1875003 667810 53633 315866 839868 1 7 1 14238 185 4 14 1 2 178947 1965719 398323 120849 48 1397222 961772 34124 2 160652 1 252629 246554 14529 1 299866 135255 490837 2863773 8 10 2 1906405 57 9782 118940 870003 255097 6 4187677 50965 3354376 17611 1804789 183601 158748 1539773 116107 77684 34738 2862836 1 2081903 727739 50328 2740070 17 923524 18 3089706 3144082 1 20 205247 347420 2076952 3725220 39270 2 15 49329 422629 5 1693818 2570558 2146654 1 5 129085 653766 47438 102243 389910 59715 21769 1246783 361571 4 120502 255235 1314165 3 3 5 2902624 76351 3117137 174413 2546645 14534 166054 1013583 1 1 2 9 3027288 3173742 338261 94929 1071263 4659804 1 506576 42798 4 984508 1 4 4 1 18541 7 1 269761 188905 2 1 92011 147031 677955 27484 1291675 2420682 99970 57943 1 4081062 1 250953 704904 4 349180 4273479 30528 2092508 2352781 3700946 1 77799 328993 3684623 3930179 1250080 1975798 54981 1621677 91664 1355832 1084049 721612 56950 197563 246868 5031 1 924076 1328694 58562 1 457662 2445958 1345169 957845 1056809 2485300 1687907 199029 3 9474 86928 1 2419980 3585265 570673 1 1514184 437383 1596697 29709 199606 126031 2 1541777 1 3 2090249 2402438 15 19 1423959 28 37852 4 1652596 1 405512 52 3 1948029 1 2 376 1155902 3 631665 3741991 57673 284026 424787 1 11569 5 1200313 1 20 2360854 1 119994 3889143 673424 797763 1 1 144306 1007659 1231874 75607 1 15 66187 8763 21366 146277 2684501 4458542 162223 3 1 5 94232 3036009 401312 19775 510737 3305062 58905 125783 274094 3089988 118483 1 106213 1 1289180 127905 30 528859 2 1215596 1955900 30 2236528 218643 1 2396631 1598175 1148688 452064 1 1840394 198540 1 1307187 107463 341396 2684981 9602 536871 1 148107 4068 4918434 1 2430254 2066144 88915 3585780 6464 259394 3098337 49601 42 79205 925658 1 2513666 26817 2738302 1 28 345735 5086930 361294 505662 386194 1103890 2653001 412247 4074274 2217918 1 519433 1338570 4289317 140138 18 2519983 168656 4546204 8 1 76545 511580 979214 9318 210013 50508 40 152908 17969 922507 1 7 32 1 388579 1 49886 13319 1066048 4663 27883 38419 1418098 2538216 1 778734 3556791 490764 666880 22746 5666164 4 20 1806284 21142 1 527906 2 12417 182224 49536 105029 206917 2427623 294247 1405136 321480 354137 84225 50 128073 1391176 352835 26074 91159 34229 237942 1 1519676 1 2428669 272681 148689 528951 560736 1 3548197 3833513 1438699 286613 1 1290904 47145 3456135 249648 277045 1012397 271073 1 6 149276 94843 11 177134 32336 2772732 7 22 37065 1 105299 76735 44 2211334 511942 30639 522056 5162 1899842 74 1 1448039 1 88817 21 1027532 555416 1 364383 1335609 167332 283252 49564 220972 1006800 3108886 801258 265596 61651 1 2413276 252747 416606 960925 54 311956 267135 3871698 22581 8978 2 10 1966155 3123429 28 46409 1 18433963725323 1769396 114766 49071 1 1 4228762 3483932 1139490 602592 2700468 770273 3 1 1 212087 281247 27093 156094 286299 1204001 18374 1 330780 1 1 25384 906728 99334 1250819 2161201 34 1027892 1 33449 2 129787 52246 94872 1536841 23470 1 1700323 1 1 3785351 1 95315 1014155 56570 22586 66842 7 156840 48752 1 3143722 1 1168309 2 4 101423 385892 42868 2893851 7 1783109 217499 24 460497 2003214 180135 3503010 131137 2 5240 1621601 2754811 11198 1 1 1105643 1 1671021 3 139611 18268 107229 44582 2211034 1 2880152747163 231008 262504 1 257760 1 1 52992 804418 2 2 4811272 1772250 3 1796530 1918647 1 1934549 1 100550 3448657 1681262 3 604526 320865 1901079 556908 2794800 2472000 637735 123663 1 3213187 118199 2553610 1 1750628 2563806 1 1670872 1 999609 50200 654831 1 164612 2865759 1841739 9 3744159 1331395 3202501 1 7 1 1 239868 1 1 581984 112413 401 1 29656 359367 74532 27226 51752 2583 1 645443 1559731 1 114195 1 85473 229474 111353 1 1521653 1 2568733 444398 2593568 18546 1 158085 1211147 1020006 23407 42514941388799 158442 1 1660358 5 34874 1594789 1551270 386464 502417 32280 170606 1954278 72486 3406066 11 52896 345631 4010742 33307 1951926 1441325 1886066 1 3 402778 3089364 351 28028 4301364 1 431569 5 3054030 375986 404966 1 449317 1230292 1 7 763949 1 2 3197443 1537806 335317 2 1 161263 1 1959902 1664530 139136 447570 1 1 50 158825 222939 1842131 11252 1680094 1017889 71 144808 1 53679 1 41278 1226724 1 1 2 10 2 1 112451 42133 1406662 1 112593 2 2832116 1544488 3579017 3029492 2752014 6 255091 731329 540861 1 426725 440330 212602 202358 173553 4 1189793 11031 84073 2084554 3963 1473295 1 642570 1 1423688 34509 75056 163273 490193 3200250 451777 157797 4156542 2386299 2794795 2735308 1332758 1193296 1131014 1001570 414257 4415511 4 3 1 3499595 536583 16731 93839 92382 1 45890 1 17695 8 867246 18 1607123 3197052 5 40009 1 329895 3497309 2416600 2316390 11 118179 2166659 2 136426 76762 2 14 2 3632525 214889 6 3900942 270409 230143 120414 417489 16706 1563597 31418 2 73 468763 88585 428274 3537347 2 1 491461 2806485 1 7 2950804 115684 4 1 429002 85771 2480 285541 186486 1 1 2430862 6 9 4 1833423 17143 353689 2568741 408890 2929237 208679 2198380 1 2501053 1933666 180843 1 1 2569886 1 17035 3449472 71357 246257 217898 1 47601 589824 401679 362878 13178 34464 1076419 1 554417 1 21248 2136449 1068 23029 8 766649 4 302879 274751 19 1 390259 1899931 233910 1392272 184492 2 2752059 55813 1 6 64674 205205 595508 1714309 582492 4821971 63973 1708726 189200 4548446 479425 2866037 1 1 1 2139319 1 1 3 1572621 2086152 2341038 1 619612 1 78942 772466 18932 1404368 936790 2263929 230200 3009227 251065 835010 88225 642856 824193 5559048 1 36348 2338046 481447 108132 2728223 3539009 1 197164 181408 171634 2172263 2317332 1598340 1318829 1746303 7 59657 1 1415452 122924 915828 1063890 40339 430186 4 2165185 2250922 704568 85138 4417453 255 326360 33541 3 49759 72127 912537 599665 1 29169 168741 349838 996835 1548193 2 28449 803521 4 2 2 3359043 3243259 1 491574 1675000 186105 3203018 11 39127 959876 334480 873131 70262 137080 1076591 1 2155613 74804 893022 2473922 1 1 269835 5 2407308 3 55200 905207 1 1 1245609 65934 7 1372126 530582 1383562 1 1 2718341 1 3947638 4 76837 412551 11 1 1 1208080 3024670 277 46485 1 9 562183 46 2985858 3379885 67816 1896527 1 105478 2035453 3026415 1 189256 2992616 2098002 1099666 775250 5913 13 406948 166773 1 322250 41919 480047 64950 17435 2147428 2336270 3330243 352709 86029 1398723 106236 312951 1 408211 252689 847088 2 17 34088 13128 187366 2 1559482 2349010 1651122 2371088 401005 1715445 1 29483921 1464444 50228 2365851 1651636 768715 226704 23677 83501 1 252623 444628 34 3640316 3602127 45369 1 1 1978261 1 3019189 1 25411 2177552 192839 191146 293712 3840622 182598 4069200 175757 1 2250458 4 1 7 2740824 2753005 1 2836428 1 12 19 2 1788326 3302198122211 3386546 1176663 20847 28 1194294 794665 2630378 13624 722012 2273872 1549353 1 3 1735700 1668388 416 970581 258382 295427 1 121571 3193610 3764806 1 368985 20436 89411 3 16130 2 241879 1 2996216 136958 2382095 510146 1762872 1372194 4215387 346915 4423 1 904153 2004500 248495 836598 3529163 27 2547535 1424181 1885308 1 1056747 289743 176929 2299073 170473 1 1 839941 12382 51457 608526 1684239 4843522 34550 929855 2767014 2979286 1 340808 184830 131077 57298 63854 381689 201998 1715328 118687 69190 123466 1 2 69392 159797 382756 1513430 2506318 457 1
Geometric mean score: 10983.8 in 31214 seconds

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:

  • detector de armadilha de perigo
  • vazio vazio em qualquer lugar da pista
  • bloco proibido em qualquer lugar da pista
  • feixe visto vazio ou bloqueado, dependendo do ambiente

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:

  • 4 perigo
  • 4 vazio
  • Bloco 4
  • 4 feixe

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:

  • 4 perigo
  • 7 vazio
  • Bloco 5

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%

A fórmula misteriosa é a mutação de 1 bit P = genoma inteiro 1-P intacto 1/100 , sendo 100 o comprimento do bit genoma.

Por exemplo, para 10% de probabilidade, mutação P 1 bit = 1 - 0,1 1/100 = 0,0277
Para 5% de probabilidade, P = 1 - 0,05 1/100 = 0,0295
Invertendo a fórmula, descobrimos que 0,01 oferece 37% de chances de ser inalterado por mutação.

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.


Como existe um número par de armadilhas, vazias, paredes e teleportos, você precisa apenas de 3 bits para armazenar as proporções com precisão (mesmo se considerar armadilhas == paredes). Além disso, você considerou / descartou a ideia de usar bits de desvio de trap não utilizados no anti-wallbanging? Como o objetivo é não herdar dos pais, você pode realmente usar todos os bits no anti-wallbanging. Nenhuma razão para eles serem únicos, eu não pensaria.
Mooing Duck

11
@MooingDuck Testei sua idéia de reutilizar bits de deslocamento, mas falhou. Como eu temia, reutilizar uma informação para dois propósitos diferentes parece não funcionar. Suponha, por exemplo, que os bits de deslocamento de uma determinada cor sejam necessários para que um genoma escolha a direção vertical adequada em um determinado caminho, essa cor não pode mais representar uma armadilha significativa sem destruir o caminho que depende dos mesmos dados. Eu também tentei usar 6 bits, mas como eu temia também os 4 bangers anti-parede restantes estavam em um estoque muito curto.

11
É bom saber, mas sugeri duas idéias: uma era usar todos os bits (reutilizando algumas) e a outra era usar os bits de deslocamento de armadilha não utilizados para paredes / vazias. Você tentou os dois? (Eu entendo se você não quiser tentar, você está quase obrigado a tentar se você não quer)
Mugidos Duck

11
Eu tentei os dois, e ambos falharam. As compensações de interceptação são importantes mesmo quando um gene não as utiliza, porque esse gene ainda pode se transformar novamente em uma cor de interceptação; nesse caso, o deslocamento de interceptação provavelmente terá se alterado para os bits de contexto mais rentáveis ​​e perdeu seu significado como deslocamento. . Agora ele voltará ao valor de compensação lucrativo e destruirá os caminhos dos ratos que dependiam dele como indicadores contextuais. Acho que vi o caso de uma oscilação tão grande com minha ferramenta gráfica, mas não é fácil exibir uma instância clara desse problema.

16

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

class ColorScorePlayer(Player):
    def __init__(self):
        Player.__init__(self)
        self.coords = [Coordinate( 1, 0),
                       Coordinate( 1,-1),
                       Coordinate( 1, 1)]
        self.n_moves = len(self.coords)

    def turn(self):
        max_score = max([self.bit_chunk(6*self.vision_at(c.x, c.y), 6) for c in self.coords if self.vision_at(c.x, c.y)>=0])
        restricted_coords = [c for c in self.coords if self.vision_at(c.x, c.y)>=0 and self.bit_chunk(6*self.vision_at(c.x,c.y), 6) == max_score]

        return random.choice(restricted_coords)

Rubi

class ColorScorePlayer < Player
    def initialize(rng)
        super(rng)
        @coords = [Vector2D.new( 1,-1),
                   Vector2D.new( 1, 0),
                   Vector2D.new( 1, 1)]
    end

    def vision_at(vec2d)
        @vision[vec2d.x+2][vec2d.y+2]
    end

    def turn
        max_score = @coords.map { |c|
            color = vision_at(c)
            color < 0 ? -1 : bit_chunk(6*color, 6)
        }.max

        restricted_coords = @coords.select { |c|
            color = vision_at(c)
            color >= 0 && bit_chunk(6*color, 6) == max_score
        }

        restricted_coords.sample(random: @rng)
    end
end

C ++

coord_t colorScorePlayer(dna_t d, view_t v) {
    const int chunklen = DNA_BITS / N_COLORS;
    int ymax[3], nmax, smax = -1;
    for(int y = -1; y <= 1; y++) {
        if(v(1, y) == OUT_OF_BOUNDS) continue;
        int score = dnarange(d, v(1, y)*chunklen, chunklen);
        if(score > smax) {
            smax = score;
            nmax = 0;
        }
        if(score == smax) ymax[nmax++] = y;
    }
    return {1, ymax[v.rng.rint(nmax)]};
}

C #

public static void ColorScorePlayer(GameLogic.IView v, GameLogic.IGenome g, Random rnd, out int ox, out int oy)
{
    ox = 0;
    oy = 0;

    var max_score = cspcoords.Where(c => v[c.x, c.y] > -1).Select(c => g.cutOutInt(6 * v[c.x, c.y], 6)).Max();
    var restrictedCoords = cspcoords.Where(c => v[c.x, c.y] > -1 && g.cutOutInt(6 * v[c.x, c.y], 6) == max_score).ToArray();

    Coord res = restrictedCoords[rnd.Next(restrictedCoords.Length)];

    ox = res.x;
    oy = res.y; 
}

Java

package game.players;

import java.awt.*;
import java.util.Map;

public class ColorScorePlayer extends Player{
    private static final Point[] possibleMoves = {new Point(1, 0), new Point(1, -1), new Point(1, 1)};

    @Override
    public Point takeTurn(String genome, Map<Point, Integer> vision) {
        int chunkLength = genome.length()/16;
        int maxSum = -1;
        Point maxSumMove = possibleMoves[0];
        for (Point move: possibleMoves){
            if (vision.get(move) == -1){
                continue;
            }
            int initialPoint = chunkLength*vision.get(move);
            int sum = 0;
            for (int i = initialPoint; i < initialPoint + chunkLength; i++){
                sum = (sum<<1)+Integer.parseInt(genome.charAt(i)+"");
            }
            if (sum > maxSum){
                maxSum = sum;
                maxSumMove = move;
            }
        }
        return maxSumMove;
    }
}

O jogador marca bastante inconsistentemente. Aqui estão 50 execuções aleatórias:

Scores: 1 1 1132581 3 43542 1 15 67 57 1 11 8 623162 1 1 1 134347 93198 6 1 2 1 1 245 3 1 1 27 1 31495 65897 9 5 1 2 20 2 117715 1 1 1 20 64616 5 38 1 2 1 2 12

12

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.

coord_t colorFarSeeker(dna_t d, view_t v) {
#define s(w,x,y) (v(x,y)>-1?((b+dnarange(d,l+m+n*v(x,y),n))*w):0)
#define max2(a,b) (((a)>(b))?(a):(b))
#define max3(a,b,c) (max2(a,max2(b,c)))
#define push(vec,maxScore,score,x,y) if(score==maxScore&&v(x,y)>-1)vec.push_back({x,y});
#define tryReturn() if(vec.size()){return vec[v.rng.rint((int)vec.size())];}vec.clear();

    // Some constants to tweak
    int k = 4;
    int l = 3;
    int m = dnarange(d, 0, l);
    int n = 4;
    int b = dnarange(d, l, k) + 10;

    std::vector<coord_t> vec;

    // Looks forward for good moves...
    int upRightScore = s(1,0,-2) + s(1,1,-2) + s(1,2,-2) + s(5,1,-1);
    int forwardScore = s(1,2,-1) + s(1,2,0) + s(1,2,1) + s(5,1,0);
    int downRightScore = s(1,0,2) + s(1,1,2) + s(1,2,2) + s(5,1,1);
    int maxForwardScore = max3(upRightScore,forwardScore,downRightScore);
    push(vec,maxForwardScore,upRightScore,1,-1);
    push(vec,maxForwardScore,forwardScore,1,0);
    push(vec,maxForwardScore,downRightScore,1,1);
    tryReturn();

    // Looks sideways for good moves...
    int upScore = s(1,-1,-2) + s(1,0,-2) + s(1,1,-2) + s(5,0,-1);
    int downScore = s(1,-1,2) + s(1,0,2) + s(1,1,2) + s(5,0,1);
    int maxSideScore = max2(upScore,downScore);
    push(vec,maxSideScore,upScore,0,-1);
    push(vec,maxSideScore,downScore,0,1);
    tryReturn();

    // If all else fails, move backwards randomly.
    // I have tried considering the scores of backmoves,
    // but it seems worse than just randomly moving backwards. 
    vec.push_back({-1,-1});
    vec.push_back({-1,0});
    vec.push_back({-1,1});
    return vec[v.rng.rint((int)vec.size())];

}

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)

Pontuações das minhas corridas: 6 6 53 1 5 101223 89684 17 2 303418 4 85730 24752 1 1 1 3482515 39752 1 59259 47530 13 554321 1 563794 1 1770329 1 57376 1 123870 4 1 1 79092 69931 594057 1 69664 59 1 6 37857 1733138 55616 2 1 51704 1 254006 4 24749 1 117987 49591 220151 26 4292194 23 57616 72 67 1 4 308039 1 1 103 89258 1 286032 1 5 3 1 5 114851 46 143712 5 15 9 80 7425 1 1 7 108379 70122 97238 1 1 5 2 23 104794 1 10476 59245 1 204 1 1 12 1 29641 1 314894 18785 13 1 3 1 1 1 2 526001 1 1 1 27559 29285 3 3 128708 70386 30 2 2 1 208531 331 1 2 1 61 114993 1 15 51997 1 2 1 146191 1 31 4 3 1 161422 207 1 64 1 1 1 68594 145434 87763 150187 169 185518 1 1 1 1 24208 2570 1 1 537 1 1 462284 1 2 55 1 1 1 214365 1 40147 2 213952 1 29 3 1 2144435 5 4502444 72111 1 1 1 1 1 774547


11
Eu tenho uma média geométrica de 74,7 com 1000 jogos, bom trabalho.
precisa saber é

8

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:

  • Duas ou mais partes do tabuleiro são mapeadas para o mesmo bit, mas requerem movimentos diferentes.
  • Algumas placas não são aceitáveis ​​usando apenas movimentos diagonais.

Código

class BishopPlayer(Player):
    def __init__(self):
        Player.__init__(self)
        self.coords = [Coordinate(1,-1),
                       Coordinate(1, 1),
                       ]
        self.inputs = [(x,y) for x in (0,1,2) for y in (-1,0,1)]

    def turn(self):
        # Move away from out of bounds areas
        if self.vision_at(0,-1) == -1:
            return self.coords[1]
        if self.vision_at(0,1) == -1:
            return self.coords[0]

        # Move right, and either up or down based on one bit of the genome
        bit_to_use = sum(self.vision_at(self.inputs[i][0],
                                        self.inputs[i][1]
                                        ) * (16 ** i) for i in range(9)
                         ) % 100
        return self.coords[self.bit_at(bit_to_use)]

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:

Pontuações: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 11 1 2 1 1 1 1 1 6 1 8 1 10 15 1 1 12544 1 2 1 1 1 1 3 7554 1 1 1 1 1


8

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 ++.

coord_t runbonus(dna_t d, view_t v) {
    int ymax[3], nmax, smax = -1;
    if(!v.rng.rint(30)) {
        int y;
        while(!~v(1, y = v.rng.rint(-1, 1)));
        return {1, y};
    }
    for(int y = -1; y <= 1; y++) {
        if(v(1, y) == OUT_OF_BOUNDS) continue;
        int score = 0;
        int streak = 0;
        for(int i = 0; i < 6; i++) {
            if(d[6*v(1,y) + i])
                score += ++streak;
            else
                streak = 0;
        }
        if(score > smax) {
            smax = score;
            nmax = 0;
        }
        if(score == smax) ymax[nmax++] = y;
    }
    return {1, ymax[v.rng.rint(nmax)]};
}

por curiosidade, quanto tempo demorou o teste de 5000 faixas? Meus ratos precisam de mais de uma hora para completar 1000 faixas, então eu teria que deixar o computador funcionar a noite toda para reproduzir seu caso de teste.

@kuroineko A resposta à sua pergunta já estava na minha resposta.
precisa saber é

OPA, desculpe. Tentarei seu código no meu PC para ver qual o papel do hardware na diferença de velocidade. E talvez tente usar o gcc em vez do MSVC. Percebi um aumento de 30% no desempenho sobre o MSVC em alguns outros bits de código pesados ​​da computação.

Seu código foi executado em pouco mais de 20 minutos para 1000 faixas no meu i3-2100@3.1GHz com 4 threads. A pontuação foi de cerca de 56 . Parece que meu PC é 5 vezes mais lento que o seu e meu código seria cerca de 6 vezes mais lento em uma determinada máquina (mas ter uma pontuação melhor mecanicamente implica em um tempo de computação mais longo). Desde que eu estou demasiado quebrou para comprar um novo PC, é hora para um pouco de otimização ...

8

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.

#include <utility>

#define IDX(a,b) a[VIEW_DIST + b.x][VIEW_DIST + b.y]

std::pair<coord_t,int> planAhead(int weights[N_COLORS], view_t &v, coord_t target) {
    bool open[VIEW_DIST*2+1][VIEW_DIST*2+1] = {false};
    bool closed[VIEW_DIST*2+1][VIEW_DIST*2+1] = {false};
    int f_score[VIEW_DIST*2+1][VIEW_DIST*2+1] = {0};
    int g_score[VIEW_DIST*2+1][VIEW_DIST*2+1] = {0};
    coord_t came_from[VIEW_DIST*2+1][VIEW_DIST*2+1] = {{0,0}};
    open[VIEW_DIST][VIEW_DIST] = true;
    g_score[VIEW_DIST][VIEW_DIST] = v.rng.rint(5);
    f_score[VIEW_DIST][VIEW_DIST] = (abs(target.x) + abs(target.y)) * 10;
    for (;;) {
        coord_t current{VIEW_DIST+1,0};
        for (int x = 0; x < (VIEW_DIST*2+1); x++)
            for (int y = 0; y < (VIEW_DIST*2+1); y++)
                if (open[x][y] && (current.x > VIEW_DIST || f_score[x][y] < IDX(f_score,current)))
                    current = {x - VIEW_DIST, y - VIEW_DIST};
        if (current.x > VIEW_DIST)
            return {{1,0}, 1000000};
        if (current.x == target.x && current.y == target.y)
            break;
        IDX(open,current) = false;
        IDX(closed,current) = true;
        for (int dx = -1; dx <= 1; dx++) for (int dy = -1; dy <= 1; dy++) {
            if (dx == 0 && dy == 0)
                continue;
            coord_t tentative{current.x + dx, current.y + dy};
            if (abs(tentative.x) > VIEW_DIST || abs(tentative.y) > VIEW_DIST)
                continue;
            if (IDX(closed,tentative))
                continue;
            auto color = v(tentative.x, tentative.y);
            if (color == OUT_OF_BOUNDS)
                continue;
            auto tentative_g = IDX(g_score,current) + weights[color];
            if (!IDX(open,tentative) || tentative_g < IDX(g_score,tentative)) {
                IDX(came_from,tentative) = current;
                auto distance = abs(tentative.x - target.x) + abs(tentative.y - target.y);
                IDX(f_score,tentative) = tentative_g + distance * 10;
                IDX(g_score,tentative) = tentative_g;
                IDX(open,tentative) = true;
            }
        }
    }
    auto prev = target, current = target;
    while (current.x != 0 || current.y != 0)
        prev = current, current = IDX(came_from,current);
    return {prev, IDX(g_score,target)};
}

coord_t starPlayer(dna_t d, view_t v) {
    const int chunklen = DNA_BITS / N_COLORS;
    int weights[N_COLORS];
    for (int i = 0; i < N_COLORS; i++)
        weights[i] = dnarange(d, i*chunklen, chunklen);
    std::pair<coord_t,int> choice{{1,0}, 1000000};
    for (int y = -VIEW_DIST; y <= VIEW_DIST; y++) {
        auto plan = planAhead(weights, v, {VIEW_DIST, y});
        if (plan.second < choice.second)
            choice = plan;
    }
    return choice.first;
}

Pontuações da amostra:

4 92078 1 10 1 1 3 2 2862314 5 24925 1 3 2 126502 1 24 1097182 39 1 1 1 47728 227625 137944 15 1 30061 1 1 1 3171790 19646 10 345866 1 1 1 829756 425 6699 22 8 1 1 6 6 104889 125608 1


11
Em 1000 jogos, obtive uma pontuação de 133,2, bom.
precisa saber é

7

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]

  • Soma cada n-ésimo bit do genoma até 96
  • Se a pontuação da soma for> = 4, diga que este quadrado está bloqueado
  • Se a pontuação da soma for <= 4, sua pontuação final será 2 ^ da pontuação da soma

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.

#include "./gamelogic.cpp"

#include <algorithm>
#include <set>
#include <map>
#include <climits>

bool operator< (const coord_t &a, const coord_t &b){
    if(a.x != b.x){ return a.x < b.x; }
    else if (a.y != b.y){ return a.y < b.y; }
    else{ return false; }
}

bool operator== (const coord_t &a, const coord_t &b){
    return (a.x == b.x) && (a.y == b.y);
}

int coordDistance(const coord_t &a, const coord_t &b){
    int xDif = abs(a.x - b.x);
    int yDif = abs(a.y - b.y);
    return xDif > yDif ? xDif : yDif;
}

int coordMinSetDistance(const coord_t &a, const std::set<coord_t> &ends){
    int min = INT_MAX;
    for (auto i : ends){
        int cur = coordDistance(a, i);
        if (cur < min){
            min = cur;
        }
    }
    return min;
}


class ColorMap{
public:
    view_t *v;
    int colors[16] = {};
    const int Blocked = -1;

    ColorMap(dna_t &d, view_t *v){
        this->v = v;

        //Decode the genome
        for (int i = 0; i <= (16*6); i++){
            if (d.at(i) == true){
                colors[i % 16]++;
            }
        }

        //Encode the result
        bool guessedWalls = false;
        for (int i = 0; i < 16; i++){
            if (colors[i] >= 4){
                colors[i] = Blocked;
                guessedWalls = true;
            }
            else{
                colors[i] = pow(2, colors[i]);
            }
        }

        if (guessedWalls == false){
            for (auto i : colors){
                i = Blocked;
            }
        }
    }

    int operator() (coord_t pos){
        if (abs(pos.x) > VIEW_DIST || abs(pos.y) > VIEW_DIST){
            return Blocked;
        }

        int value = (*v)(pos.x, pos.y);
        if (value == OUT_OF_BOUNDS){
            return Blocked;
        }
        else{
            return colors[value];
        }
    }

    void print(){
        int lower = -1 * VIEW_DIST;
        int upper = VIEW_DIST;
        for (int y = lower; y <= upper; y++){
            for (int x = lower; x <= upper; x++){
                std::cout << std::setw(3) << this->operator()({ x, y });
            }
            std::cout << std::endl;
        }
    }
};

class node{
public:
    coord_t pos;
    coord_t cameFrom;
    int gScore;
    int minDistance;

    node(coord_t pos, coord_t cameFrom, int gScore, int minDistance){
        this->pos = pos;
        this->cameFrom = cameFrom;
        this->gScore = gScore;
        this->minDistance = minDistance;
    }

    int fScore() const{ return gScore + minDistance; };

    bool operator< (const node &rhs) const{ return fScore() < rhs.fScore(); }
};

class EditablePriorityQueue{
private:
    //This is reversed so smallest are on top
    struct lesser{
        bool operator()(node *a, node *b) const{
            return (*b) < (*a);
        }
    };

    std::vector<node*> queue; // Use heap functions to maintain the priority queue ourself
    std::map<coord_t, node*> members;

public:
    EditablePriorityQueue(){};

    ~EditablePriorityQueue(){
        for (auto &m : members){
            delete m.second;
        }
    }

    bool empty(){ return members.empty(); }

    node *top(){
        auto top = this->queue.front();
        std::pop_heap(queue.begin(), queue.end(), lesser());
        queue.pop_back();
        members.erase(top->pos);
        return top;
    }

    void set(coord_t target, coord_t cameFrom, int gScore, int minDistance){
        auto targetLocation = members.find(target);

        //If the target isn't a member add it
        if (targetLocation == members.end()){
            auto *newNode = new node(target, cameFrom, gScore, minDistance);
            queue.push_back(newNode);
            std::push_heap(queue.begin(), queue.end(), lesser());
            members[target] = newNode;
        }
        //The target must be updated
        else{
            auto currentNode = targetLocation->second;
            if (currentNode->gScore > gScore){
                currentNode->gScore = gScore;
                currentNode->cameFrom = cameFrom;
                std::make_heap(queue.begin(), queue.end()); //More efficient way to do this?
            }
        }
    }
};

std::pair<coord_t, int> pathCost(ColorMap &m, coord_t start, const std::set<coord_t> &ends){
    EditablePriorityQueue openSet;
    std::set<coord_t> closedSet;
    std::map<coord_t, coord_t> cameFrom;

    openSet.set(start, start, 0, coordMinSetDistance(start, ends));
    while (openSet.empty() == false){
        auto current = openSet.top();
        closedSet.insert(current->pos);
        cameFrom[current->pos] = current->cameFrom;

        //Check if we're done
        if (ends.count(current->pos) != 0){
            //Recover the path
            coord_t path = current->pos;
            int finalScore = current->gScore;
            delete current;
            while (!(cameFrom[path] == start)){
                path = cameFrom[path];
            }

            return{ path, finalScore };
        }               

        //Examine current's neighbours
        for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++){
            coord_t neighbour = { current->pos.x + x, current->pos.y + y };

            if (x == 0 && y == 0){ continue; }

            closedSet.count(neighbour);
            if (closedSet.count(neighbour) != 0){ continue; }

            int neighbourScore = m(neighbour);
            if (neighbourScore == m.Blocked){ continue; }

            int tentativeScore = current->gScore + neighbourScore;
            openSet.set(neighbour, current->pos, tentativeScore, coordMinSetDistance(neighbour, ends));

        }
        delete current;
    }

    return{ { -1, 0 }, INT_MAX }; //Try to end it
}

coord_t myPlayer(dna_t d, view_t v) {
    auto ourMap = ColorMap(d, &v);

    std::set<coord_t> edges;
    for (coord_t edge = { VIEW_DIST, -1 * VIEW_DIST }; edge.y <= VIEW_DIST; edge.y++){
        edges.insert(edge);
    }

    //Move to the neighbor closest to a square on the right
    auto result = pathCost(ourMap, { 0, 0 }, edges);
    auto minMove = result.first;

    return minMove;
}

int main() {
    slog << "Geometric mean score: " << runsimulation(myPlayer) << std::endl;
}

Hm, isso não compila para mim 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){
Martin Ender

Não tem problema, basta mudar atpara []corrigi-lo.
feersum

7

The FITTEST - Pontuação média geométrica: ~ 922 (2K corridas)

Minha abordagem é:

  1. Descubra o que mata as espécies e defina o comportamento desejado (funcional)
  2. Implementar o comportamento desejado no código (técnico)
  3. Dê uma prioridade . É mais importante ou menos importante do que outro comportamento desejado.
  4. Otimize a pontuação média geométrica, ajustando os parâmetros das soluções.

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:

  1. As espécies devem aprender quais cores são seguras e quais são ruins.
  2. As espécies devem focar principalmente sua decisão para onde ir, com base nas 3 células bem na frente, mas se não houver bons movimentos disponíveis, os movimentos verticais ou para trás devem ser considerados
  3. As espécies também devem olhar o que está além das 8 células ao seu redor e usá-lo em informações na tomada de decisões
  4. As espécies devem aprender a identificar armadilhas .
  5. 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. Se elas são as espécies com a maior pontuação mais adequada naquele momento, seu DNA com a suposição errada sobre a parede é duplicado muitas vezes nos recém-nascidos . Depois de algum tempo, todas as espécies ficam presas em frente às paredes e nenhuma delas atinge o objetivo de marcar pontos. Como parar os idiotas?

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:

  1. armazenamento de dados decimal: usando a função 'incorporada' dnarange, exemplo: 4 bits 1011se 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]
  2. risca o armazenamento de dados: usando a dnaStreakRangefunçã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]
int dnaStreakRange(dna_t &d, int start, int chunklen) {
    int score = 0;
    int streak = 0;
    for(int i = 0; i < chunklen; i++) {
        if(d[start + i])
            score += ++streak;
        else
            streak = 0;
    };  
    return score;
}
  1. armazenamento de dados de bits: usando a dnaCountRangefunção (definida abaixo), exemplo: 4bits 1011 se tornará 1x1 + 0x1 + 1x1 + 1x1 = 3. Valores possíveis (para 4 bits): [0, 1, 2, 3, 4]
int dnaCountRange(dna_t &d, int start, int chunklen) {
    int score = 0;
    for(int i = 0; i < chunklen; i++) {
        if(d[start + i])
            score ++;
    };  
    return score;
}

As diferenças entre os métodos de armazenamento são:

  • O método de armazenamento decimal é vulnerável a uma alteração de bit único no DNA. Quando o valor do bitsum muda de 1011 para 0011, seu valor muda de 3 para 2, o que é uma alteração menor.
  • O método de armazenamento decimal é homogêneo . Cada um dos valores possíveis tem a mesma alteração para ocorrer. A chance de você ler o valor de 15 em um bloco de memória de armazenamento de 4 bits é 1/16 = 6%. O método de armazenamento de faixas não é homogêneo . A chance de um valor de 4 bits da raia ser menor ou igual a 6 é (15-3) / 16 = 81% (todas as 16 combinações, exceto 0111,1110,111). Abaixo de um visual que mostra a forma da distribuição. Como você pode ver na seta azul, a chance de uma sequência de 4 bits ser menor ou igual a 6 é 81%: visualização da distribuição dos tipos de armazenamento decimal, de raias e de bits para números binários longos de 4,5 e 6 bits

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:

  • 33% das 16 cores são ruins e, portanto, quando a pontuação de um movimento for inferior a 63/3, o movimento não será permitido. Portanto 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:

  • Quando nenhum avanço é permitido, os movimentos verticais são comparados entre si da mesma maneira. Se também nenhum movimento vertical for permitido, os movimentos para trás serão classificados. Isso é alcançado através da weightMovevariável

Veja o que está além:

  • Quando 2 ou 3 movimentos têm pontuações idênticas, a caixa 3x3 ao redor desses movimentos determinará (via x2e y2loops) qual é a melhor opção (através da mainSubScorevariável). A coluna mais à direita nessa caixa 3x3 é a líder.
coord_t adjustedColorPlayer(dna_t d, view_t v) {
    const int chunklen = 6,threshold = 63/3;
    int xBestScore=0, yBestScore=0;
    long bestScore=-1, weightMove, weightMove2, mainScore;
    for(int x = -1; x <= 1; x++) {
        if (x < 0) weightMove = 1000; // moving backward
        if (x== 0) weightMove = 10000; //moving vertical
        if (x > 0) weightMove = 100000; //moving forward
        for(int y = -1; y <= 1; y++) {
            if(v(x, y) == OUT_OF_BOUNDS || (x==0&&y==0) ) continue;
            mainScore = dnarange(d,v(x,y)*chunklen,chunklen);
            if (mainScore<threshold+1) {
                mainScore =  0; //not a suitable move because too low score
            }else{
                mainScore*= weightMove;
                // when equal score, use sub score by examining 5x5 box to rank moves
                for(int x2 = x-1; x2 <= x+1; x2++){     
                    if (x2 < x) weightMove2 = 1; // moving backward
                    if (x2== x) weightMove2 = 10; //moving vertical
                    if (x2 > x) weightMove2 = 100; //moving forward
                    for(int y2 = x-1; y2 <= y+1; y2++){     
                        if(v(x2, y2) != OUT_OF_BOUNDS){
                            long mainSubScore = dnarange(d,v(x2,y2)*chunklen,chunklen);
                            if (mainSubScore>=threshold+1) mainScore+=mainSubScore*weightMove2;
                        }
                    }
                 }
            }
            if(mainScore > bestScore) {
                bestScore = mainScore;              
                xBestScore = x;
                yBestScore = y;
            }
        }
    }
    return{xBestScore,yBestScore};
}

Pontuação: 123 (2K corridas)

Primeiras 50 pontuações (18 jogos marcaram apenas 1 ponto):

1 10 1 79947 3 1 11 125 7333287 23701 310869 53744 1 2 2 2 2 1 1 57556 2 688438 60 1 2 2636261 26306 1 125369 1 1 1 61895 27 1 36 1 91100 87636 1 2 47497 53 16 1 11 222384 1 1 1

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]):

  • marcou 0: Teleporte para trás, ambas as paredes, 1x seguro
  • pontuação 1: Armadilha para trás (tão inofensiva), Teleporte para trás, 1x segura
  • marcou 2: Armadilha para a frente (tão perigosa), 1x segura
  • marcou 3: Teleporte para frente, 5x seguro
  • marcou 4: Teleporte para frente, 1x seguro

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:

  • 0: cor 0 - cor 3,
  • 1: cor 4 - cor 7,
  • 2: cor 8 - cor 11,
  • 3: cor 12 - cor 16

e 4 posições de direção de movimento

  • 0: mover vertical ou para trás,
  • 1: avançar para cima,
  • 2: seguir em frente,
  • 3: avançar para baixo

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.

int dnaLookup2(dna_t &d, int start, int chunklen, int storageMethod) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    int score = 0, streak = 0;
    for(int i = start; i < start+chunklen; i++) {
        int value = d[i];
        if (storageMethod==0) {
            score = (score << 1) |value;
        }else{
            if (storageMethod==1){
                if(value) score += ++streak; else streak = 0;
            }else{
                if(value) score ++;         
            }
        }
    };  
    return score;
}

coord_t theTrapFighter(dna_t d, view_t v) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    const int colorMemStorageMethod = 1, colorMemBlockSize = 3;
    const int trapMemStorageMethod = 0, trapMemBlockSize = 3;
    const int trapMemTopThreshold = 4, nDirBins = 4, nColorBins = 4;

    int xBestScore=0, yBestScore=0;
    long bestScore=-1, weightMove, weightMove2, mainScore;
  for(int x = -1; x <= 1; x++) {
        if (x < 0) weightMove = 1000; // moving backward
        if (x== 0) weightMove = 10000; //moving vertical
        if (x > 0) weightMove = 100000; //moving forward
        for(int y = -1; y <= 1; y++) {          
            int color = v(x, y);
            if(color == OUT_OF_BOUNDS || (x==0&&y==0) ) continue;
            mainScore = dnaLookup2(d,color*colorMemBlockSize,
             colorMemBlockSize,colorMemStorageMethod);
            if (mainScore==0) {
                //not a suitable move because too low score
            }else{
                mainScore*= weightMove;
                //lookup trap likelihood
                int directionBin = 0;
                if (nDirBins==3) directionBin = x>0?y+1:-1;
                if (nDirBins==4) directionBin = x>0?y+2:0;
                // put 16 colors in nColorBins bins
                int colorBin = v(0,0)*nColorBins/N_COLORS; 
                colorBin = colorBin>(nColorBins-1)?(nColorBins-1):colorBin;
                if (directionBin >= 0 &&
                 dnaLookup2(
                   d,
                   colorMemBlockSize*16
                    +trapMemBlockSize*(nColorBins*directionBin+colorBin),
                   trapMemBlockSize,
                   trapMemStorageMethod
                 ) >=trapMemTopThreshold){
                  //suspect a trap therefore no sub score is added                  
                 }else{
                    // when equal score, use sub score by examining 5x5 box to rank moves
                    for(int x2 = x-1; x2 <= x+1; x2++){     
                        if (x2 < x) weightMove2 = 1; // moving backward
                        if (x2== x) weightMove2 = 10; //moving vertical
                        if (x2 > x) weightMove2 = 100; //moving forward
                        for(int y2 = x-1; y2 <= y+1; y2++){     
                            int color2 = v(x2, y2);
                            if(color2 != OUT_OF_BOUNDS){
                                mainScore+=weightMove2 * dnaLookup2(d,color2*colorMemBlockSize,
                                 colorMemBlockSize,colorMemStorageMethod);
                            }
                        }
                    }               
                 }
            }
            if(mainScore > bestScore) {
                bestScore = mainScore;              
                xBestScore = x;
                yBestScore = y;
            }
        }
    }
    return{xBestScore,yBestScore};
}

Pontuação: 580 (2K corridas)

Primeiras 50 pontuações (13 jogos marcaram apenas 1 ponto):

28.044 14.189 1 2.265.670 2.275.942 3 122.769 109.183 401.366 61.643 205.949 47.563 138.680 1 107.199 85.666 31 2 29 1 89.519 22 100.908 14.794 1 3.198.300 21.601 14 3.405.278 1 1 2 74.167 1 71.242 97.331 201.059 1 1 102 94.444 2.734

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 inInversee inReverse.

Se for inInverseigual 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, essa inInverseespé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 inReverseigual 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 a inInverseabordagem é que a inReversevontade 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 inInversee inReverseao 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.

unsigned long hashCode=17;
for(int x = -2; x <= 2; x++) {
    for(int y = -2; y <= 2; y++) {
        int color = v(x, y)+2;
        hashCode = hashCode*31+color;
    }
}       

Os números primos 17e 31estã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):

int dnaLookup(dna_t &d, int start, int chunklen, int storageMethod,int inInverse) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    int score = 0;
    int streak = 0;
    for(int i = start; i < start+chunklen; i++) {
        int value = d[i];
        if (inInverse) value = (1-d[i]);            
        if (storageMethod==0) {
            score = (score << 1) |value;
        }else{
            if (storageMethod==1){
                if(value) score += ++streak; else streak = 0;
            }else{
                if(value) score ++;         
            }
        }
    };  
    return score;
}

coord_t theFittest(dna_t d, view_t v) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    const int colorMemStorageMethod = 2, colorMemBlockSize = 2, colorMemZeroThreshold = 0;
    const int useTrapMem = 0, trapMemStorageMethod = -1, trapMemBlockSize = -1;
    const int trapMemTopThreshold = -1, nDirBins = -1, nColorBins = -1;
    const int reorderMemStorageMethod = -1, reorderMemReverseThreshold = -1;
    const int reorderMemInverseThreshold = -1;
    // Definition of hashPrority: -1: no hash, 0:hash when 'look beyond' scores equal,
    // 1: hash replaces 'look beyond', 2: hash replaces 'trap finder' and 'look beyond'
    // 3: hash replaces everything ('color finder', 'trap finder' and 'look beyond')
    const int hashPrority = 2;
    int inReverse = reorderMemReverseThreshold != -1 && 
     (dnaLookup(d,92,8,reorderMemStorageMethod,0) >= reorderMemReverseThreshold);
    int inInverse = reorderMemInverseThreshold != -1 && 
     (dnaLookup(d,84,8,reorderMemStorageMethod,0) >= reorderMemInverseThreshold);
    int trapMemStart=N_COLORS*colorMemBlockSize;
    unsigned long hashCode=17;
    int moveUp=0;
    if (hashPrority>0){
        for(int x = -2; x <= 2; x++) {
            for(int y = -2; y <= 2; y++) {
                int color = v(x, y)+2;
                hashCode = hashCode*31+color;
            }
        }       
        unsigned long hashMemStart=N_COLORS*colorMemBlockSize;
        if (useTrapMem==1 && hashPrority<=1) hashMemStart+=nDirBins*nColorBins*trapMemBlockSize;
        if (hashPrority==3) hashMemStart=0;
        int hashMemPos = hashCode % (DNA_BITS-hashMemStart);
        moveUp = dnaLookup(d,hashMemStart+hashMemPos,1,0,inInverse);
    }

    int xBestScore=0, yBestScore=0;
    long bestScore=-1, weightMove, weightMove2, mainScore;
    for(int x = -1; x <= 1; x++) {
        if (x < 0) weightMove = 1000; // moving backward
        if (x== 0) weightMove = 10000; //moving vertical
        if (x > 0) weightMove = 100000; //moving forward
        for(int y = -1; y <= 1; y++) {          
            int color = v(x, y);
            if (inReverse) color = 15-v(x, y);
            if(color == OUT_OF_BOUNDS || (x==0&&y==0) ) continue;
            //when MoveUp=1 -> give move with highest y most points (hashScore=highest)
            //when MoveUp=0 -> give move with lowest y most points (hashScore=lowest)
            int hashScore = (y+2)*(2*moveUp-1)+4; 
            mainScore = dnaLookup(
              d,
              color*colorMemBlockSize,
              colorMemBlockSize,
              colorMemStorageMethod,
              inInverse
             );
            if (mainScore<colorMemZeroThreshold+1) {
                mainScore =  0; //not a suitable move because too low score
            }else{
                mainScore*= weightMove;
                //lookup trap likelihood
                int directionBin = 0;
                if (nDirBins==3) directionBin = x>0?y+1:-1;
                if (nDirBins==4) directionBin = x>0?y+2:0;
                // put 16 colors in nColorBins bins
                int colorBin = v(0,0)*nColorBins/N_COLORS; 
                if (inReverse) colorBin = (15-v(0,0))*nColorBins/N_COLORS; 
                colorBin = colorBin>(nColorBins-1)?(nColorBins-1):colorBin;
                if (useTrapMem && directionBin >= 0 &&
                 dnaLookup(
                   d,
                   trapMemStart+trapMemBlockSize*(nColorBins*directionBin+colorBin),
                   trapMemBlockSize,
                   trapMemStorageMethod,
                   0
                 )>=trapMemTopThreshold){
                  //suspect a trap therefore no sub score is added                  
                 }else{
                    if (hashPrority>=1){
                        mainScore+=hashScore;
                    } else{
                        // when equal score, use sub score by examining 5x5 box to rank moves
                        for(int x2 = x-1; x2 <= x+1; x2++){     
                            if (x2 < x) weightMove2 = 1; // moving backward
                            if (x2== x) weightMove2 = 10; //moving vertical
                            if (x2 > x) weightMove2 = 100; //moving forward
                            for(int y2 = x-1; y2 <= y+1; y2++){     
                                int color2 = v(x2, y2);
                                if (inReverse) color2 = 15-v(x2, y2);
                                if(color2 != OUT_OF_BOUNDS){
                                    long mainSubScore = dnaLookup(
                                      d,
                                      color2*colorMemBlockSize,
                                      colorMemBlockSize,
                                      colorMemStorageMethod,
                                      inInverse
                                    );
                                    if (mainSubScore>=colorMemZeroThreshold+1){
                                        mainScore+=mainSubScore*weightMove2;
                                    }
                                }
                            }
                        }
                    }               
                 }
            }
            if (hashPrority==2 || (useTrapMem<=0 && hashPrority>=1)) mainScore+=hashScore*10;
            if (hashPrority==3) mainScore=hashScore*weightMove;         

            if(mainScore > bestScore) {
                bestScore = mainScore;              
                xBestScore = x;
                yBestScore = y;
            }
        }
    }
    return{xBestScore,yBestScore};
}   

Pontuação: 922 (2K corridas)

Primeiras 50 pontuações (9 jogos marcaram apenas 1 ponto):

112.747 3 1 1.876.965 8 57 214.921 218.707 2.512.937 114.389 336.941 1 6.915 2 219.471 74.289 31 116 133.162 1 5 633.066 166.473 515.204 1 86.744 17.360 2 190.697 1 6 122 126.399 399.045 1 4.172.168 1 169.142 5.990 1

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.


Acho sua análise de armadilhas bastante interessante.

Você tentou outra função hash, como, por exemplo, armazenar os 25 valores de cores vistos como 12,5 palavras de 16 bits e usar um módulo? Não estou convencido de que a congruência com números primos dê uma melhor homogeneidade, mas não sou um grande matemático.

Além disso, você considerou adicionar um algoritmo de caminho? Parece ser um grande fator de melhoria, independentemente do genoma, uma vez que limitará os movimentos àqueles que testam a capacidade do genoma apenas por caminhos com maior probabilidade de levar a uma posição vencedora.

Kuroi, obrigado pelo seu feedback. Eu não tentei o xoring, pois não sou tão familiarizado com operações binárias em c ++. Suponho que você quer dizer 12,5 palavras de 8 bits? Você está usando xoring?
Ruut

Você pode olhar para o código dos meus "crentes duros" para ver que tipo de função de hash eu uso. Basicamente, pulo as células fora da pista e considero as cores na pista como partes de ordem alta e baixa de uma palavra de 16 bits. Todas essas palavras são acumuladas com XORs em um registro que é dividido pelo tamanho da tabela de hash. Desde que o valor de hash max (65535) seja muito maior que o tamanho da tabela (<100), o módulo tem um bom poder de espalhamento. Eu testei em um amplo conjunto de grades geradas aleatoriamente e parece ter uma boa homogeneidade.

6

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.


int dnarange(dna_t &d, int start, int len) {
    int res = 0;
    for(int i = start; i < start+len; i++) {
        res = (res << 1) | d[i];
    }
    return res;
}

int dnasum(dna_t &d, int start, int len) {
    int res = 0;
    for(int i = start; i < start+len; i++) {
        res += d[i];
    }
    return res;
}

int dnaweight(dna_t &d, int start) {
    return d[start] + d[start+1] + 2*d[start+2] + 2*d[start+3] + 3*d[start+4];
}

int trap_d [16] = {1,0,1,1,0,1,-1,1,-1,0,-1,-1,0,-1,1,-1}; //immutable
int nhood [10] = {1,0,1,1,1,-1,0,1,0,-1}; //immutable

coord_t pathfinder(dna_t d, view_t v) {
  int is_trap[16] = {0};
  int pos_or_weight[16] = {0};
  int u_weight = dnaweight(d, 80);
  for (int i = 0; i < 16; i++) {
    int status = dnarange(d, 5*i, 2);
    if (status == 1) {
      is_trap[i] = 1;
      pos_or_weight[i] = dnarange(d, 5*i + 2, 3);
    } else {
      pos_or_weight[i] = dnaweight(d, 5*i);
    }
  }
  int w_area[7][4] = {0};
  for (int j = 0; j < 7; j++) {
    w_area[j][3] = u_weight;
  }
  for (int i = 0; i < 3; i++) {
    w_area[0][i] = u_weight;
    w_area[6][i] = u_weight;
  }
  int d_coeff = dnaweight(d, 85);
  for (int i = 0; i < 3; i++) {
    for (int j = 1; j < 6; j++) {
      int p_or_w, color = v(i, j-3);
      if (color != OUT_OF_BOUNDS) {
    p_or_w = pos_or_weight[color];
      } else {
    p_or_w = 1000;
      }
      if (color != OUT_OF_BOUNDS && is_trap[color] && i+trap_d[2*p_or_w] >= 0) {
    w_area[j + trap_d[2*p_or_w + 1]][i + trap_d[2*p_or_w]] += d_coeff;
      } else {
    w_area[j][i] += p_or_w;
      }
    }
  }
  for (int i = 3; i >= 0; i--) {
    for (int j = 0; j < 7; j++) {
      int min_w = 1000;
      for (int k = std::max(0, j-1); k <= std::min(6, j+1); k++) {
    min_w = std::min(min_w, w_area[k][i + 1]);
      }
      w_area[j][i] += min_w;
    }
  }
  int speed = dnasum(d, 90, 5);
  w_area[2][0] += 2 + speed;
  w_area[4][0] += 2 + speed;
  int goal = dnaweight(d, 95);
  int min_w = 10000;
  int sec_w = 10000;
  int min_x, min_y, sec_x, sec_y, w;
  for (int i = 0; i < 5; i++) {
    w = w_area[nhood[2*i + 1] + 3][nhood[2*i]];
    if (w < min_w) {
      sec_w = min_w;
      sec_x = min_x;
      sec_y = min_y;
      min_w = w;
      min_x = nhood[2*i];
      min_y = nhood[2*i + 1];
    } else if (w < sec_w) {
      sec_w = w;
      sec_x = nhood[2*i];
      sec_y = nhood[2*i + 1];
    }
  }
  if (min_w > goal) {
    int r = v.rng.rint(5);
    return {nhood[2*r], nhood[2*r+1]};
  } else if (sec_w <= goal && v.rng.rint(100) < 2*speed) {
    return {sec_x, sec_y};
  }
  return {min_x, min_y};
}

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. Se ab = 01, a cor é uma armadilha e cdecodifica sua direção (oito possibilidades). Se ab ≠ 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).


Executei sua implementação no meu computador. Demorou algumas horas. Obtém uma pontuação média de 7.848433940863856 pontos. pastebin.com/d50GcwnK
Jakube

@Jakube Muito obrigado! Isso é muito pior do que eu esperava, mas agora que olho para o código novamente, vejo vários bugs e outras esquisitices. Vou tentar portar isso para C ++ mais tarde, para que eu possa analisá-lo.
Zgarb 21/01

5

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.

// get striped color score, 6 bits per color. should be
// resistant to getting erased by a crossover
void mapColorsBitwise(dna_t &d, int* color_array) {
    for (int i=0; i<N_COLORS; i++) {
        int score = 0;
        for (int j=0; j<6; j++) {
            score = (score<<1) | d[ j*N_COLORS + i ];
        }
        color_array[i] = score;
    }
}

// label for the lookup tables
enum direction_lut {
    UP_RIGHT=0, RIGHT, DOWN_RIGHT
};

// movement coord_t's to correspond to a direction
static const coord_t direction_lut[3] = {
    { 1, -1 }, { 1, 0 }, { 1, 1 }
};

// indexes into the arrays to denote what should be summed
// for each direction.
static const int sum_lut[3][6] = {
    { 3, 4, 8, 8, 9, 14 }, { 9, 13, 13, 14, 14, 19 },
    { 14, 18, 18, 19, 23, 24 }
};

coord_t lookAheadPlayer(dna_t d, view_t v) {
    int scoreArray[25] = { 0 };
    int colorScores[N_COLORS] = { };

    // Get color mapping for this iteration
    mapColorsBitwise(d, colorScores);

    for (int y=-2; y<=2; y++) {
        for (int x=0; x<=2; x++) {
            // Get the scores for our whole field of view
            color_t color = v(x,y);
            if (color != OUT_OF_BOUNDS)
                scoreArray[ (x+2)+((y+2)*5) ] += colorScores[color];
        }
    }

    // get the best move by summing all of the array indices for a particular
    // direction
    int best = RIGHT;
    int bestScore = 0;
    for (int dir=UP_RIGHT; dir<=DOWN_RIGHT; dir++) {
        if (v(direction_lut[dir].x, direction_lut[dir].y) == OUT_OF_BOUNDS)
            continue;

        int score = 0;
        for (int i=0; i<6; i++) {
            score += scoreArray[ sum_lut[dir][i] ];
        }

        if (score > bestScore) {
            bestScore = score;
            best = dir;
        }
    }

    return direction_lut[best];
}

5

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 1s. Inicialmente, 111111era bom e 000000ruim, 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 uso 9 - (#1 - 3)^2para 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.

coord_t SlowAndSteadyPlayer(dna_t d, view_t v) {
    const int chunklen = 6;
    int color_scores[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    for(int i=0; i<16; i++){ //count ones
        for(int j=0; j<chunklen; j++){
            color_scores[i] += d[i*chunklen + j];
        }
    }

    int moves[7][2] = {
        {-1,1}, {0,1}, {1,1},
                       {1,0},
        {-1,-1},{1,-1},{-1,-1}
    };
    int movescores[7];
    int smax = -1;
    int nmax = 0;
    int best_moves[7];
    for(int m=0; m<7; m++){ //compute the score for each move
        int temp_color = v(moves[m][0], moves[m][1]);
        if(temp_color == OUT_OF_BOUNDS){
            movescores[m] = 0;
            continue;
        }
        int dir_bias[3] = {1,3,6};
        int temp_score = 9-(color_scores[temp_color]-3)*(color_scores[temp_color]-3) + dir_bias[moves[m][0]+1];
        movescores[m] = temp_score;

        if(temp_score > smax) {
            smax = temp_score;
            nmax = 0;
        }
        if(temp_score == smax) best_moves[nmax++] = m;
    }

    int best_chosen = v.rng.rint(nmax);
    return {moves[best_moves[best_chosen]][0], moves[best_moves[best_chosen]][1]};
}

E uma amostra de pontuação em 100 placas

Scores: 5 4 13028 1 1 101 2 24 1 21 1 4 2 44 1 1 24 8 2 5 1 13 10 71 2 19528 6 1 69 74587 1 1 3 138 8 4 1 1 17 23 1 2 2 50 7 7 710 6 231 1 4 3 263 4 1 6 7 20 24 11 1 25 1 63 14 1 2 2 1 27 9 7 1 7 31 20 2 17 8 176 3 1 10 13 3 142 1 9 768 64 6837 49 1 9 3 15 32 10 42 8

Escore médio geométrico: 9.76557


A pontuação que você mencionou para um quadro está usando a taxa de mutação padrão ou o seu valor ajustado?
precisa saber é o seguinte

"meus bichos atingem a linha de chegada e marcam> 1 em 3/4 dos casos". Desejo que a métrica de pontuação tenha recompensado isso
Sparr

5

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)

using ppcggacscontroller;
using System.Linq;
using System;

public class WeightChooser
{
    public static ppcggacscontroller.Program.Coord[] cspcoords = new[] {
            new Program.Coord(1, -1),
            new Program.Coord(1, 0),
            new Program.Coord(1, 1),
        };

    const int dnaBits = 4;

    public static void move(GameLogic.IView v, GameLogic.IGenome g, Random rnd, out int ox, out int oy)
    {
        var gcrds = cspcoords.Where(c => viewVal(v, c) > -1)
            .OrderByDescending(p => getBitsSet(g, viewVal(v, p)))
            .ThenByDescending(gt => weight(v, g, gt));

        Program.Coord nextMove = gcrds.First();
        ox = nextMove.x;
        oy = nextMove.y;
    }

    private static uint getBitsSet(GameLogic.IGenome g, int vVal)
    {
        uint i = g.cutOutInt(dnaBits * vVal, dnaBits);
        i = i - ((i >> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
        return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
    }

    private static int viewVal(GameLogic.IView v, Program.Coord c)
    {
        return v[c.x, c.y];
    }

    private static double weight(GameLogic.IView v, GameLogic.IGenome g, Program.Coord toGo)
    {
        double w = 0;

        int y = (toGo.y + v.yd) - 1;
        int i = 0;
        for (; i <= 2; i++)
        {
            int field = v[toGo.x + 1, (y + i) - v.yd];
            if (field > -1)
                w += getBitsSet(g, field);
        }

        return w / i;
    }
}

Scores: 32, 56103, 1361, 3351446, 33027, 23618, 22481, 1172713, 1, 3, 1, 1, 1, 2 88584, 106357, 1, 1232, 1, 1651280, 16690, 1, 1, 23732, 207554, 53, 69424, 1, 1,  79361, 1, 1, 51813, 229624, 25099, 2, 1, 234239, 362531, 1, 1, 19, 7295, 1, 7, 2, 196672, 1654208, 73453, 1, 23082, 1, 8, 5, 1685018, 4, 20, 1, 1, 1, 1, 1, 144 671, 122309, 10, 94752, 100895, 1, 54787, 54315, 252911, 79277, 1159, 241927, 94 347, 1, 318372, 37793, 1, 1, 1345310, 18934, 169700, 1, 1, 3, 186740, 83018, 121 758, 1, 358, 1935741, 88, 1, 1, 1, 1, 7, 21, 51144, 2, 1, 267638, 1, 1, 3, 1, 1,  1, 1, 674080, 47211, 8879, 7, 222766, 67214, 2, 89, 21038, 178463, 92846, 3, 14 0836, 1, 1, 111927, 1, 92165, 1, 192394, 1, 1, 2563722, 1, 42648, 1, 16, 1, 1, 2 85665, 1, 212653, 1, 4, 20513, 3, 135118, 13161, 2, 57, 78355, 3, 3, 44674, 8, 1 , 226472, 1, 1, 31588, 19619, 1, 2931870, 60814, 1, 1, 33867, 60740, 20558, 1, 1 5, 3, 5, 1, 1, 1, 60737, 450636, 468362, 1, 1, 347193, 91248, 551642, 1, 427215,  1, 57859, 17, 15, 66577, 24192, 1, 63560, 6568, 40279, 68216, 23098, 180732, 1,  1, 3041253, 1, 253488, 60535, 1, 1, 150838, 7361, 72855, 290699, 104644, 1, 763 01, 378, 1, 89220, 1, 262257, 2, 2, 1, 117, 105478, 33, 1, 65210, 1, 117588, 1, 1, 24320, 12, 3714568, 81152, 1, 1, 10125, 2, 1, 22, 1, 45201, 1, 1, 10518, 1, 1 , 1, 1, 34, 210021, 1, 1, 1, 65641, 6, 72, 1, 7, 2, 161578, 1, 1, 38378, 1, 4113 741, 1, 34450, 244212, 127660, 1, 256885, 46, 2, 1, 1, 103532, 1, 503965, 114774 , 52450, 124165, 73476, 50250, 1, 3, 3755352, 24928, 1, 1, 51, 11, 1, 210580, 1,  62375, 1, 1, 92745, 341232, 167675, 86, 242, 293710, 454841, 1, 49840, 4456758,  121378, 145323, 74904, 5048, 25459, 1, 57, 116999, 1, 1, 76074, 111447, 95706, 1, 1, 52631, 166756, 2159474, 161216, 1, 2, 3, 11904, 1, 22050, 6, 1, 1, 1, 41, 48908, 6, 80878, 28125, 28, 160516, 1, 4, 1, 8, 1, 1, 7, 362724, 1, 397193, 1, 2 5, 1, 59926, 3, 74548, 2320284, 470189, 1, 108, 1, 1, 16, 1, 496013, 1, 1, 1, 1,  107758, 1, 284144, 146728, 1, 70769, 94215, 1, 1, 9961, 97300, 7, 1, 76263, 1, 27, 294046, 40, 8, 2, 1, 57796, 2, 79800, 1043488, 470547, 1, 1, 1, 6, 69666, 8,  1, 1, 344011, 205325, 3963186, 1141527, 61598, 446029, 1, 1, 1, 1, 625247, 1877 92, 136391, 1, 72519, 1, 141168, 412, 98491, 103995, 297052, 1, 1, 1, 1, 3, 17, 9, 62899, 5, 47810, 254, 26789, 2, 1, 1, 3, 10361, 19615, 40430, 17288, 3, 71831 , 41374, 1, 91317, 409526, 1, 184305, 1, 192552, 3, 3587674, 39, 13, 134500, 41,  42, 672, 559835, 9, 39004, 51452, 1, 1, 12293, 11544, 265766, 8590, 1, 8632, 1,  1, 61849, 35155, 1, 74798, 72773, 1, 89, 37, 4, 4405882, 1, 99, 44397, 5, 4, 6,  1, 1, 1, 515818, 78383, 20, 127829, 1824801, 157, 1, 1, 268561, 19, 2, 230922, 1, 103, 98146, 5029789, 304324, 1, 5, 60516, 1, 139, 28982, 7, 20755, 187083, 1,  1, 143811, 37697, 1, 1, 269819, 83, 1, 202860, 13793, 16438, 113432, 1, 1, 2, 5 134384, 29, 84135, 39035, 2, 125, 1, 30, 129771, 41982, 13548, 61, 1, 2, 1, 82, 102, 2, 105581, 210399, 291204, 3012324, 1, 84763, 1, 1, 442067, 2, 1, 1, 1, 116 , 1, 3, 3, 56, 208807, 1, 2, 1, 14, 29, 31286, 1, 1, 162358, 28856, 46898, 1, 16 2698, 1, 1, 1, 65, 1, 1, 234566, 6, 1, 1, 128, 124, 2167692, 181946, 29, 1, 1, 1 , 1, 17, 162550, 179588, 4, 226480, 28, 1, 158512, 35084, 1, 26160, 17566, 1, 81 826, 2, 33, 1, 1, 11, 1, 230113, 1, 1, 1, 24405, 17, 1, 2, 1, 162365, 2, 1, 1, 8 5225, 1, 15016, 51509, 1, 5, 1, 93, 13, 59, 24548, 1, 3, 2, 2, 1, 64424, 1, 1, 4 , 1, 1, 1, 2, 267115, 139478, 52653, 96225, 1, 1, 35768, 3, 1, 1, 3280017, 8, 80 014, 43095, 112102, 1, 1, 1, 79594, 5, 1, 1, 4, 455714, 19, 15, 1, 233760, 55850 5, 2, 2, 1, 63672, 1, 3732951, 1, 135858, 134256, 452456, 151573, 79057, 638215,  88820, 1, 1, 76517, 13, 314006, 5, 1, 17704, 1, 79589, 1, 18371, 530793, 59020,  1, 1, 1, 4, 1, 1, 1, 71735, 1, 1, 1, 1, 1, 37894, 1, 2, 24054, 1, 8, 26471, 34,  1, 48033, 5, 3, 1, 25, 101, 1, 1, 5, 1, 1, 1, 97521, 1, 682817, 286486, 5, 1472 4, 1, 7805226, 6, 1, 1, 1, 7, 2, 1, 1, 1, 25, 233330, 1, 20899, 3417337, 92793, 23, 80821, 1, 1, 115948, 264191, 3, 79809, 1, 2, 59531, 2, 1, 1, 28684, 97, 1, 2 69433, 98769, 1, 76608, 138124, 1, 1, 325554, 122567, 1, 1, 3, 689604, 4, 85823,  66911, 138091, 169416, 21430, 1, 2, 486654, 108446, 93072, 1, 67907, 4, 1, 1, 5 2260, 67867, 210496, 25157, 1, 1, 1, 5477, 2, 2, 11907, 106, 48404, 1, 1, 1, 787 11, 190304, 112025, 1, 9313, 143055, 40189, 315537, 157581, 70714, 6, 180600, 38 594, 103658, 59444, 7, 31575, 1, 1, 581388, 370430, 1, 114446, 1, 1, 2, 3968, 1,  1, 1, 1, 1, 4523411, 1, 1, 270442, 1, 59, 235631, 3, 110196, 9, 1, 93724, 1, 22 917, 1, 6, 1, 2350266, 1, 1, 20, 4686858, 31, 1, 240180, 10, 470592, 3, 61051, 1 45372, 2831, 64052, 10, 120652, 255971, 479239, 1, 387659, 1, 1, 1, 378379, 7, 3 3218, 55914, 1, 1, 1667456, 6, 2, 74428, 3, 2, 1, 121582, 121274, 19651, 59899, 1, 11, 406670, 137835, 100269, 2, 164361, 98762, 44311, 25817, 178053, 31576, 1,  8, 2539307, 121430, 1, 41001, 1, 4, 1, 116258, 91101, 1, 126857, 1, 8, 49503, 1 , 489979, 12, 500332, 1, 52, 4, 8786, 4, 4878652, 12354, 27480, 89115, 87560, 11 793, 5, 1, 4702325, 301188, 1, 1, 1, 1, 1, 416520, 49357, 230103, 24497, 1, 3, 2 , 57366, 183021, 1, 1, 1, 1, 1, 2, 2, 2546229, 1, 2, 38665, 1, 6903, 1, 89519, 9 5119, 64879, 1, 1, 160380, 474336, 3107, 1, 7, 29099, 28667, 3, 196933, 35979, 1 2924, 7, 1, 99885, 6, 1, 1, 1, 7, 1, 1, 1, 1, 65727, 1, 1, 1, 1, 2108110, 3, 107 811, 23818, 701905, 1, 156034, 32, 1, 29, 143548, 1, 67665, 4612762, 1, 3, 20, 1 , 1, 9, 28543, 1, 1, 1, 30978, 9, 1, 19504, 79412, 15375, 763265, 1, 352373, 193 045, 1, 4570217, 9, 1, 6, 29180, 90030, 1, 1, 1, 1, 1, 93, 1, 100889, 1, 1, 37, 15, 17, 1, 81184, 1, 2, 272831, 1, 137, 1, 9, 42874, 679183, 1, 350027, 12, 1, 2 , 1, 26408, 1, 11182, 1, 30, 139590, 7, 3, 1, 1, 34729, 1, 2, 1, 1, 50343, 66873 , 3891, 1, 148952, 1, 1, 22322, 104176, 1, 3, 20549, 140266, 37827, 30504, 17, 6 8588, 120195, 1, 123353, 2, 64301, 11, 1, 109867, 4, 1, 1, 1, 28671, 1, 50963, 5 4584, 1, 1, 1, 33, 1, 381918, 1, 265823, 4771840, 155179, 314, 134086, 1, 1, 30,  1, 2, 1102665, 18, 132243, 3861, 1, 1, 208906, 60112, 1, 1, 1, 31273, 551, 3490 0, 2, 43606, 1, 1, 1, 1, 5, 2, 88342, 2, 1, 19, 3, 1, 1, 1, 1, 28507, 1, 491467,  1, 1, 22, 1, 1, 1, 1, 9345, 9, 18, 84343, 1, 2, 1, 18, 36816, 1, 1, 513028, 287 88, 5037383, 721932, 170292, 108942, 539115, 1, 575676, 20, 1, 31698, 99797, 205 21, 380986, 1, 1, 14, 2, 1, 201100, 30, 1, 119484, 1, 1, 1, 1, 2214252, 3, 4, 18 179, 9, 4, 542150, 1, 6, 157, 3182099, 4, 1, 1, 6140, 3339847, 498283, 52523, 1,  1, 1, 1, 1, 202054, 263324, 1, 6, 2, 1, 2, 72357, 12, 5, 66, 4, 7368, 1, 30706,  61936, 3945270, 138991, 1, 68247, 1, 1, 30482, 35326, 1, 1, 9, 1, 148, 1, 46985 , 1, 4325093, 1, 1, 2880384, 65173, 1, 56581, 179178, 372369, 56187, 3, 12, 8, 4 00743, 3, 28658, 1, 1, 9, 1, 4, 2, 34357, 1, 42596, 68840, 2, 62638, 158027, 617 34, 71263, 1, 1, 9, 1, 6830309, 3, 1, 1, 157253, 129837, 9, 5008187, 48499, 5981 3, 1, 40320, 233893, 5, 1383, 7732178, 16, 1, 13, 5686145, 84554, 1, 79442, 1, 1 , 256812, 127818, 31, 226113, 1, 4, 1, 1, 4506163, 1, 4, 1, 40176, 19107, 205, 2 7, 1, 448999, 1, 1, 2750, 62723, 1, 12, 1, 1, 79881, 1, 48, 13, 4, 1, 28765, 1, 33, 291330, 30817, 2, 1, 1, 1, 4170949, 16, 1, 1, 118781, 10473, 520797, 1, 8, 1 , 80215, 1, 21759, 5143209, 79141, 40229, 1, 17403, 71680, 1115694, 1, 1, 1, 10,  1, 77149, 382712, 1, 11, 84891, 47633, 1, 2, 39037, 1, 213148, 1607280, 127674,  1, 333207, 1, 78901, 1, 16203, 87580, 1, 1565571, 537902, 53000, 15, 1, 2, 1, 2 13127, 1, 338634, 2469990, 469479, 9519, 51083, 1, 42082, 33179, 1, 1, 32444, 3,  1, 201642, 99724, 377, 1, 2, 1, 36919, 1, 322707, 2, 164765, 82516, 1, 5274643,  1, 36421, 1, 8, 1, 117856, 1, 1, 493342, 1, 36289, 7, 1, 62, 2, 1, 38533, 1, 68 , 45754, 9, 102015, 312941, 1, 99 
Final score is 220.826222910756

5

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:

faixa de amostra

Você provavelmente precisará aumentar o zoom para ver qualquer coisa, então aqui está apenas a primeira metade:

meia pista (sem trocadilhos)

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:

  • verde: vitória
  • amarelo: loop infinito
  • marrom: batendo na parede
  • vermelho: acidente infeliz

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ê).

  • parede: quadrado
  • teleporter: estrela 4 ramificada
  • detector de armadilha: octogon pequeno

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).

  • parede: quadrado
  • detector de armadilha: octogon
  • armadilha: X

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 :).


11
Você poderia compartilhar sua ferramenta? Não vejo código nem link na sua resposta.
Franky

5

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.

class SkyWalker(Player):
    def __init__(self):
        Player.__init__(self)
        self.coords = [#Coordinate(-1,-1),
                       #Coordinate( 0,-1),
                       Coordinate( 1, 0),
                       Coordinate( 1,-1),
                       #Coordinate(-1, 0),
                       #Coordinate( 0, 0),
                       #Coordinate(-1, 1),
                       #Coordinate( 0, 1),
                       Coordinate( 1, 1)]

        self.n_moves = len(self.coords)

    def visionToMove(self, x, y):
        x = x - 2
        y = y - 2

        return (x, y)

    def trapToMove(self, x, y, offx, offy):
        x = x - 2 + (offx % 3) - 1
        y = y - 2 + (offy % 3) - 1
        return (x, y)

    def isNeighbour(self, x1, y1, x2, y2):
        if (x1 == x2) or (x1+1 == x2) or (x2+1 == x1):
            if (y1 == y2) or (y1+1 == y2) or (y2+1 == y1):
                return True
        return False

    def calcMove(self, donots, never, up):
        forwards = {(1, 0): 0, (1, 1): 0, (1, -1): 0, (0, 1): 10, (0, -1): 10}

        for key in forwards:
            if key in never:
                forwards[key] = 100
            for x in donots:
                if (key[0] == x[0]) and (key[1] == x[1]):
                    forwards[key] = 20

        min_value = min(forwards.itervalues())
        min_keys = [k for k in forwards if forwards[k] == min_value]

        return random.choice(min_keys)

    def turn(self):
        trap1 = self.bit_chunk(0, 4)
        trap1_offsetx = self.bit_chunk(4, 2)
        trap1_offsety = self.bit_chunk(6, 2)
        trap2 = self.bit_chunk(8, 4)
        trap2_offsetx = self.bit_chunk(12, 2)
        trap2_offsety = self.bit_chunk(14, 2)
        wall1 = self.bit_chunk(16, 4)
        wall2 = self.bit_chunk(20, 4)
        tel1 = self.bit_chunk(24, 4)
        tel1_good = self.bit_chunk(28, 3)
        tel2 = self.bit_chunk(31, 4)
        tel2_good = self.bit_chunk(35, 3)
        tel3 = self.bit_chunk(38, 4)
        tel3_good = self.bit_chunk(42, 3)
        tel4 = self.bit_chunk(45, 4)
        tel4_good = self.bit_chunk(49, 3)
        up = self.bit_at(100)

        donots = []
        never = []

        for y in range(0, 5):
            for x in range(0, 5):
                c = self.vision[y][x]
                if (c == -1):
                    never += self.visionToMove(x, y),
                elif (c == trap1):
                    donots += self.trapToMove(x, y, trap1_offsetx, trap1_offsety),
                elif (c == trap2):
                    donots += self.trapToMove(x, y, trap2_offsetx, trap2_offsety),
                elif (c == wall1):
                    donots += self.visionToMove(x, y),
                elif (c == wall2):
                    donots += self.visionToMove(x, y),
                elif (c == tel1):
                    if (tel1_good > 3):
                        donots += self.visionToMove(x, y),
                elif (c == tel2):
                    if (tel2_good > 3):
                        donots += self.visionToMove(x, y),
                elif (c == tel3):
                    if (tel3_good > 3):
                        donots += self.visionToMove(x, y),
                elif (c == tel4):
                    if (tel4_good > 3):
                        donots += self.visionToMove(x, y),

        coord = self.calcMove(donots, never, up)

        return Coordinate(coord[0], coord[1])

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.

  • 2 x 8 bits para armadilhas, os 4 primeiros bits são o número da cor, os outros 4 são o deslocamento
  • 2 x 4 bits para paredes, apenas a cor
  • 4 x 7 bits para teleportadores, novamente 4 bits para a cor, 3 para decidir bom ou ruim

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 é:

  1. frente
  2. Para cima ou para baixo
  3. para cima ou para baixo
  4. para trás

Se dois campos de uma categoria são aplicáveis, um é escolhido aleatoriamente.

Resultados

Individual scores: [192, 53116, 5, 1649, 49, 2737, 35, 5836, 3, 10173, 4604, 22456, 21331, 445, 419, 2, 1, 90, 25842, 2, 712, 4, 1, 14, 35159, 13, 5938, 670, 78, 455, 45, 18, 6, 20095, 1784, 2, 11, 307853, 58171, 348, 2, 4, 190, 7, 29392, 15, 1158, 24549, 7409, 1]
On average, your bot got 231.34522696 points

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.


Eu também tento armazenar cores, mas no final concluí que não funciona porque você receberá duplas. Por exemplo, se self.bit_chunk(16, 4)e self.bit_chunk(20, 4)tiver o valor, 0010você efetivamente armazenou apenas informações sobre uma das duas armadilhas.
Ruut

Eu precisava adicionar recuo em uma linha para que isso funcionasse - acho que ele se perdeu durante a cópia e a colagem. Também o adicionei ao seu código aqui agora.
Trichoplax

Para qualquer outra pessoa que queira executar isso: É executado no python 2 e pode ser executado no python 3 alterando a ocorrência única de itervaluespara values.
Trichoplax

Obtive os seguintes resultados: [6155, 133, 21, 12194, 8824, 3, 3171, 112, 111425, 3026, 1303, 9130, 2680, 212, 28, 753, 2923, 1, 1, 4140, 107, 1256 , 90, 11, 104, 1538, 63, 917, 8, 1, 709, 11, 304, 212, 2, 43, 5, 4, 206, 8259, 75, 28, 7, 1, 11, 5, 1 , 1244, 1398, 13] Média geométrica 122.9220309940335
trichoplax

Parece que precisamos executar muito mais de 50 jogos para obter uma pontuação confiável.
Trichoplax

3

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.

class NeighborsOfNeighbors(Player):
  def __init__(self):
    Player.__init__(self)
    self.coords = [ Coordinate( 1, 0),
                    Coordinate( 1,-1),
                    Coordinate( 1, 1)
                    ]

  def turn(self):
    scores=[self.score(c.x,c.y)+0.5*self.adjacentScore(c.x,c.y) if self.vision_at(c.x,c.y)>-1 else None for c in self.coords ]
    max_score = max(scores)
    return random.choice( [c for s,c in zip(scores,self.coords) if s==max_score] )

  def adjacentScore(self,x,y):
    adjacent = [(x+1,y)]
    if self.vision_at(x,y+1)>-1:
      adjacent+=[(x+1,y+1)]
    if self.vision_at(x,y-1)>-1:
      adjacent+=[(x+1,y-1)]
    adjscores=[self.score(a,b) for a,b in adjacent]
    return sum(adjscores)/float(len(adjscores))

  def score(self,x,y):
    return -1 if self.vision_at(x,y) == -1 else self.bit_chunk(6*self.vision_at(x,y),6)

Faltava um recuo em uma linha. Eu acho que ele se perdeu ao colar. Eu adicionei.
Trichoplax

Correndo no python 3, queixou-se de comparar None no cálculo de max (scores). Então mudei else Nonepara else 0na 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).
Trichoplax

Correndo no python 3, obtive as seguintes pontuações para esta resposta: [1, 13085, 360102, 1, 73713, 1, 189, 1, 1, 193613, 34, 195718, 199, 8, 1, 1, 60006, 66453, 2, 2, 53, 425206, 1, 4, 1, 1, 16, 153556, 1, 18134, 35655, 1, 4211684, 2, 1, 26451, 8, 1, 724635, 69242, 38469, 796553, 111340, 1, 25, 40017, 76064, 66478, 209365, 3925393]
trichoplax

A média geométrica de 428,3750848244933
Trichoplax

2

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.

import java.awt.*;
import java.util.Map;

public class ROUS extends Player{

    private static final int NUMBER_OF_GENES = 33;
    private static final int GENE_SIZE = 3;
    private static final Point[] coords = new Point[]{
        new Point(-1, -1),
        new Point(-1, 0),
        new Point(-1, 1),
        new Point(0, -1),
        new Point(0, 1),
        new Point(1, -1),
        new Point(1, 0),
        new Point(1, 1)
    };

    public Point takeTurn(String dna, Map<Point, Integer> vision){
        Point[] table = decode(dna);
        int hash = hash(vision);
        return table[hash];
    }

    private int hash(Map<Point, Integer> surroundings) {
        return Math.abs(surroundings.hashCode()) % NUMBER_OF_GENES;
    }

    private Point[] decode(String dna) {
        Point[] result = new Point[NUMBER_OF_GENES];

        for (int i = 0; i < NUMBER_OF_GENES; i++){
            int p = Integer.parseInt(dna.substring(i * GENE_SIZE, (i + 1) * GENE_SIZE), 2);
            int x;
            int y;

            result[i] = coords[p];
        }
        return result;
    }
}

11
O controlador Java está funcionando agora.
Martin Ender

3
A princípio, pensei que você estivesse prestando uma homenagem à Rússia antiga, mas parece que foi para Rob Reiner.

A pontuação mínima possível é 1
trichoplax

@trichoplax ... falhar o controlador ...
TheNumberOne

Ah, entendo - então isso acontece com bastante frequência e você não pode chegar ao final de uma corrida?
precisa saber é o seguinte

2

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.

coord_t colorTileRanker(dna_t d, view_t v) {
    const int COLOR_OFFSET = 0; // scores for each color (4 bits each)
    const int SELF_MUL_OFFSET = 96; // 2 bits for self-color multiplier
    const int MOVE_MUL_OFFSET = 98; // 2 bits for move-forward multiplier

    static const int gray2[4] = {0, 1, 3, 2};
    static const int gray3[8] = {0, 1, 3, 2, 7, 6, 4, 5};

    // bias factor table
    const int factorTable[4] = {0, 1, 2, 1};

    const int selfMul = factorTable[gray2[dnaRange(d, SELF_MUL_OFFSET, 2)]]*2 + 9;
    const int moveMul = factorTable[gray2[dnaRange(d, MOVE_MUL_OFFSET, 2)]] + 1;

    // scoring table for the color scores
    static const int scoreValue[8] = {0, 1, 2, 3, 4, 3, 2, 1};

    std::vector<coord_t> bestMoves;
    int bestScore = 0;

    for (int x = -1; x <= 1; x++) {
        for (int y = -1; y <= -1; y++) {
            const int color = v(x, y);
            if ((x || y) && (color >= 0)) {
                int score = 0;

                // score for the square itself
                score += selfMul*(scoreValue[gray3[dnaRange(d, COLOR_OFFSET + color*3, 3)]] - 2);

                // score for making forward progress;
                score += moveMul*(x + 1);

                // score for the resulting square's surrounding tiles
                for (int a = -1; a <= 1; a++) {
                    for (int b = -1; b <= 1; b++) {
                        const int color2 = v(x + a, y + b);
                        if (color2 >= 0) {
                            score += scoreValue[gray3[dnaRange(d, COLOR_OFFSET + color2*3, 3)]] - 2;
                        }
                    }
                }

                if (score > bestScore) {
                    bestMoves.clear();
                    bestScore = score;
                }
                if (score >= bestScore) {
                    bestMoves.push_back({x, y});
                }
            }
        }
    }

    if (bestMoves.empty()) {
        return {v.rng.rint(2), v.rng.rint(3) - 1};
    }
    return bestMoves[v.rng.rint(bestMoves.size())];
}

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. :)


11
Usar o código cinza é uma idéia de cinza! ;)
matovitch 26/01

11
+1 para códigos Gray. No entanto, um genoma totalmente resistente a mutações prejudicará bastante a diversidade. E entre 20.000 pontos nem é o máximo que você pode alcançar. Se algum rato evolui a capacidade de percorrer a pista a partir de qualquer possível local de partida, torna-se imortal e adquire uma enorme pontuação de condicionamento físico. Seu genoma domina rapidamente, levando a uma população de quase 50 mil ratos e a uma pontuação de alguns milhões.

2

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:

  • A pontuação da cor do destino (depende do DNA, como no colorScorePlayer)
  • A pontuação de avançar (depende do DNA)
  • A pontuação máxima de avançar um passo a partir do destino (multiplicado por um fator armazenado no dna)

Como em outras respostas, a pontuação depende muito do número de 1 pontuação retornada.

#define CHUNKSIZE 5 //We have 20 values so 5 bits/value
#define MAXVALUE 32 //2^CHUNKSIZE
#define AVGVALUE MAXVALUE/2

#define DNASEGMENT(dna, i) dnarange(dna, i*CHUNKSIZE, CHUNKSIZE)
#define DNA_COLOR 0
#define DNA_FORWARD 16
#define DNA_LOOKAHEAD 17

//Get the score for a specific move
int calcscore(dna_t dna, view_t view, int x, int y, bool final){
  if (view(x,y) == OUT_OF_BOUNDS){
    //We cant go there
    return -MAXVALUE;
  }
  //The score of the color
  int s = DNASEGMENT(dna, DNA_COLOR+view(x,y))-AVGVALUE;
  //The score of going forward
  s += x*DNASEGMENT(dna, DNA_FORWARD);

  //Get the children or not
  if (!final){
    int max=-MAXVALUE;
    int v;
    //Get the maximum score of the children
    for (int i=-1; i<2; ++i){
        v = calcscore(dna, view, x+1, y+i, true);
        if (v>max){
            max=v;
        }
    }
    //Apply dna factor to the childs score
    s += (max * DNASEGMENT(dna, DNA_LOOKAHEAD))/AVGVALUE;
  }
  return s;
}

coord_t TripleScore(dna_t dna, view_t view) {
  int maxscore = -100;
  int score;
  coord_t choices[5]; //Maximum 5 possible movements
  int maxchoices = 0;
  int zeroscore = calcscore(dna, view, 0, 0, false);

  //Go over all possible moves and keep a list of the highest scores
  for (int x=0; x<2; ++x){
    for (int y=-1; y<2; ++y){
        if (x | y){
            score = calcscore(dna, view, x, y, false);
            if (score > maxscore){
                maxscore = score;
                choices[0] = {x, y};
                maxchoices = 1;
            }else if (score == maxscore){
                choices[maxchoices++] = {x, y};
            }
        }
    }
    if (!x && maxscore <= zeroscore){
        //I will NOT bounce!
        maxscore = -100;
    }
  }

  return choices[view.rng.rint(maxchoices)];
}

2

Ruby - ProbabilisticScorePlayer

class ProbabilisticScorePlayer < Player
    Here = Vector2D.new( 0, 0)
    Forward = Vector2D.new( 1, 0)
    Right = Vector2D.new( 0, 1)
    Left = Vector2D.new( 0,-1)

    def vision_at(vec2d)
        v = @vision[vec2d.x+2][vec2d.y+2]
        v==-1?nil:v
    end

    def turn
        coords = [Forward]
        [Here,Forward].each{|x|
            [Here,Right,Left].each{|y|
                c = x+y
                if x!=y && vision_at c > -1
                  coords.push c if bit_at(vision_at c)==1
                  coords.push c if bit_at(vision_at(c+Forward)+16)==1
                  coords.push c if bit_at(vision_at(c+Right)+32)==1
                  coords.push c if bit_at(vision_at(c+Left)+48)==1
                  coords.push c if bit_at(vision_at(c+Forward+Right)+64)==1
                  coords.push c if bit_at(vision_at(c+Forward+Left)+80)==1
                end
            }
        }
        coords.sample(random: @rng)
    end
end

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.


+1 para originalidade. Que tipo de pontuação você obteve?

Nunca realmente testei ainda ...
MegaTom 30/01

Você esqueceu de dar cum valor inicial? Não parece ser definido quando você o usa no primeiro if.
Martin Ender

@ MartinBüttner sim, eu esqueci. Vou consertar isso agora.
MegaTom 02/02

Não conheço Ruby tão bem, mas seu código não é executado no Ruby2.1.5. coordsnão é uma lista, você usa, em &&vez de, ande 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?

2

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.

Scores: 6, 81533, 1648026, 14, 5, 38841, 1, 76023, 115162, 3355130, 65759, 59, 4, 235023, 1, 1, 1, 3, 2, 1, 1, 14, 50, 1, 306429, 68, 3, 35140, 2, 1, 196719, 162703, 1, 1, 50, 78233, 5, 5, 5209, 1, 2, 60237, 1, 14, 19710, 1528620, 79680, 33441, 58, 1, 4, 45, 105227, 11, 4, 40797, 2, 22594, 9, 2192458, 1954, 294950, 2793185, 4, 1, 1, 112900, 30864, 23839, 19330, 134178, 107920, 5, 122894, 1, 1, 2721770, 8, 175694, 25235, 1, 3109568, 4, 11529, 1, 8766, 319753, 5949, 1, 1856027, 19752, 3, 99071, 67, 198153, 18, 332175, 8, 1524511, 1, 159124, 1, 1917181, 2, 1, 10, 276248, 1, 15, 1, 52, 1159005, 43251, 1, 536150, 75864, 509655, 1126347, 250730, 1548383, 17, 194687, 27301, 2, 1, 207930, 621863, 6065, 443547, 1, 6, 1, 1, 1, 1, 556555, 436634, 25394, 2, 61335, 98076, 1, 190958, 2, 18, 67981, 3, 8, 119447, 1, 1, 1, 19, 28803, 23, 33, 60281, 613151, 1, 65, 20341, 799766, 476273, 105018, 357868, 3, 92325, 2062793, 18, 72097, 30229, 1, 1, 3, 610392, 1, 202149, 887122, 56571, 1, 77788, 61580, 4, 72535, 381846, 148682, 26676, 1, 210, 3556343, 212550, 650316, 33491, 180366, 1, 295685, 46255, 43295, 1006367, 63606, 1, 1, 1, 1, 3094617, 21, 10, 3, 1, 1, 14730, 1585801, 102, 2, 410353, 1570, 1, 17423, 1, 1849366, 5, 1, 357670, 1, 1, 1, 1, 89936, 349048, 15, 7, 6, 2, 121654, 1852897, 19, 1, 103275, 1, 1, 771797, 23, 19, 6700, 1, 135844, 2966847, 3, 2356708, 101515, 1, 17, 1, 996641, 22, 16, 657783, 171744, 9604, 1, 1335166, 1739537, 2365309, 1, 3378711, 11332, 3980, 182951, 609339, 8, 10, 1746504, 61895, 386319, 24216, 331130, 12193, 1, 284, 1, 2, 50369, 38, 8, 1, 1238898, 177435, 124552, 22370, 1418184, 20132, 6, 2, 730842, 1, 1341094, 141638, 534983, 1551260, 31508, 96196, 434312, 3012, 715155, 1, 276172, 214255, 1, 208948, 4, 1631942, 512293, 37, 64474, 1342713, 1, 132634, 13, 2, 61876, 1081704, 160301, 2, 488156, 2414109, 1809831, 5, 74904, 6, 11, 5, 1, 79856, 96, 35421, 229858, 238507, 3838897, 18, 44, 1, 1659126, 9, 33708, 12, 1, 758381, 162742, 256046, 3, 15, 142673, 70953, 58559, 6, 2, 1, 984066, 290404, 1072226, 66415, 4465, 924279, 48133, 319765, 519401, 1, 1, 1201037, 418362, 17022, 68, 213072, 37, 1039025, 1, 2, 6, 4, 45769, 1, 5, 1061838, 54614, 21436, 7149, 1, 1, 1, 35950, 2199045, 1, 379742, 3, 2008330, 238692, 181, 7, 140483, 92278, 214409, 5179081, 1, 1, 334436, 2, 107481, 1142028, 1, 31146, 225284, 1, 14533, 4, 3963305, 173084, 102, 1, 4732, 14, 1, 25, 11032, 224336, 2, 131110, 175764, 81, 5630317, 1, 42, 1, 89532, 621825, 2291593, 210421, 8, 44281, 4, 303126, 2895661, 2672876, 3, 436915, 21025, 1, 4, 49227, 1, 39, 3, 1, 103531, 256423, 2, 1600922, 15, 1, 2, 58933, 1114987, 1, 4, 3, 1, 1544880, 285673, 240, 2, 128, 214387, 3, 1327822, 558121, 5, 2718, 4, 1258135, 7, 37418, 2729691, 1, 346813, 385282, 2, 35674, 513070, 13, 1930635, 117343, 1929415, 52822, 203219, 1, 52407, 1, 1, 1, 3, 2, 37121, 175148, 136893, 2510439, 2140016, 437281, 53089, 40647, 37663, 2579170, 83294, 1597164, 206059, 1, 9, 75843, 773677, 50188, 12, 1, 1067679, 105216, 2452993, 1813467, 3279553, 280025, 121774, 62, 5, 113, 182135, 1, 16, 71853, 4, 557139, 37803, 228249, 6, 32420, 8, 410034, 73889, 1, 2, 96706, 48515, 1, 3, 1314561, 137, 966719, 692314, 80040, 85147, 75291, 1, 1, 30, 38119, 182723, 42267, 3836110, 22, 986685, 2, 37, 1, 3, 26, 43389, 2679689, 1, 1, 57365, 1, 2662599, 2, 72055, 1, 141247, 1, 1, 1122312, 1, 1080672, 4, 266211, 1, 34163, 1490610, 256341, 1, 627753, 32110, 1, 42468, 1, 10746, 1, 9, 1, 46, 1714133, 5, 117, 1, 104340, 218338, 151958, 122407, 211637, 223307, 57018, 74768, 582232, 2, 621279, 4, 1, 11, 196094, 1839877, 167117, 8, 42991, 2199269, 124676, 1, 1, 1, 5, 1, 1, 698083, 1, 76361, 1564154, 67345, 1398411, 9, 11, 105726, 1197879, 1, 2, 62740, 39, 2, 397236, 17057, 267647, 13, 57509, 22954, 1, 12, 747361, 4325650, 21425, 2160603, 144738, 1, 204054, 3113425, 6, 3019210, 30, 3359, 1, 89117, 489245, 1, 218068, 1, 1, 14718, 222722, 1, 1, 216041, 72252, 279874, 183, 89224, 170218, 1549362, 2, 1, 953626, 32, 130355, 30460, 121028, 20, 159273, 5, 2, 30, 1, 76215, 1654742, 2326439, 1, 53836, 1, 6, 4, 72327, 9, 285883, 1, 908254, 698872, 47779, 3, 2293485, 265788, 3766, 1, 1, 83151, 36431, 307577, 256891, 29, 1, 1, 1093544, 145213, 5, 2, 581319, 2911699, 1, 213061, 1359700, 2, 1, 343110, 1, 157592, 1708730, 1, 22703, 32075, 1, 1, 87720, 159221, 2313143, 10, 2266815, 2106917, 1345560, 3146014, 4, 551632, 1066905, 550313, 4069794, 1, 1406178, 38981, 1, 3, 1, 3039372, 241545, 35, 63325, 85804, 1365794, 2, 2143204, 48, 1, 99, 3225633, 7, 4074564, 1023899, 3209940, 2054326, 70880, 2, 1, 284192, 1944519, 84682, 2, 867681, 90022, 378115, 1, 15, 602743, 1337444, 131, 1, 229, 161445, 3, 2, 5591616, 195977, 92415, 637936, 142928, 1, 2310569, 923, 1, 230288, 1300519, 398529, 2233, 100261, 4323269, 81362, 37300, 1, 233775, 32277, 434139, 323797, 19214, 782633, 2881473, 1, 1, 9, 337016, 1, 515612, 44637, 17, 1, 25, 67758, 1737819, 16454, 30613, 692963, 62216, 222062, 344596, 3, 33782, 19, 180441, 23552, 20462, 70740, 10298, 109691, 1, 1729427, 33714, 1770930, 1, 1, 1, 1, 290766, 136688, 688231, 3250223, 30703, 1985963, 527128, 3, 226340, 195576, 30, 1, 3, 1, 793085, 5527, 5, 1, 2188429, 1327399, 5, 6192537, 1445186, 2478313, 2, 16892, 3, 1, 1, 15, 12, 1361157, 4, 1241684, 1, 45008, 1, 505095, 4037314, 14, 8, 1, 16740, 69906, 45, 1, 240949, 3975533, 212705, 2617552, 278884, 1, 24966, 958059, 231886, 22929, 4052071, 51259, 67791, 78739, 1, 165787, 67, 518191, 86923, 437, 1271004, 135941, 244766, 1, 1, 1, 1152745, 1, 3, 406365, 3847357, 476636, 135097, 304368, 8, 1578276, 1, 1, 375, 1, 1, 1298206, 1860743, 2, 35311, 834516, 421428, 2, 66629, 1, 309845, 398756, 33, 907277, 384475, 2267460, 1, 269300, 124525, 34399, 93584, 362186, 811260, 426109, 1, 1009323, 109986, 122181, 1, 1, 3626487, 11452, 1092410, 57233, 6, 2009226, 1, 83333, 4, 1338631, 79114, 2140249, 51813, 1118986, 43514, 1529365, 1, 101, 1, 1,
package game.players;

import java.awt.Point;
import java.util.*;

public class RunningStar extends Player{

    @Override
    public Point takeTurn(String genome, Map<Point, Integer> vision) {
        Map<Integer, Integer> squareCosts = decode(genome);
        Path path = astar(vision, squareCosts);
        return path.get(1);
    }

    private Path astar(Map<Point, Integer> vision, Map<Integer, Integer> squareCosts) {
        Set<Path> closed = new HashSet<>();
        PriorityQueue<Path> open = new PriorityQueue<>();
        open.add(new Path(new Point(0, 0), 0));
        while (!open.isEmpty()){
            Path best = open.remove();
            if (best.head().x == 2 || (best.head().x > 0 && (best.head().y == 2 || best.head().y == -2))){
                return best;
            }
            for (Path path : pathsAround(best, vision, squareCosts)){
                if (!closed.contains(path) && !open.contains(path)){
                    open.add(path);
                }
            }
            closed.add(best);
        }

        Path p = new Path(new Point(0,0), 0);
        return p.add(new Point((int)(random.nextDouble() * 3 - 1), (int)(random.nextDouble() * 3 - 1)), 0);
    }

    private List<Path> pathsAround(Path path, Map<Point, Integer> vision, Map<Integer, Integer> costs) {
        Point head = path.head();
        List<Path> results = new ArrayList<>();
        for (int i = -1; i <= 1; i++){
            for (int j = -1; j <= 1; j++){
                if (i == 0 && j == 0){
                    continue;
                }
                Point p = new Point(head.x + i, head.y + j);
                if (!vision.containsKey(p) || vision.get(p) == -1){
                    continue;
                }
                results.add(path.add(p, costs.get(vision.get(p))));
            }
        }
        return results;
    }

    private Map<Integer, Integer> decode(String genome) {
        int chunkLength = genome.length()/16;
        Map<Integer, Integer> costs = new HashMap<>();
        for (int i = 0; i < 16; i++){
            int runSize = 0;
            int cost = 0;
            for (int j = i * chunkLength; j < (i + 1) * chunkLength; j++){
                switch (genome.charAt(j)){
                    case '0':
                        runSize = 0;
                        break;
                    case '1':
                        cost += ++runSize;
                }
            }
            costs.put(i, cost);
        }
        return costs;
    }

    private class Path implements Comparable<Path>{

        Point head;
        Path parent;
        int length;
        int totalCost;

        private Path(){}

        public Path(Point point, int cost) {
            length = 1;
            totalCost = cost;
            head = point;
            parent = null;
        }

        public Point get(int index) {
            if (index >= length || index < 0){
                throw new IllegalArgumentException(index + "");
            }
            if (index == length - 1){
                return head;
            }
            return parent.get(index);
        }

        public Point head() {
            return head;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Path path = (Path) o;

            if (!head.equals(path.head)) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return head.hashCode();
        }

        @Override
        public int compareTo(Path o) {
            return totalCost - o.totalCost;

        }

        public Path add(Point point, int cost) {
            Path p = new Path();
            p.head = point;
            p.totalCost = totalCost + cost;
            p.length = length + 1;
            p.parent = this;
            return p;
        }
    }
}

2

LeapForward, Python 2

Não é particularmente inovador, mas é a minha única tentativa que foi bem-sucedida.

class LeapForward(Player):
  def __init__(self):
    Player.__init__(self)
    self.coords = [Coordinate( 1, 0),
                   Coordinate( 1,-1),
                   Coordinate( 1, 1)]
    self.n_moves = len(self.coords)

  def turn(self):
    notOKColors = [self.bit_chunk(4*n,4) for n in range(4,8)]
    notOKMap = [Coordinate(x-2,y-2) for x in range(0,5) for y in range(0,5) if self.vision[y][x] not in notOKColors]
    goTo = [c for c in self.coords if c in notOKMap]
    if not goTo:
      goTo = [Coordinate(1,0)]
    return random.choice(goTo)

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.


Provavelmente deveria tê-lo chamado "RedQueen" :)
plannapus

1

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.

package game.players;
import java.awt.*;
import java.util.Map;
public class IAmARobotPlayer extends Player{
    private static final Point[] possibleMoves = {new Point(1,-1), new Point(1,0), new Point(1,1), new Point(0,-1), new Point(0,1), new Point(1,-1), new Point(1,0), new Point(1,1)};
    private int isGood(int pos,Map<Point,Integer> vision, char[] genomeChar){
        int value = vision.get(new Point(1,pos));
        if(value ==-1){
            return 0;
        } else {
            return genomeChar[84+value]-'0';
        }
    }

    @Override
    public Point takeTurn(String genome, Map<Point, Integer> vision) {

        char[] genomeChar = genome.toCharArray();
        int situation = 4*isGood(1,vision,genomeChar)+2*isGood(0,vision,genomeChar)+1*isGood(-1,vision,genomeChar);
        int reaction = 4*(genomeChar[3*situation+0]-'0')+2*(genomeChar[3*situation+1]-'0')+1*(genomeChar[3*situation+2]-'0');
        return possibleMoves[reaction];

    }
}

Resultado:

Individual scores: 1, 1, 332, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 47560, 15457, 1, 
Your final score is 3.7100115087136234

1

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 é:

  • 10XX - para quadrados seguros;
  • 11XX - para quadrados letais; e
  • 0000 a 0111 - para os 8 tipos de quadrados de interceptação.

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.

#include <initializer_list>
#include <vector>

enum class D { SAFE, LETHAL,TRAP_N, TRAP_NE, TRAP_E, TRAP_SE, TRAP_S, TRAP_SW, TRAP_W, TRAP_NW, UNSURE };
enum class X { SAFE, LETHAL, UNSURE };

inline void checkLocation( color_t color, D (&dna)[16], D check )
{
    if ( color != OUT_OF_BOUNDS && dna[color] == check )
        dna[color] = D::UNSURE;
}

inline void updateMapLocation( X (&map)[7][7], unsigned int x, unsigned int y, const X& safety ){
    if (        ( safety == X::LETHAL && map[x][y] != X::LETHAL )
            || ( safety == X::UNSURE && map[x][y] == X::SAFE ) )
        map[x][y] = safety;
}

inline unsigned int isSafePath( X (&map)[7][7], coord_t p )
{
    return map[p.x][p.y] == X::SAFE ? 1 : 0;
}
inline unsigned int isSafePath(X (&map)[7][7],coord_t p,coord_t q,coord_t r){
    if ( isSafePath( map,p ) )
        if ( isSafePath( map, q ) )
            return isSafePath( map, r );
    return 0;
}

inline unsigned int isSafeEast( X (&map)[7][7], coord_t p )
{
    if ( !isSafePath( map, p ) )
        return 0;
    if ( p.x == 6 )
        return 1;
    return isSafeEast(map,{p.x+1,p.y-1})
            +isSafeEast(map,{p.x+1,p.y+0})
            +isSafeEast(map,{p.x+1,p.y+1});
}

template<typename T> inline T max(T a,T b){return a>=b?a:b;}
template<typename T, typename... A> inline T max(T a,T b,A... c){return max(max(a,b),c...); }

coord_t cautiousSpecimins( dna_t d, view_t v ) {
    X map[7][7] = { { X::SAFE } };
    D dna[16] = { D::UNSURE };
    for ( color_t i = 0; i < 16; i++ )
    {
        if ( d[4*i] == 1 )
        {
            dna[i] = d[4*i + 1] == 1 ? D::LETHAL : D::SAFE;
        }
        else
        {
            switch ( dnarange( d, 4*i + 1, 3 ) )
            {
                case 0: dna[i] = D::TRAP_N; break;
                case 1: dna[i] = D::TRAP_NE; break;
                case 2: dna[i] = D::TRAP_E; break;
                case 3: dna[i] = D::TRAP_SE; break;
                case 4: dna[i] = D::TRAP_S; break;
                case 5: dna[i] = D::TRAP_SW; break;
                case 6: dna[i] = D::TRAP_W; break;
                case 7: dna[i] = D::TRAP_NW; break;
                default: dna[i] = D::UNSURE; break;
            }
        }
    }
    if ( v(-1, 0) != OUT_OF_BOUNDS )
        checkLocation( v( 0, 0), dna, D::LETHAL );

    if ( v(-1, 0) != OUT_OF_BOUNDS )
        for ( unsigned int y = 0; y < 7; ++ y )
            map[2][y] = X::LETHAL;

    if ( v(-2, 0) != OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 2; ++x )
            for ( unsigned int y = 0; y < 7; ++ y )
                map[x][y] = X::LETHAL;

    if ( v( 0, 1) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
                map[x][4] = X::LETHAL;

    if ( v( 0, 2) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
            for ( unsigned int y = 5; y < 7; ++ y )
                map[x][y] = X::LETHAL;

    if ( v( 0,-1) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
                map[x][2] = X::LETHAL;

    if ( v( 0,-2) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
            for ( unsigned int y = 0; y < 2; ++ y )
                map[x][y] = X::LETHAL;

    checkLocation( v( 1, 1), dna, D::TRAP_SW );
    checkLocation( v( 1, 0), dna, D::TRAP_W  );
    checkLocation( v( 1,-1), dna, D::TRAP_NW );
    checkLocation( v( 0,-1), dna, D::TRAP_N  );
    checkLocation( v(-1,-1), dna, D::TRAP_NE );
    checkLocation( v(-1, 0), dna, D::TRAP_E  );
    checkLocation( v(-1, 1), dna, D::TRAP_SE );
    checkLocation( v( 0, 1), dna, D::TRAP_S  );

    for ( int x = 1; x <= 5; ++x )
    {
        for ( int y = 1; y <= 5; ++y )
        {
            switch( dna[v(x-3,y-3)] )
            {
                case D::LETHAL : updateMapLocation( map, x+0, y+0, X::LETHAL ); break;
                case D::TRAP_N : updateMapLocation( map, x+0, y+1, X::LETHAL ); break;
                case D::TRAP_NE: updateMapLocation( map, x+1, y+1, X::LETHAL ); break;
                case D::TRAP_E : updateMapLocation( map, x+1, y+0, X::LETHAL ); break;
                case D::TRAP_SE: updateMapLocation( map, x+1, y-1, X::LETHAL ); break;
                case D::TRAP_S : updateMapLocation( map, x+0, y-1, X::LETHAL ); break;
                case D::TRAP_SW: updateMapLocation( map, x-1, y-1, X::LETHAL ); break;
                case D::TRAP_W : updateMapLocation( map, x-1, y+0, X::LETHAL ); break;
                case D::TRAP_NW: updateMapLocation( map, x-1, y+1, X::LETHAL ); break;
//              case D::UNSURE : updateMapLocation( map, x+0, y+0, X::SAFE );
//                               updateMapLocation( map, x+0, y+1, X::UNSURE );
//                               updateMapLocation( map, x+1, y+1, X::UNSURE );
//                               updateMapLocation( map, x+1, y+0, X::UNSURE );
//                               updateMapLocation( map, x+1, y-1, X::UNSURE );
//                               updateMapLocation( map, x+0, y-1, X::UNSURE );
//                               updateMapLocation( map, x-1, y-1, X::UNSURE );
//                               updateMapLocation( map, x-1, y+0, X::UNSURE );
//                               updateMapLocation( map, x-1, y+1, X::UNSURE );
//                               break;
                default        : break;
            }           
        }
    }

    unsigned int north = isSafeEast(map,{4,4});
    unsigned int east  = isSafeEast(map,{4,3});
    unsigned int south = isSafeEast(map,{4,2});
    unsigned int mx    = max( north, east, south );
    unsigned int sz;
    std::vector<coord_t> dir;
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+1,+1} );
        if ( east  == mx ) dir.push_back( {+1,+0} );
        if ( south == mx ) dir.push_back( {+1,-1} );

        return dir[v.rng.rint(dir.size())];
    }


    north = isSafePath(map,{4,4},{5,5},{5,6})
            + isSafePath(map,{4,4},{4,5},{5,6});
    south = isSafePath(map,{4,2},{5,1},{5,0})
            + isSafePath(map,{4,2},{4,1},{5,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+1,+1} );
        if ( south == mx ) dir.push_back( {+1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{3,4},{4,5},{5,6});
    south = isSafePath(map,{3,2},{4,1},{5,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+0,+1} );
        if ( south == mx ) dir.push_back( {+0,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = 2*isSafePath(map,{4,4},{4,5},{4,6})
            + 1*isSafePath(map,{4,4},{3,5},{4,6});
    south = 2*isSafePath(map,{4,2},{4,1},{4,0})
            + 1*isSafePath(map,{4,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+1,+1} );
        if ( south == mx ) dir.push_back( {+1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{3,4},{4,5},{4,6})
            + isSafePath(map,{3,4},{3,5},{4,6});
    south = isSafePath(map,{3,2},{4,1},{4,0})
            + isSafePath(map,{3,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+0,+1} );
        if ( south == mx ) dir.push_back( {+0,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{2,4},{3,5},{4,6});
    south = isSafePath(map,{2,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {-1,+1} );
        if ( south == mx ) dir.push_back( {-1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{3,4},{3,5},{3,6})
            + isSafePath(map,{3,4},{2,5},{3,6});
    south = isSafePath(map,{3,2},{3,1},{3,0})
            + isSafePath(map,{3,2},{2,1},{3,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+0,+1} );
        if ( south == mx ) dir.push_back( {+0,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{2,4},{3,5},{4,6});
    south = isSafePath(map,{2,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {-1,+1} );
        if ( south == mx ) dir.push_back( {-1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    return {-1,-1};
}

Amostras de pontuação:

Scores: 421155 2 129418 71891 90635 1 211 1111987 29745 7 2200750 41793 50500 45 2012072 2 485698 1 110061 1554720 210308 249336 2 1 262110 17 3 19 1719139 23859 45118 3182784 318 2 1 15572 14 2822954 18 11 2 3 15954 1331392 2296280 135015 1 360826 1 692367 4 244775 4814645 3749144 3 1 660000 1 11 3688002 3920202 3428464 123053 1 243520 86 9 6 289576 195966 549120 220918 9 1 43 71046 5213 118177 150678 54639 3 200839 1 3 6 1978584 1514393 119502 1 1 137695 184889 337956 1 1 441405 133902 991 1 4137428 1 1427115 3340977 1 2 1 55559 11 1 94886 30270 1 6 3 69394 264780 6877 47758 128568 1 116672 130539 163747 96253 1 2654354 1 141 58212 1613661 27 9504 1 2474022 843890 1 59 3110814 2353731 150296 313748 2590241 6 5970407 1434171 2 334715 141277 1 56810 2964306 51544 61973 715590 1 106 900384 50948 2 34652 108096 391006 1 2969764 47625 1 24 30481 44 8 1 18 2094036 106461 3080432 75 620651 16 71730 282145 275031 17 1 8 15 121731 18 2 1 1 495868 3252390 6 1 63712 7 3733149 13380 1 1
Geometric mean score: 2030.17

Pontuação máxima durante o teste: 8.150.817 amostras salvas.


Agora você conseguiu ... queria salvar o caminho para mais tarde, mas não pude deixar seus roedores cautelosos desafiados :) Como parece, o caminho funciona ainda melhor com uma codificação mais eficiente. Sua idéia de estender a área do caminho para 7x7 também parece promissora. Vou ver se consigo usar isso.

Atualmente, estou fazendo 2000 corridas para isso ... depois dos primeiros 900, a média parece estar se aproximando de 600, o que é bem distante de 2000. Você se importaria de executá-lo novamente para ver se o 2000 foi apenas um acaso?
Martin Ender
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.