Lab Rat Race: un esercizio di algoritmi genetici


113

Questa è la Sfida quindicinale n. 3. Tema: algoritmi genetici

Questa sfida è un po 'un esperimento. Volevamo vedere cosa potevamo fare, in termini di sfida, con algoritmi genetici. Non tutto può essere ottimale, ma abbiamo fatto del nostro meglio per renderlo accessibile. Se questo funziona, chissà cosa potremmo vedere in futuro. Forse un re genetico della collina?

Le specifiche sono piuttosto lunghe! Abbiamo provato a separare le specifiche in The Basics - il minimo indispensabile che devi sapere per iniziare a giocare con il framework e inviare una risposta - e The Gory Details - le specifiche complete, con tutti i dettagli sul controller, in base alle quali potrebbe scrivere il tuo.
Se hai qualche domanda, sentiti libero di unirti a noi in chat!

Sei un ricercatore in psicologia comportamentale. È venerdì sera e tu e i tuoi colleghi decidete di divertirvi e usare i topi di laboratorio per una piccola corsa di topi. In effetti, prima di attaccarci troppo emotivamente a loro, chiamiamoli esemplari .

Hai allestito una piccola pista per gli esemplari e, per renderlo più interessante, hai messo alcuni muri, trappole e teletrasporto lungo il percorso. Ora, i tuoi esemplari sono ancora topi ... non hanno idea di cosa sia una trappola o un teletrasporto. Tutto ciò che vedono sono alcune cose in diversi colori. Inoltre non hanno alcun tipo di memoria - tutto ciò che possono fare è prendere decisioni in base al loro ambiente attuale. Immagino che la selezione naturale selezionerà gli esemplari che sanno come evitare una trappola da quelli che non lo fanno (questa razza richiederà del tempo ...). Date inizio alle danze!

Immagine di esempio della scheda in uso

† 84.465 esemplari sono stati danneggiati nel fare questa sfida.

Le basi

Questo è un gioco per giocatore singolo (tu e i tuoi colleghi non volevate confondere le popolazioni, quindi ognuna ha costruito la propria pista). La pista di gara è una griglia rettangolare, alta 15 celle e larga 50 celle. Si inizia con 15 campioni su cellule casuali (non necessariamente distinte) sul bordo sinistro (dove x = 0 ). I tuoi campioni dovrebbero cercare di raggiungere l'obiettivo che è qualsiasi cella a x ≥ 49 e 0 ≤ y ≤ 14 (i campioni possono superare il binario a destra). Ogni volta che succede, ottieni un punto. Inizi anche il gioco con 1 punto. Dovresti cercare di massimizzare i tuoi punti dopo 10.000 turni.

I campioni multipli possono occupare la stessa cellula e non interagiranno.

Ad ogni turno, ogni esemplare vede una griglia 5x5 dell'ambiente circostante (con se stesso al centro). Ogni cella di quella griglia conterrà un colore -1per 15. -1rappresenta celle fuori limite. Il tuo esemplare muore se esce dai limiti. Per quanto riguarda gli altri colori, rappresentano celle vuote, trappole, pareti e teletrasporto. Ma il tuo esemplare non sa quale colore rappresenta cosa e nemmeno tu. Ci sono alcuni vincoli però:

  • 8 colori rappresenteranno le celle vuote.
  • 4 colori rappresenteranno un teletrasporto. Un teletrasporto invierà il campione in una determinata cella all'interno del suo vicinato 9x9. Questo offset sarà lo stesso per tutti i teletrasporto dello stesso colore.
  • 2 colori rappresenteranno le pareti. Entrare in un muro è come stare fermi.
  • 2 colori rappresenteranno una trappola. Una trappola indica che una delle 9 celle nelle sue immediate vicinanze è letale (non necessariamente la stessa cellula trap). Questo offset sarà lo stesso per tutte le trappole dello stesso colore.

Ora, riguardo a quella selezione naturale ... ogni esemplare ha un genoma, che è un numero con 100 bit. Verranno creati nuovi esemplari riproducendo due esemplari esistenti e quindi mutando leggermente il genoma. Più un esemplare ha successo, maggiori sono le possibilità di riproduzione.

Quindi ecco il tuo compito: scriverai una singola funzione, che riceve come input la griglia di colori 5x5 che un campione vede, così come il suo genoma. La tua funzione restituirà una mossa (Δx, Δy) per il campione, dove Δx e Δy saranno ciascuno uno {-1, 0, 1}. Non è necessario conservare i dati tra le chiamate di funzione. Ciò include l'uso di generatori di numeri casuali personalizzati. La tua funzione ti verrà fornita con un RNG seminato che sei libero di usare come desideri.

Il punteggio della tua presentazione sarà la media geometrica del numero di punti su 50 tracce casuali. Abbiamo scoperto che questo punteggio è soggetto a una discreta variazione. Pertanto, questi punteggi saranno preliminari . Una volta che questa sfida si esaurirà, verrà annunciata una scadenza. Alla fine della scadenza, verranno scelte casualmente 100 schede e tutte le comunicazioni verranno salvate su queste 100 schede. Sentiti libero di mettere un punteggio stimato nella tua risposta, ma valuteremo noi stessi ogni invio per garantire che nessuno trucchi.

Abbiamo fornito programmi di controllo in una manciata di lingue. Attualmente, puoi scrivere la tua proposta in Python (2 o 3), Ruby , C ++ , C # o Java . Il controller genera le schede, esegue il gioco e fornisce un framework per l'algoritmo genetico. Tutto quello che devi fare è fornire la funzione di spostamento.

Aspetta, quindi cosa faccio esattamente con il genoma?

La sfida è di capirlo!

Dato che gli esemplari non hanno memoria, tutto ciò che hai in un dato turno è una griglia di colori 5x5 che non significa nulla per te. Quindi dovrai usare il genoma per raggiungere l'obiettivo. L'idea generale è che usi parti del genoma per memorizzare informazioni sui colori o sul layout della griglia e il tuo bot basa le sue decisioni sulle informazioni aggiuntive memorizzate nel genoma.

Ora, ovviamente, non è possibile archiviare nulla manualmente. Quindi le informazioni effettivamente memorizzate saranno inizialmente completamente casuali. Ma l'algoritmo genetico selezionerà presto quegli esemplari il cui genoma contiene le informazioni giuste mentre uccide quelli che hanno le informazioni sbagliate. Il tuo obiettivo è trovare una mappatura dai bit del genoma e dal tuo campo visivo a una mossa, che ti permetta di trovare rapidamente un percorso verso l'obiettivo e che si evolva costantemente in una strategia vincente.

Dovrebbero essere sufficienti informazioni per iniziare. Se lo desideri, puoi saltare la sezione successiva e selezionare il tuo controller preferito dall'elenco dei controller in basso (che contiene anche informazioni su come utilizzare quel controller specifico).

Continua a leggere se vuoi tutto ...

I dettagli di Gory

Questa specifica è completa. Tutti i controller devono implementare queste regole.

Tutta la casualità utilizza una distribuzione uniforme, se non diversamente indicato.

Traccia generazione:

  • La traccia è una griglia rettangolare, X = 53 celle di larghezza e Y = 15 celle di altezza. Le celle con x ≥ 49 sono celle obiettivo (dove x è a base zero).
  • Ogni cellula ha un singolo colore e può essere letale o meno - le cellule non sono letali se non specificato da uno dei tipi di cellule sottostanti.
  • Esistono 16 diversi colori di celle, etichettati da 0a 15, il cui significato cambierà da gioco a gioco. Inoltre, -1rappresenta le cellule fuori limite - queste sono letali .
  • Scegli 8 colori casuali . Queste saranno celle vuote (che non hanno alcun effetto).
  • Scegli altri 4 colori casuali . Questi sono teletrasporto. Per due di questi colori, scegliere un offset diverso da zero nel vicinato 9x9 (da (-4, -4) a (4,4) tranne (0,0)). Per gli altri due colori, invertire tali offset. Se un campione calpesta un teletrasporto, viene immediatamente spostato da quell'offset.
  • Scegli altri 2 colori casuali . Queste sono trappole. Per ciascuno di questi colori, scegli un offset nel vicinato 3x3 (da (-1, -1) a (1,1)). Una trappola indica che la cella a quell'offset è letale . Nota: la stessa cella trap non è necessariamente letale.
  • I 2 colori rimanenti sono le pareti, che impediscono il movimento. Il tentativo di spostarsi su una cella a muro trasformerà la mossa in fermo. Le stesse cellule del muro sono letali .
  • Per ogni cella non obiettivo della griglia, scegli un colore casuale. Per ogni cella obiettivo scegliere un colore vuoto casuale .
  • Per ogni cella sul bordo sinistro della traccia, determinare se l'obiettivo può essere raggiunto entro 100 turni (in base alle regole di ordine dei turni di seguito). In tal caso, questa cella è una cella iniziale ammissibile . Se ci sono meno di 10 celle iniziali, scarta la traccia e generane una nuova.
  • Crea 15 esemplari, ognuno con un genoma casuale e un'età di 0 anni . Posizionare ciascun campione su una cella iniziale casuale.

Ordine di svolta:

  1. I seguenti passaggi verranno eseguiti, in ordine, per ciascun campione. I campioni non interagiscono né si vedono e possono occupare la stessa cellula.
    1. Se l'età del campione è 100 , muore. Altrimenti, aumenta la sua età di 1.
    2. Al campione viene assegnato il suo campo visivo - una griglia di colori 5x5, centrata sul campione - e restituisce una mossa nel suo vicinato 3x3. Lo spostamento al di fuori di questo intervallo provoca l'arresto del controller.
    3. Se la cella di destinazione è un muro, lo spostamento viene modificato in (0,0).
    4. Se la cella target è un teletrasporto, il campione viene spostato dall'offset del teletrasporto. Nota: questo passaggio viene eseguito una volta , non iterativamente.
    5. Se la cellula attualmente occupata dal campione (potenzialmente dopo aver usato un teletrasporto) è letale, il campione muore. Questa è l' unica volta in cui i campioni muoiono (a parte il passaggio 1.1 sopra). In particolare, un nuovo esemplare che si genera su una cellula letale non morirà immediatamente, ma ha la possibilità di spostarsi prima dalla cellula pericolosa.
    6. Se il campione occupa una cella obiettivo, segna un punto, sposta il campione in una cella iniziale casuale e resetta la sua età a 0.
  2. Se sul tabellone rimangono meno di due esemplari, il gioco termina.
  3. Crea 10 nuovi esemplari con età 0 . Ogni genoma è determinato (individualmente) dalle seguenti regole di riproduzione. Posizionare ciascun campione su una cella iniziale casuale.

Allevamento:

  • Quando viene creato un nuovo campione, scegliere due genitori distinti a caso, con una propensione per i campioni che sono progrediti ulteriormente verso destra. La probabilità di scegliere un campione è proporzionale al suo punteggio di fitness corrente . Il punteggio di fitness di un campione è

    1 + x + 50 * numero di volte in cui ha raggiunto l'obiettivo

    dove x è l'indice orizzontale basato su 0. Gli esemplari creati nello stesso turno non possono essere scelti come genitori.

  • Dei due genitori, scegline uno casuale da cui prendere il primo bit del genoma.

  • Ora mentre cammini lungo il genoma, cambia i genitori con una probabilità di 0,05 e continua a prendere i pezzi dal genitore risultante.
  • Muta il genoma completamente assemblato: per ogni bit, capovolgilo con probabilità 0,01 .

punteggio:

  • Una partita dura 10.000 turni.
  • I giocatori iniziano il gioco con 1 punto (per consentire l'uso della media geometrica).
  • Ogni volta che un campione raggiunge l'obiettivo, il giocatore segna un punto.
  • Per ora, l'invio di ciascun giocatore verrà eseguito per 50 partite, ognuna con una traccia casuale diversa.
  • L'approccio sopra riportato comporta una varianza maggiore di quanto sia desiderabile. Una volta che questa sfida si esaurirà, verrà annunciata una scadenza. Alla fine della scadenza, verranno scelte casualmente 100 schede e tutte le comunicazioni verranno salvate su queste 100 schede.
  • Il punteggio complessivo di un giocatore è la media geometrica dei punteggi di questi singoli giochi.

I controller

È possibile scegliere uno dei seguenti controller (poiché sono funzionalmente equivalenti). Li abbiamo testati tutti, ma se trovi un bug, vuoi migliorare il codice o le prestazioni o aggiungere una funzionalità come l'output grafico, invia un problema o invia una richiesta pull su GitHub! Puoi anche aggiungere un nuovo controller in un'altra lingua!

Fare clic sul nome della lingua per ciascun controller per accedere alla directory corretta su GitHub, che contiene una README.mdcon istruzioni d'uso esatte.

Se non hai familiarità con git e / o GitHub, puoi scaricare l'intero repository come ZIP dalla prima pagina (vedi il pulsante nella barra laterale).

Pitone

  • Più accuratamente testato. Questa è la nostra implementazione di riferimento.
  • Funziona con Python 2.6+ e Python 3.2+!
  • È molto lento. Si consiglia di eseguirlo con PyPy per un notevole aumento di velocità.
  • Supporta l'output grafico usando pygameo tkinter.

Rubino

  • Testato con Ruby 2.0.0. Dovrebbe funzionare con le versioni più recenti.
  • È anche abbastanza lento, ma Ruby può essere utile per la prototipazione di un'idea per una presentazione.

C ++

  • Richiede C ++ 11.
  • Opzionalmente supporta il multithreading.
  • Di gran lunga il controller più veloce del gruppo.

C #

  • Utilizza LINQ, quindi richiede .NET 3.5.
  • Piuttosto lento.

Giava

  • Non particolarmente lento. Non particolarmente veloce.

Classifica preliminare

Tutti i punteggi sono preliminari. Tuttavia, se qualcosa è chiaramente sbagliato o obsoleto, per favore fatemi sapere. Il nostro esempio di presentazione è elencato per il confronto, ma non in contesa.

  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

Crediti

Questa sfida è stata un enorme sforzo di collaborazione:

  • Nathan Merril: ha scritto controller Python e Java. Trasformato il concetto di sfida da King of the Hill a Rat Race.
  • trichoplax: Playtesting. Ha funzionato sul controller Python.
  • feersum: ha scritto controller C ++.
  • VisualMelon: ha scritto il controller C #.
  • Martin Büttner: Concept. Ha scritto controller Ruby. Playtesting. Ha funzionato sul controller Python.
  • T Abraham: Playtest. Testato Python e rivisto C # e controller C ++.

Tutti gli utenti di cui sopra (e probabilmente un altro paio che ho dimenticato) hanno contribuito alla progettazione complessiva della sfida.

Aggiornamento controller C ++

Se si utilizza C ++ con Visual Studio e il multithreading, è necessario ottenere l' ultimo aggiornamento a causa di un bug con il seeding del generatore di numeri casuali che consente la produzione di schede duplicate.


3
Qualcuno non potrebbe semplicemente creare un algoritmo genetico genetico per trovare l'algoritmo genetico ottimale per questo problema?
mbomb007,

1
@ anon3202 Beh, questo ovviamente ti darebbe maggiori informazioni sul layout della traccia, dato che potresti valutare dove ti trovi. In sostanza, volevamo mantenere semplice l'interfaccia per i robot e renderla un problema puramente locale, in cui avrai bisogno del genoma per apprendere quale soluzione locale è più vantaggiosa per i tuoi progressi globali.
Martin Ender,

1
@matovitch Vedi la sezione 5 della sezione Ordine di svolta dei Dettagli Gory (specifiche complete):'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

1
Ho modificato il codice C ++ per mostrare media di esempio, stddev, stderr e intervallo di conf 99% (prima del log / exp "geometrico") e ho fatto una scoperta sorprendente. La risposta "Fede cieca" aveva "Media campionaria di 116529 + - 2,778337e + 010 (99%) stddev = 7,77951e + 010" dopo 50 corse. "Trascinare l'intervallo di confidenza al 50% non migliora affatto le cose. La media geometrica era però più stabile: "Media di 159.458 + - 117262 (99%) stddev = 32.6237" (Prima del suo aggiornamento di 800 punteggi)
Mooing Duck

1
Ho fatto qualche esperimento con il tasso di mutazione e penso che la sfida sarebbe più interessante (e i controllori sarebbero molto più veloci) se la probabilità fosse aumentata da 0,01 a 0,0227, il che dà al DNA solo il 10% di possibilità di passare mutazione invariata invece del 37% con il valore corrente. Questo evita ridicole esplosioni di popolazione (che a loro volta fanno risparmiare molto tempo di calcolo) e prevengono molti fallimenti a causa dell'insufficiente diversità. I punteggi individuali sono più bassi, ma poiché più tiri producono vincitori, la media globale tende ad aumentare.

Risposte:


37

La fede cieca - C ++ - sembra segnare oltre 800 (!) Su 2000 corse

Genoma con codice colore con un misterioso feedback di traccia e un efficace deterrente contro il muro

#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";
}

Risultati del campione:

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

Sulla base del test involontariamente lungo di Feersum, credo che 2000 corse siano sufficienti per produrre un risultato accettabilmente stabile.
Dato che il mio controller modificato mostra la media geometrica corrente dopo ogni corsa, ho confermato visivamente che la variazione nelle ultime 50 corse era relativamente piccola (+ - 10 punti).

Ciò che fa battere queste creature

Invece di dare priorità uguali a ciascun colore, considero questi possibili valori:

  1. bene -> il ratto pensa che possa andare tranquillamente lì
  2. cattivo -> il topo non ci andrà
  3. trappola -> il ratto considererà la posizione della trappola cattiva e la cella che indica la trappola buona .
    Anche se sono troppo pigro per rinominarlo, questo piuttosto un "rivelatore di pericolo" che indica la posizione (presunta) di una trappola, un muro, un teletrasporto in attesa di inviare il vagabondo ignaro in un posto spiacevole o addirittura l'ingresso di un morto -fine. In breve, un posto in cui un topo saggio preferirebbe non andare.

i geni buoni o cattivi richiedono solo 2 bit per l'archiviazione (ad esempio 11e 10), ma le trappole richiedono 4 bit ( 0tttdove tttrappresenta una delle 8 possibili posizioni "pericolose").

Per mantenere coerente ogni gene (cioè mantenere il suo significato dopo essere stato miscelato in un genoma completamente diverso, che richiede che ciascun gene con codice colore sia in una posizione fissa), tutti i valori sono codificati su 4 bit (così buono è codificato 11xxe male come 10xx), per un totale di 16 * 4 = 64 bit.

I restanti 36 bit vengono utilizzati come "anti-wall-bangers" (ne parleremo più avanti). I 25 colori circostanti sono sottoposti a hash in un indice di questi 36 bit. Ogni bit indica una direzione verticale preferita (su o giù), che viene utilizzata quando è possibile scegliere tra due celle.

La strategia è la seguente:

  • decodifica ogni colore in base al genoma (o rapporto del controller diretto per celle "difettose" fuori traccia)
  • costruire una mappa dei dintorni immediati (3x3 celle, 8 possibili vicini)
  • calcola una firma dell'ambiente circostante (un hash dei 25 colori tranne le celle fuori pista)
  • scegli una direzione verticale preferita dalla firma (tra 36 secchi di hash)
  • prova a spostarti verso un vicino accennato come "buono", iniziando da quelli più vicini all'obiettivo e andando prima nella direzione verticale preferita
  • se non è possibile trovare un vicino "buono", prova a spostare indietro di una cella (quindi potresti essere vittima di uno sfortunato incidente ed evitare comunque di migliorare la forma fisica)

Roditori, ecco i nemici del vostro genere

il temuto muro loop di teletrasporto

La cosa peggiore che può accadere a una popolazione è di non aver ancora prodotto alcun vincitore, ma molti ratti sono rimasti bloccati contro un muro o all'interno di un loop di teletrasporto infinito abbastanza vicino all'obiettivo da avere una probabilità dominante di essere selezionati per la riproduzione .
Contrariamente ai topi che vengono schiacciati in una trappola o teletrasportati nei muri, questi roditori saranno uccisi solo dalla vecchiaia.
Non hanno alcun vantaggio competitivo rispetto ai loro cugini che hanno bloccato 3 cellule sin dall'inizio, ma avranno molto tempo per allevare generazione dopo generazione di cretini fino a quando il loro genoma non diventerà dominante, danneggiando così gravemente la diversità genetica senza una buona ragione.

Per mitigare questo fenomeno, l'idea è di rendere più probabile la progenie di questi topi cattivi e cattivi per evitare di seguire i passi dei loro antenati.
L'indicazione della direzione verticale è lunga solo 1 bit (in pratica dice "prova a salire o scendere prima in questi dintorni"), e probabilmente alcuni bit avranno un effetto sul percorso seguito, quindi le mutazioni e / o i crossover dovrebbero avere un impatto significativo.
Molte prole avranno un comportamento diverso e non finiranno per sbattere la testa sullo stesso muro (tra i cadaveri dei loro antenati affamati).
La subtelty qui è che questa indicazione non è il fattore dominante nel comportamento del ratto. L'interpretazione del colore continuerà a prevalere nella maggior parte dei casi (la scelta su / giù avrà importanza solo se ci sono effettivamente due "buoni"e ciò che il ratto vede come un colore innocuo non è un teletrasporto in attesa di gettarlo in un muro).

Perché (sembra) funzionare?

Non so ancora esattamente perché.

Il colpo di fortuna assoluto che rimane un mistero irrisolto è la logica della mappatura delle trappole. È senza dubbio la pietra angolare del successo, ma funziona in modo misterioso.

Con la codifica utilizzata, un genoma casuale produrrà identificatori di colore "buono", 25% "cattivo" e 50% "trappola".
gli identificatori "trap" a loro volta produrranno stime "buone" e "cattive" in correlazione con l'ambiente 5x5.
Di conseguenza, un topo in una determinata posizione "vedrà" il mondo come un mix di colori "go / no go" stabili e contestuali.

Come sembra indicare il meccanismo anti-colpo abbastanza riuscito, il peggior tipo di elemento sulla pista è il muro temuto (e suo cugino il circuito di teletrasporto, ma immagino che questi siano molto meno comuni).

La conclusione è che un programma di successo deve soprattutto riuscire a far evolvere i topi in grado di rilevare posizioni che porteranno a una lenta fame senza raggiungere l'obiettivo.

Anche senza "indovinare" i due colori che rappresentano i muri, i colori della "trappola" sembrano contribuire all'evitamento del muro permettendo a un ratto di aggirare alcuni ostacoli non perché "vedeva" i muri, ma perché la stima della "trappola" escludeva questi celle a parete particolari in questi particolari dintorni.

Anche se il ratto tenta di spostarsi verso l'obiettivo (il che potrebbe portare a pensare che gli indicatori di trappola più "utili" siano quelli che indicano un pericolo di fronte), penso che tutte le direzioni delle trappole abbiano all'incirca la stessa influenza: una trappola che indica "pericolo dietro "situato 2 celle di fronte a un ratto ha la stessa influenza di quello che indica" pericolo davanti "quando il ratto si trova proprio sopra di esso.

Il motivo per cui questo mix ha la proprietà di far convergere il genoma in modo così efficace è purtroppo ben oltre la mia matematica.

Mi sento più a mio agio con il deterrente da sbattere contro il muro. Questo ha funzionato come previsto, sebbene ben al di sopra delle mie aspettative (il punteggio è stato sostanzialmente moltiplicato per quattro).

Ho hackerato pesantemente il controller per visualizzare alcuni dati. Ecco un paio di piste:

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

Qui una razza di super-topo è apparsa presto (la pista probabilmente ha permesso di correre in linea retta e qualche topo fortunato nelle primissime generazioni aveva il DNA giusto per approfittarne). Il numero di esemplari alla fine è circa la metà del massimo teorico di circa 100.000 ratti, il che significa che quasi la metà delle creature ha acquisito la capacità di sopravvivere indefinitamente a questa particolare pista (!).
Naturalmente il punteggio risultante è semplicemente osceno - come lo è il tempo di calcolo, tra l'altro.

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

Qui possiamo vedere la raffinatezza del genoma al lavoro. Il lignaggio tra gli ultimi due genomi appare chiaramente. Le valutazioni positive e negative sono le più significative. Le indicazioni della trappola sembrano oscillare fino a quando non si stabilizzano in una trappola "utile" o si trasformano in buone o cattive .

Sembra che i geni del colore abbiano alcune caratteristiche utili:

  • hanno un significato autonomo
    (un colore specifico deve essere gestito in un modo specifico)
    Ogni codice colore può essere gettato in un genoma completamente diverso senza cambiare drasticamente il comportamento - a meno che il colore non sia effettivamente decisivo (in genere un muro o un teletrasporto che porta a un ciclo infinito).
    Questo è meno il caso di una codifica di priorità di base, poiché il colore più prioritario è l'unica informazione utilizzata per decidere dove spostarsi. Qui tutti i colori "buoni" sono uguali, quindi un determinato colore aggiunto all'elenco "buono" avrà un impatto minore.
  • sono relativamente resistenti alle mutazioni
    la codifica buona / cattiva ha solo 2 bit significativi su 4 e la posizione della trappola potrebbe essere cambiata la maggior parte del tempo senza alterare significativamente il comportamento del ratto.
  • sono piccoli (4 bit), quindi la probabilità di essere distrutti da un crossover è molto bassa.
  • le mutazioni producono innocui cambiamenti significativi
    Un gene che muta in "buono" avrà o un effetto limitato (se, ad esempio, corrisponde a una cella vuota, potrebbe consentire di trovare un nuovo percorso più breve, ma che potrebbe anche portare il ratto a una trappola) o drammatica (se il colore rappresenta un muro, è molto probabile che il nuovo topo rimanga bloccato da qualche parte).
    Un gene che passa alla "trappola" priverà il ratto di un colore essenziale o non avrà alcun effetto evidente.
    Una mutazione della posizione di una trappola conta solo se c'è davvero una trappola (o qualcosa di dannoso) davanti, che ha una probabilità relativamente piccola (direi qualcosa come 1/3).

Infine, immagino che gli ultimi 36 bit contribuiscano non solo a evitare che i topi rimangano bloccati, ma anche a diffondere i ratti in modo più uniforme sulla pista, preservando così la diversità genetica fino a quando non emerge un genoma vincente e diventa dominante attraverso la parte di codice colore.

Ulteriori lavori

Devo dire che trovo queste piccole creature affascinanti.
Grazie ancora a tutti i partecipanti a questa eccellente sfida.

Sto pensando di macellare ulteriormente il controller per visualizzare dati più significativi, come la discendenza di un topo di successo.

Mi piacerebbe anche vedere questi topi in azione, ma questo linguaggio C ++ in un linguaggio rende la creazione - e tanto meno l'animazione - delle immagini (tra le altre cose) un lavoro disordinato.

Alla fine, vorrei produrre almeno una spiegazione del sistema di trappole e possibilmente migliorarlo.

Hacker del controller

Se qualcuno è interessato, posso pubblicare le modifiche che ho apportato al controller.
Sono sporchi ed economici, ma fanno il lavoro.

Non sono un esperto di GitHub, quindi dovrebbe passare attraverso un semplice post.


16
Ha ottenuto un punteggio di 208,14 con 10.000 partite. Stavo provando a testarlo per 1000, ma non mi ero mai reso conto di aver digitato uno 0 in più, quindi ci sono volute più di 7 ore.
feersum

Grazie LOL lo stesso. Rispetto alle mie due 1000 corse, sembra che circa 2000 corse potrebbero produrre un risultato stabile allora.

Cosa ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^significa? Il resto lo posso immaginare, ma sto avendo problemi con quel po '?
Mooing Duck,

Stavo pensando di creare un controller di "debug" separato, che esegue un topo alla volta, e ogni volta che viene generato un nuovo topo mostra il DNA di entrambi i genitori e il bambino (tramite una funzione personalizzabile). Ciò renderebbe molto più semplice esaminare come funziona il ratto.
Mooing Duck,

2
che rappresenta i 36 bit dell'indicatore "su / giù", ma in questi esempi il DNA vincente è già diventato dominante, quindi non variano molto.

18

Credenti forti - C ++ - (teletrasporto migliorato): oltre 10.000 per 2000 tiri

(questa è un'evoluzione della fede cieca , quindi potresti voler scalare un altro muro di testo prima di questo)

#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";
}

Episodio IV: orientare la griglia

risultati

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

Sono passato a g ++ / MinGW e 3 thread.
Il codice generato da GNU è più del doppio di quello di Microsoft.
Non c'è da meravigliarsi, con la loro spaventosa implementazione STL.

teletrasporti

L'effetto del teletrasporto dipende fortemente dalla posizione. Fino ad ora ero felice di considerare un teletrasporto o sempre buono (visto come uno spazio vuoto) o sempre cattivo (visto come un muro in modo che nessun roditore lo avrebbe mai preso).

Questo è un modello troppo approssimativo.
Un determinato teletrasporto può spingere un topo in avanti fino a poche celle dall'obiettivo, ma una volta lì lo stesso teletrasporto può lanciare il topo fuori dal tabellone.
Un tale teletrasporto sarà probabilmente riconosciuto come passabile (poiché aumenta la forma fisica più velocemente rispetto a quando "cammina" nella stessa posizione x), diventa parte del genoma dominante e uccide quasi tutti i topi che si fidano di esso "sempre al sicuro".
Poiché i topi non hanno alcun modo di conoscere la loro posizione X, l'unica soluzione per rilevare questi insidiosi teletrasportatori è decidere se calpestarli sulla base degli unici dati contestuali disponibili, ovvero la griglia di colore 5x5.

Per fare ciò, ho definito 4 tipi di geni di colore:

  • rilevatore di trappole di pericolo
  • vuoto passabile ovunque sulla pista
  • blocco vietato in qualsiasi punto della pista
  • fascio visto come vuoto o blocco a seconda dell'ambiente circostante

L'idea è di cercare di distinguere un teletrasporto guardando i suoi 8 vicini immediati. Poiché la probabilità di avere 8 vicini identici in una determinata posizione è molto bassa, ciò dovrebbe consentire di identificare un'istanza unica di ciascun teletrasporto.

Gli 8 colori vicini possono essere combinati per formare una firma locale, che è invariante rispetto alla posizione nel labirinto. Sfortunatamente, gli 8 vicini sono visibili solo per le cellule situate all'interno del quadrato 3x3 del campo visivo, quindi le firme saranno imprecise sul bordo del campo visivo.
Tuttavia, questo ci fornirà una costante informazione contestuale nelle immediate vicinanze, il che è sufficiente per aumentare la probabilità di navigare con successo nei teletrasporti.

i geni del fascio hanno un campo variabile a 2 bit.
Per una data firma locale del teletrasporto, c'è una possibilità su quattro che la cella di raggio sia considerata impraticabile. Ogni valore del campo seleziona una di queste quattro possibilità.
Di conseguenza, una mutazione del gene del fascio su questi 2 bit scorrerà attraverso 4 possibili significati contestuali del colore.

Inoltre, i colori più importanti da indovinare sono ancora muri e trappole. Ciò significa che dovremmo consentire il rilevamento del teletrasporto solo dopo che i topi hanno imparato dove sono le pareti e le trappole.

Questo viene fatto aggiornando le firme locali solo in modo sprezzante. L'attuale criterio per l'aggiornamento di una firma locale è la fedeltà di un colore identificato come potenziale teletrasporto.

La codifica utilizza 5 bit per gene colore e raggruppa i tipi per liberare i 3 bit meno significativi per codificare un valore 0..7:

  • 4 pericolo
  • 4 vuoti
  • 4 blocchi
  • 4 raggi

Ogni gene del raggio ha 1/4 di probabilità di essere considerato come un blocco e 3/4 possibilità di essere considerato vuoto, quindi 4 raggi rappresentano in media 1 blocco e 3 vuoti.

La proporzione media rappresentata da una diffusione casuale di 16 colori è quindi:

  • 4 pericolo
  • 7 vuoti
  • 5 blocchi

Questo mix sembra dare i migliori risultati finora, ma non ho finito di modificarlo.

Mutabilità genica

Una cosa è certa: i valori di codice scelti per rappresentare i tipi di geni sono fondamentali. L'inversione di due valori può costare 2000 punti o più.

Anche in questo caso il motivo per cui va oltre la mia matematica.

La mia ipotesi è che le probabilità di mutazione da un tipo a un altro debbano essere bilanciate, oppure, come in una matrice di Markow, le probabilità cumulative tendono a limitare i valori al sottoinsieme con le più alte probabilità di transizione in entrata.

Percorso per il salvataggio

Il pathing ridurrà drasticamente il numero di cellule visitate, consentendo di testare solo le più probabili per raggiungere l'obiettivo. Pertanto, non solo vengono evitati alcuni vicoli ciechi frequenti, ma è anche molto più probabile che vengano scoperti codici colore errati in precedenza.
Di conseguenza, il tempo di convergenza è fortemente ridotto.

Tuttavia, ciò non aiuta a risolvere le mappe in cui il genoma non è in grado di produrre una rappresentazione adeguata della traccia.

Cosa fare con i deficienti?

Dopo aver osservato visivamente la pista, ho capito perché una strategia predefinita che tenta di andare avanti anche quando sembra che non ci siano nient'altro che muri davanti è davvero meglio che trattenersi.
"muri" possono in realtà essere teletrasporti che producono così tanti sfortunati risultati che il genoma li mappa come ostacoli da non superare, ma in rare occasioni un particolare esempio di questo cattivo teletrasporto può avere un effetto positivo (o almeno non letale) , quindi prenderlo invece di tornare indietro aumenta le possibilità di trovare una strada per la vittoria.

Convergenza precoce

Mi sembra che il tasso di mutazione sia un po 'troppo basso (almeno per i miei roditori).

L'attuale impostazione 0,01 offre al DNA il 37% di possibilità di sopravvivere al processo di mutazione intatto. La modifica del parametro su 0,0227 riduce questa probabilità a circa il 10%

La formula misteriosa è la mutazione P 1 bit = 1-P intero genoma intatto 1/100 , 100 è la lunghezza del bit del genoma.

Ad esempio per una probabilità del 10%, mutazione P 1 bit = 1 - 0,1 1/100 = 0,0277
Per una probabilità del 5%, P = 1 - 0,05 1/100 = 0,0295
Invertendo la formula, troviamo che 0,01 offre una probabilità del 37% di essere invariato dalla mutazione.

Ho ripetuto lo stesso identico test (usando una sequenza fissa di semi casuali) con una probabilità del 10%.
Su molte mappe, i precedenti fallimenti si sono trasformati in (limitati) successi. D'altra parte, le enormi esplosioni della popolazione erano meno (il che ha avuto l'interessante effetto collaterale di accelerare molto il calcolo).
Anche se i punteggi molto alti (oltre un milione) erano meno comuni, il numero di corse più riuscite era più che sufficiente per compensare.
Alla fine, la media è passata da 1400+ a circa 2000.

Impostando P al 5%, al contrario, si produceva una media di circa 600.
Suppongo che il tasso di mutazione fosse così alto che il genoma dei ratti vincitori si è trasformato troppo spesso in varianti meno efficienti.

Come funziona questo

Con i rilevatori di teletrasporto aggiunti, il numero di partite fallite (punteggio <10) è diminuito in modo significativo.
In una prova del 2000, c'erano solo 1/3 di guasti.
La media geometrica è aumentata solo da 2900 a 3300, ma questo numero non riflette il miglioramento.

I colori vuoti sono spesso intesi come travi e pericoli (di solito da 2 a 5). Il genoma "usa" questi colori per bloccare i percorsi che metterebbero i ratti nei guai.

Il genoma è abbastanza bravo a indovinare le trappole (cioè una volta che i ratti sono in grado di raggiungere l'obiettivo, i colori che rappresentano i rilevatori di trappole reali vengono indovinati circa il 90% delle volte).
Fa anche uso dei nuovi codici di raggio per i teletrasporto, anche se più raramente (probabilmente perché i teletrasporto "insidiosi" sono meno comuni delle trappole e altri colori di raggio / pericolo si evolvono per bloccare il percorso verso le ultime istanze di questi traditori).

A giudicare dal numero di giochi in cui emerge un genoma vincente dopo 5000 o più turni, credo che questa nuova razza trarrebbe grandi benefici da un aumento del tasso di mutazione.


Poiché esiste un numero pari di trappole, vuoti, muri e teletrasporti, sono necessari solo 3 bit per memorizzare accuratamente le proporzioni (anche se si considerano trappole == muri). Inoltre, hai preso in considerazione / scartato l'idea di utilizzare i bit di offset trap non utilizzati nell'anti-wallbanging? Dal momento che l'obiettivo non è quello di non ereditano dai genitori, si potrebbe effettivamente utilizzare tutti i bit nel campo anti-wallbanging. Nessun motivo per loro di essere unici, non credo.
Mooing Duck,

1
@MooingDuck Ho testato la tua idea di riutilizzare i bit di offset, ma non è riuscito. Come temevo, riutilizzare un'informazione per due scopi diversi non sembra funzionare. Supponiamo ad esempio che i bit di offset di un determinato colore siano necessari affinché un genoma scelga la direzione verticale corretta in un determinato percorso, questo colore non può più rappresentare una trappola significativa senza distruggere il percorso che dipende dagli stessi dati. Ho anche provato a usare 6 bit, ma temevo che anche i restanti 4 bangers anti wall fossero scarsi.

1
Buono a sapersi, ma ho suggerito due idee lì, una era di usare tutti i bit (riutilizzandone alcuni), e l'altro era usare i bit di offset della trappola non usati per muri / vuoti. Hai provato entrambi? (Capisco perfettamente se non vuoi provare, non ti viene quasi richiesto di provare se non vuoi)
Mooing Duck

1
Ho provato entrambi, ma entrambi hanno fallito. Gli offset di trap sono importanti anche quando un gene non li utilizza, poiché questo gene può ancora mutare in un colore di trap, nel qual caso l'offset di trap probabilmente sarà mutato in qualsiasi contesto contenga i bit più redditizi e abbia perso il suo significato di offset . Ora tornerà al valore di offset redditizio e distruggerà i percorsi dei ratti che dipendono da esso come indicatori contestuali. Penso di aver visto il caso di tale oscillazione con il mio strumento grafico, ma non è facile esibire una chiara istanza di questo problema.

16

ColorScorePlayer, punteggio preliminare ≈ 22

Questo è il robot che vedi al lavoro nella GIF nella sfida.

Questo è stato il nostro robot di prova durante la fase di sviluppo. Utilizza il genoma per memorizzare un punteggio di qualità per ciascuno dei 16 colori. Quindi effettua la mossa in avanti che la sposta sul colore con il miglior punteggio (e non si sposta mai su -1). In caso di pareggio, viene scelta una mossa casuale tra le celle.

Abbiamo eseguito il porting di questo lettore in tutte le lingue del controller, quindi funge da esempio per come usarli:

Pitone

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)

Rubino

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; 
}

Giava

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;
    }
}

Il giocatore segna abbastanza incoerentemente. Ecco 50 esecuzioni casuali:

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

Questa sfida è davvero molto divertente e semplice se la provi.

Non lasciarti scoraggiare dalla lunga descrizione.
Basta visitare GitHub e controllare le cose ... tutto sarà molto più chiaro! :)

Il simulatore C ++ è altamente raccomandato per la sua velocità. Anche dopo aver finito di tradurre il mio programma Python in C ++, la simulazione Python non si è ancora fermata.

Questa è una variante migliorata di ColorScorePlayer. Per fare buon uso della sua vista 5x5, considera le mosse a 2 passi da essa usando una funzione ponderata. Muoviti di 1 passo avanti ad esso viene dato un peso maggiore, poiché hanno un effetto più immediato sulla sopravvivenza. Spostati di 2 passi avanti hanno un peso inferiore.

Cerca di avanzare, ma se non si vede alcuna mossa sicura ... quindi prova lateralmente ... e se tutto il resto fallisce, si sposta all'indietro in modo casuale.

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())];

}

Punto:

Ci sono un bel po 'di 1 ... che può essere un po' deprimente quando vedi la console vomitare 1 dopo l'altro. Come un pianeta con tutte le necessità per la vita, ma nessun segno di civiltà avanzate dei topi ...
Poi il picco occasionale. :)

Hmm ... a quanto pare sono stato fortunato per il mio primo lotto di piste, ottenendo una geometria di 300+. I punteggi davvero oscillano un po '. Ma comunque, con più corse del simulatore, è probabilmente più vicino a ≈ 74. (Grazie per avermi aiutato a simulare e il suo programma velocissimo)

Punteggi delle mie piste: 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 556 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 1 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


1
Ho ottenuto una media geometrica di 74.7 con 1000 giochi, bel lavoro.
feersum

8

Bishop - Python, punteggio preliminare 1.901

Il Vescovo si muove sempre in diagonale, quindi la metà della tavola è inaccessibile su un determinato trek lungo la tavola, ma ciò significa un minor numero di mosse potenziali da codificare, quindi ogni singolo bit del genoma può rappresentare una mossa (il Vescovo non si ritira mai). Quale bit a cui fare riferimento viene deciso in base al blocco 3x3 di quadrati davanti (a destra) del campione. La mossa migliore per una determinata situazione è sempre e solo una mutazione di un po '.

Questo robot impara rapidamente all'inizio, ma poi colpisce spesso un soffitto prima di raggiungere il traguardo, presumibilmente dove si verifica uno dei due seguenti problemi:

  • Due o più parti della mappa vengono mappate sullo stesso bit ma richiedono mosse diverse.
  • Alcune schede non sono passabili usando solo mosse diagonali.

Codice

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

Nonostante queste limitazioni, in rare occasioni il Vescovo fa bene, con singoli esemplari che completano ciascuno diversi giri del tabellone. Avevo pensato che in un dato giro un esemplare potesse muoversi solo su metà della scacchiera (equivalente solo ai quadrati neri o solo ai quadrati bianchi su una scacchiera). Tuttavia, come ha sottolineato Martin Büttner, un teletrasporto può spostare un campione da un quadrato nero a un quadrato bianco o viceversa, quindi sulla maggior parte delle schede non saranno limitati.

(Esistono due coppie di tipi di teletrasporto corrispondenti e ognuno ha una probabilità di 0,5 di avere un offset che sposta un campione nell'altra metà dei quadrati bianchi e neri. Quindi la probabilità di una scheda ha solo teletrasporto che limitano il campione a uno la metà della tavola al giro è solo 0,25.)

I punteggi mostrano che i trionfi occasionali sono intervallati da lunghi periodi di mancato arrivo:

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

Run-bonus player: Geometrica media 50.35 (test di 5000 partite)

Questo bot segna i quadrati in base ai loro singoli colori in base a una sezione di DNA a 6 bit come il player del punteggio di colore, ma con un sistema di numerazione diverso. Questo bot è stato motivato dal pensiero che è piuttosto arbitrario che uno dei bit cambi il valore del punteggio di 32, mentre un altro lo fa solo di 1. Assegna il valore n (n + 1) / 2 a una serie di n 1 bit consecutivi. Inoltre, aggiunge un meccanismo di randomizzazione nel tentativo di evitare di rimanere bloccato. Farà una mossa casuale in avanti con una probabilità 1 su 30.

Per confronto, il giocatore con punteggio di colore ha segnato da 30 a 35 in un paio di test da 1000 partite. È interessante notare che il punteggio massimo di gioco del giocatore con punteggio di colore era compreso tra 3-5 milioni, mentre il massimo di run-bonus era di soli 200 mila. I vantaggi del run-bonus beneficiano del sistema di punteggio medio logaritmico ottenendo un punteggio diverso da zero in modo più coerente.

L'esecuzione di 5000 giochi ha richiesto circa 20 minuti con 6 thread sul controller 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)]};
}

solo per curiosità, quanto tempo ha impiegato il test delle 5000 tracce? I miei topi hanno bisogno di più di un'ora per completare 1000 tracce, quindi dovrei lasciare che il computer funzioni tutta la notte per riprodurre il tuo test case.

@kuroineko La risposta alla tua domanda era già nella mia risposta.
feersum

Oops scusa. Quindi proverò il tuo codice sul mio PC per vedere quale parte dell'hardware gioca nella differenza di velocità. E forse prova a usare gcc invece di MSVC. Ho notato un aumento delle prestazioni del 30% rispetto a MSVC su un paio di altri bit di codice pesanti per il calcolo.

Il tuo codice è durato poco più di 20 minuti per 1000 tracce sul mio i3-2100@3.1GHz con 4 thread. Il punteggio era di circa 56 . Sembra che il mio PC sia 5 volte più lento del tuo e il mio codice sarebbe circa 6 volte più lento su una determinata macchina (ma avere un punteggio migliore implica meccanicamente un tempo di calcolo più lungo). Dato che sono troppo al

8

StarPlayer | C ++ | Punteggio: 162 (basato su 500 corse)

Questo giocatore cerca di usare A * per trovare la via migliore. Assegna pesi allo stesso modo di ColorScorePlayer e cerca di trovare il percorso verso il bordo destro della vista. L'implementazione non è la più carina che abbia mai fatto ma almeno non è troppo 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;
}

Punteggi campione:

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


1
In 1000 partite ho ottenuto un punteggio di 133.2, bello.
feersum

7

WallGuesser: segnati 113.266 in un test di 1000 giochi

Codifica

Ho realizzato una codifica a 6 bit / colori davvero semplice. Per decodificare il colore [n]

  • Somma ogni ennesimo bit nel genoma fino a 96
  • Se il punteggio della somma è> = 4, allora dire che questo quadrato è bloccato
  • Se il punteggio di somma è <= 4, allora il suo punteggio finale è 2 ^ del suo punteggio di somma

Diffondendo i bit per un colore in tutto il genoma, sto aumentando la possibilità che i bit di entrambi i genitori vengano utilizzati per ciascun colore.

Movimento

Uso una ricerca basata su A * (non sono certo molto efficiente) per cercare il percorso più economico verso uno dei quadrati del bordo destro. Se un colore viene mappato su "bloccato", non verrà mai inserito dalla ricerca. Se la ricerca non riesce a trovare un percorso presuppone che questo topo non sia adatto alla riproduzione e cerca di terminarlo spostandone uno a sinistra.

Ridurre il numero di ratti inadatti

Dal momento che il mio genoma sta effettivamente indovinando su quali quadrati siano i teletrasporti a parete o all'indietro, i topi che non hanno ipotesi (nessun colore mappato al blocco) non sono molto adatti. Per provare a rimuovere questi ratti se nessun colore viene contrassegnato come bloccato, OGNI colore viene contrassegnato come bloccato e il ratto si sposterà sempre a sinistra.

FARE

Attualmente non c'è casualità nel comportamento, quindi è facile per i ratti rimanere bloccati.

#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, questo non si compila con me g++ -std=c++11 .\wallguesser.cpp -O2 -o .\wallguesser.exe. Ricevo molti errori ma il primo è.\wallguesser.cpp:47:19: error: 'dna_t' has no member named 'at' if (d.at(i) == true){
Martin Ender,

Nessun problema, semplicemente cambiando atper []risolverlo.
feersum

7

Il più veloce - Punteggio medio geometrico: ~ 922 (2K corse)

Il mio approccio è quello di:

  1. Scopri cosa uccide la specie e definisci il comportamento desiderato (funzionale)
  2. Implementare il comportamento desiderato nel codice (tecnico)
  3. Dagli una priorità . È più importante o meno importante di altri comportamenti desiderati.
  4. Ottimizza il punteggio medio geometrico modificando i parametri delle soluzioni.

Ho testato oltre 2000 set di parametri con gli stessi 50 semi. I set più promettenti sono stati selezionati e sono stati segnati usando 250 semi identici e quelli con il punteggio più alto sono stati l'input per il prossimo round di test. Così sono riuscito a creare un algoritmo genetico per trovare l'algoritmo genetico ottimale per questo problema, come suggerito dall'utente mbomb007 .

Il comportamento desiderato:

  1. Le specie dovrebbero imparare quali colori sono sicuri e quali cattivi.
  2. La specie dovrebbe principalmente focalizzare la propria decisione su dove spostarsi in base alle 3 celle proprio di fronte, ma se non sono disponibili buone mosse, si dovrebbero considerare le mosse verticali o all'indietro
  3. La specie dovrebbe anche guardare cosa c'è oltre le 8 cellule che lo circondano e usarlo nelle informazioni nel processo decisionale
  4. Le specie dovrebbero imparare a identificare le trappole .
  5. Alcune specie presumono erroneamente che i muri siano buoni e provano sempre ad avvicinarsi a loro e quindi rimangono bloccati davanti ai muri. Se sono le specie con il punteggio più alto in quel momento, il loro DNA con l'ipotesi sbagliata sul muro viene duplicato più volte nei neonati . Dopo qualche tempo tutte le specie sono bloccate davanti alle pareti e nessuna di esse raggiunge l'obiettivo di segnare punti. Come fermare i deficienti?

Metodi di archiviazione dei dati:

Vogliamo che le specie imparino le cose, si adattino al suo ambiente, diventino le più adatte. Inevitabilmente funziona solo se l'apprendimento può essere memorizzato in qualche modo. L'apprendimento verrà "memorizzato" nei 100 bit del DNA. È uno strano modo di immagazzinare, perché non possiamo cambiare il valore del nostro DNA. Quindi supponiamo che il DNA memorizzi già informazioni su mosse cattive e buone. Se per una determinata specie vengono memorizzate le informazioni corrette nel suo DNA, egli avanzerà rapidamente e produrrà molte nuove specie con il suo DNA.

Ho scoperto che il punteggio medio geometrico è sensibile al modo in cui le informazioni sono memorizzate. Supponiamo di leggere i primi 4 bit dei 100 bit di DNA e di volerli memorizzare in una variabile intera. Possiamo farlo in diversi modi:

  1. memorizzazione dei dati decimali: utilizzando la funzione 'integrata' dnarange, esempio: 4 bit 1011diventerà `1x2 ^ 3 + 0x2 ^ 2 + 1x2 ^ 1 + 1x2 ^ 0 = 15. Valori possibili (per 4 bit): [0, 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  2. memorizzazione dei dati delle strisce: usando la dnaStreakRangefunzione (definita di seguito), diventerà un esempio: 4bits 1011 1x1 + 0x1 + 1x1+ 1x2 = 4. Valori possibili (per 4 bit): [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. memorizzazione dei dati bitsum: usando la dnaCountRangefunzione (definita di seguito), diventerà un esempio: 4bits 1011 1x1 + 0x1 + 1x1 + 1x1 = 3. Valori possibili (per 4 bit): [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;
}

Le differenze tra i metodi di archiviazione sono:

  • Il metodo di memorizzazione decimale è vulnerabile per una singola modifica del DNA. Quando il valore del bitume cambia da 1011 a 0011, il suo valore cambia da 3 a 2, il che è una modifica minore.
  • Il metodo di archiviazione decimale è omogeneo . Ognuno dei possibili valori ha lo stesso cambiamento in atto. La possibilità di leggere il valore di 15 da un blocco di memoria di archiviazione a 4 bit è 1/16 = 6%. Il metodo di archiviazione delle strisce non è omogeneo . La possibilità che un valore di 4 bit della striscia sia inferiore o uguale a 6 è (15-3) / 16 = 81% (tutte le 16 combinazioni tranne 0111,1110,111). Sotto un elemento visivo che mostra la forma della distribuzione. Come puoi vedere dalla freccia blu, la possibilità che una sequenza di 4 bit sia inferiore o uguale a 6 è dell'81%: visualizzazione della distribuzione di tipi di memorizzazione decimale, a strisce e bituminosi per numeri binari lunghi 4,5 e 6 bit

Dai la priorità alle soluzioni.

Quando ColorScorePlayer ha identificato due mosse in avanti con punteggi identici, viene fatta una scelta arbitraria. IMHO, non si dovrebbe mai usare la funzione random v.rng.rint()funzioni . Invece dovresti usare questa opportunità di punteggi uguali come hook per valutare soluzioni per effetti del secondo ordine.

L'effetto del primo ordine ottiene la massima priorità. Se vengono raggiunti punteggi uguali, prevale la soluzione con la priorità 2 e così via. Modificando i parametri di una soluzione è possibile influenzare la probabilità che si verifichino punteggi uguali e in tal modo modificare il peso delle soluzioni con priorità 1 e priorità 2.

Implementazione del comportamento desiderato

Scopri quali colori sono sicuri:

  • Il 33% dei 16 colori è scadente e quindi quando il punteggio di una mossa è inferiore a 63/3 la mossa non sarà consentita. Pertanto threshold = 63/3=21, dove 63 è il punteggio massimo per 6 bit e 33% = 1/3 (può essere cercato nel grafico sopra).

Se non sono disponibili mosse valide, sposta in verticale o all'indietro:

  • Quando non sono consentite mosse in avanti, le mosse verticali verranno confrontate tra loro allo stesso modo. Se anche non sono consentite mosse verticali, le mosse all'indietro vengono classificate. Questo si ottiene tramite la weightMovevariabile.

Guarda cosa c'è oltre:

  • Quando 2 o 3 mosse hanno punteggi identici, la casella 3x3 attorno a quelle mosse determinerà (tramite x2e y2loop) qual è l'opzione migliore (tramite la mainSubScorevariabile). La colonna più a destra in quella casella 3x3 è in testa.
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};
}

Punteggio: 123 (2K run)

Primi 50 punteggi (18 partite hanno segnato solo 1 punto):

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

Identifica le trappole:

Ho esaminato il DNA della specie con il punteggio più alto quando un gioco arbitrario è terminato usando la memoria a bitsum4 (quindi il punteggio di colore ha un intervallo [0,4]):

  • segnato 0: teletrasporto all'indietro, entrambi i muri, 1x cassaforte
  • segnato 1: Trappola all'indietro (così innocuo), Teletrasporto all'indietro, 1x sicuro
  • segnato 2: Trappola in avanti (così pericoloso), 1x sicuro
  • segnato 3: teletrasporto in avanti, 5 volte sicuro
  • segnato 4: teletrasporto in avanti, 1x sicuro

Da ciò si può concludere che muri e teletrasporto ottengono un punteggio corretto. Le trappole non vengono identificate poiché dipendono dalla direzione e dal colore di origine, mentre il punteggio viene eseguito sul colore di destinazione. Pertanto, è necessario memorizzare anche i dati sul colore di origine, quindi v(0,0). In un mondo ideale vorremmo memorizzare informazioni per 16 colori x 8 direzioni x 3 bit = 384 bit.

Sfortunatamente, ci sono solo 100 bit disponibili e non possiamo usarli tutti poiché abbiamo anche bisogno di memoria per la soluzione spiegata sopra. Pertanto realizzeremo 4 contenitori per i colori:

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

e 4 bin di direzione di movimento

  • 0: sposta in verticale o all'indietro,
  • 1: vai avanti,
  • 2: andare avanti,
  • 3: andare avanti verso il basso

Quando il punteggio decimale è 4 o superiore (100.101.110.111), si presume che a questa cella sia associata una trappola, di conseguenza questa mossa non verrà presa quando sorgono punteggi uguali. Quindi l'identificazione delle trappole è un effetto del secondo ordine e "guardare ciò che c'è oltre" sarà una soluzione di terza priorità.

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};
}

Punteggio: 580 (2K run)

Primi 50 punteggi (13 partite hanno segnato solo 1 punto):

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 1 2 74.167 1 71.942 1 54.948 1

Un'ipotesi errata sul muro è duplicata più volte nei neonati da idioti:

Alcune specie presumono erroneamente che i muri siano buoni e provano sempre a spostarsi su di essi e quindi rimangono bloccati davanti ai muri. Possono anche rimanere bloccati in infiniti anelli di teletrasporto. L'effetto è lo stesso in entrambi i casi.

Il problema principale è che dopo alcune centinaia di iterazioni alcuni geni diventano molto dominanti . Se questi sono i geni "giusti", puoi ottenere punteggi molto alti (> 1 milione di punti). Se questi sono sbagliati, sei bloccato, poiché hai bisogno della diversità per trovare i geni "giusti".

Moroni in lotta: Soluzione 1: inversione di colore

La prima soluzione che ho provato è stata uno sforzo per utilizzare una parte della memoria inutilizzata che è ancora molto diversa. Supponiamo che tu abbia assegnato 84 bit alla tua memoria colore e che la memoria di ricerca trap. I restanti 16 bit saranno molto diversi. Possiamo riempire 2 variabili decimali8 che hanno valori sull'intervallo [0,255] e sono omogenee, il che significa che ogni valore ha una probabilità di 1/256. Le variabili verranno chiamate inInversee inReverse.

Se è inInverseuguale a 255 (una possibilità di 1/256), invertiremo l'interpretazione dei punteggi di colore . Quindi il muro che il deficiente ritiene sicuro a causa del suo punteggio elevato, otterrà un punteggio basso e quindi diventerà una mossa sbagliata. Lo svantaggio è che ciò influirà anche sui geni dei "diritti", quindi avremo punteggi meno alti. Inoltre questa inInversespecie dovrà riprodursi e anche i suoi figli avranno parti del DNA dominante. La parte più importante è che riporta la diversità.

Se è inReverseuguale a 255 (una possibilità di 1/256), invertiremo l'ordine delle posizioni di memorizzazione dei punteggi dei colori . Quindi prima che il colore 0 fosse memorizzato nei bit 0-3. Ora il colore 15 verrà memorizzato in quella posizione. La differenza con l' inInverseapproccio è che la inReversevolontà annullerà il lavoro svolto finora. Siamo tornati al punto di partenza. Abbiamo creato una specie che ha geni simili a quando è iniziato il gioco (ad eccezione della trappola che trova memoria)

Tramite l'ottimizzazione viene testato se è saggio utilizzare inInversee inReverseallo stesso tempo. Dopo l'ottimizzazione si è concluso che il punteggio non è aumentato. Il problema è che abbiamo una popolazione geneticamente più diversificata, ma ciò influisce anche sul "DNA giusto". Abbiamo bisogno di un'altra soluzione.

Moroni che combattono: Soluzione 2: codice hash

La specie ha 15 possibili posizioni di partenza e attualmente c'è una possibilità troppo grande che seguirà esattamente lo stesso percorso se inizia nella stessa posizione di partenza. Se è un idiota che ama i muri, si bloccherà più volte sullo stesso muro. Se per fortuna è riuscito a raggiungere un muro molto più avanti, inizierà a dominare il pool di DNA con i suoi presupposti sbagliati. Ciò di cui abbiamo bisogno è che la sua prole seguirà un percorso leggermente diverso (poiché per lui è comunque troppo tardi) e non rimarrà bloccato sul muro più lontano, ma su un muro più vicino . Ciò può essere ottenuto introducendo un hashcode .

Un hashcode dovrebbe avere lo scopo di identificare ed etichettare in modo univoco la posizione corrente sul tabellone. Lo scopo non è quello di scoprire qual è la posizione (x, y), ma di rispondere alle domande che i miei antenati sono stati prima in questa posizione?

Supponiamo che tu abbia la tavola completa di fronte a te e realizzeresti un jpg di ogni quadrato di 5 per 5 celle possibile. Si finirà con (53-5) x (15-5) = 380 immagini. Diamo quei numeri di immagini da 1 a 380. Il nostro hashcode dovrebbe essere visto come tale ID, con quel diverso che non va da 1 a 330, ma ha ID mancanti, quindi ad esempio 563, 3424, 9424, 21245, ecc.

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;
    }
}       

I numeri primi 17e 31sono lì per evitare che le informazioni aggiunte all'inizio del ciclo scompaiano. Più avanti su come integrare il nostro hashcode nel resto del programma.

Sostituiamo il meccanismo di sottoscrizione "guarda cosa c'è oltre" con un altro meccanismo di sottoscrizione. Quando due o tre celle hanno punteggi principali uguali, ci sarà una probabilità del 50% di scegliere quella superiore, una probabilità del 50% di raccogliere le celle inferiori e una probabilità dello 0% di scegliere quella centrale. La probabilità non sarà determinata dal generatore casuale, ma dai bit della memoria , poiché in questo modo ci assicuriamo che nella stessa situazione venga fatta la stessa scelta.

In un mondo ideale (dove abbiamo una quantità infinita di memoria), calcoleremmo un codice hash univoco per la nostra situazione attuale, ad esempio 25881, e andremmo nella posizione di memoria 25881 e leggiamo lì se dovremmo scegliere la cella superiore o inferiore (quando c'è è un punteggio uguale). In questo modo saremmo nella stessa identica situazione (quando, ad esempio, viaggiamo per la seconda volta sul tabellone e iniziamo dalla stessa posizione) prendere le stesse decisioni. Poiché non abbiamo memoria infinita, applicheremo un modulo della dimensione della memoria disponibile all'hashcode . L'attuale hashcode è buono nel senso che la distribuzione dopo l'operazione modulo è omogenea.

Quando la prole viaggia sulla stessa tavola con un DNA leggermente modificato, nella maggior parte dei casi (> 99%) prenderà esattamente la stessa decisione. Ma più si avvicina, più aumenta la possibilità che il suo percorso si riempia in modo diverso dai suoi antenati. Quindi la possibilità che rimanga bloccato su questa parete molto più avanti è piccola. Mentre rimane bloccato sullo stesso muro vicino del suo antenato è relativamente grande, ma questo non è poi così male, dal momento che non genererà molta prole. Senza l' approccio hashcode , la possibilità di rimanere bloccati sul muro vicino e lontano è quasi la stessa

Ottimizzazione

Dopo l'ottimizzazione, si è concluso che la tabella di identificazione del trap non è necessaria e che sono sufficienti 2 bit per colore. Il resto della memoria 100-2x16 = 68 bit viene utilizzato per memorizzare il codice hash. Sembra che il meccanismo del codice hash sia in grado di evitare trappole.

Ho ottimizzato per 15 parametri. Questo codice includeva il miglior set di parametri ottimizzati (finora):

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};
}   

Punteggio: 922 (2K run)

Primi 50 punteggi (9 partite hanno segnato solo 1 punto):

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.119 4.990 77.4326.6

Questo è il mio primo programma C ++. Come molti di voi ora ho esperienza nell'analisi degli gnomi. Voglio ringraziare gli organizzatori, dal momento che mi è davvero piaciuto lavorare su questo.

In caso di feedback, si prega di lasciare un commento qui sotto. Ci scusiamo per i lunghi testi.


Trovo che la tua analisi delle trappole sia piuttosto interessante.

Hai provato un'altra funzione hash, come ad esempio xoring i 25 valori di colore visti come 12,5 parole a 16 bit e prendendo un modulo? Non sono convinto che la congruenza dei numeri primi dia una migliore omogeneità, ma non sono un grande matematico.

Inoltre, hai preso in considerazione l'aggiunta di un algoritmo di tracciamento? Sembra essere un enorme fattore di miglioramento a prescindere dal genoma, poiché limiterà le mosse a quelli che testano la capacità del genoma solo lungo percorsi che hanno molte più probabilità di portare a una posizione vincente.

Kuroi, grazie per il tuo feedback. Non ho provato il xoring, dal momento che non sono così familiare con le operazioni binarie in c ++. Suppongo che intendi parole di 12,5 8 bit? Stai usando xoring?
Ruut

Puoi guardare il mio codice "credenti forti" per vedere che tipo di funzione hash che uso. Fondamentalmente salto le celle fuori pista e considero i colori su pista come parti di ordine alto e basso di una parola di 16 bit. Tutte queste parole vengono accumulate con XOR in un registro che viene quindi diviso per la dimensione della tabella hash. Finché il valore hash max (65535) è molto più alto della dimensione della tabella (<100), il modulo ha una buona potenza di diffusione. L'ho testato su una vasta gamma di griglie generate casualmente e sembra avere una buona omogeneità.

6

Pathfinder, C ++, punteggio preliminare 35.8504 (50 round)

Una revisione completa! Ho portato il mio algoritmo su C ++ e l'ho modificato un po ', ma il punteggio non è ancora molto alto, probabilmente perché i topi continuano a sbattere la testa contro i muri. Sono stanco di provare a migliorare questo, quindi lo lascerò per ora.


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};
}

Spiegazione

L'idea generale è classificare ogni colore come una trappola o meno, quindi assegnare le direzioni alle trappole e ai pesi alle non-trappole e provare a seguire il percorso di peso minimo fino al bordo destro della griglia di visione.

Nei primi 80 bit del genoma, ogni colore è classificato usando 5 bit abcde. Se ab = 01, il colore è una trappola e cdecodifica la sua direzione (otto possibilità). Se ab ≠ 01il colore non è una trappola e il suo peso lo è a + b + 2*(c + d + e).

Successivamente, inizializziamo una griglia 3x7, che rappresenta il campo visivo del ratto alla sua destra, imbottito con colori "sconosciuti". I bit 80-84 codificano il peso delle celle sconosciute in modo simile ai colori non-trap, e i bit 85-89 codificano un peso comune per le trap. Riempiamo la griglia con i pesi, calcoliamo i percorsi più corti e aggiungiamo un po 'di peso extra (codificato nei bit 90-95) alle celle direttamente sopra e sotto il ratto per scoraggiare lo scostamento. I bit 95-99 codificano un peso obiettivo. Se il peso minimo di un percorso è al di sotto di esso, il ratto è probabilmente bloccato da qualche parte e procede a muoversi in modo casuale (ma mai indietro). Altrimenti, segue il percorso di peso minimo. Con una piccola probabilità a seconda del peso che previene la deviazione, il ratto sceglie invece il percorso del peso dal secondo al minimo. Questo per evitare di rimanere bloccati sui muri (ma sembra non funzionare molto bene in questo momento).


Ho eseguito la tua implementazione sul mio computer. Ci sono volute alcune ore. Ottiene un punteggio medio di 7,848433940863856 punti. pastebin.com/d50GcwnK
Jakube

@Jakube Grazie mille! È molto peggio di quanto mi aspettassi, ma ora che guardo di nuovo il codice, vedo diversi bug e altre stranezze. Proverò a portarlo su C ++ più tardi in modo da poterlo analizzare da solo.
Zgarb,

5

LookAheadPlayer C ++ ≈ 89.904

Il mio pensiero originale era di cercare 4 bit che corrispondessero al colore che stavo cercando e di usare i seguenti bit come punteggio. Questa si è rivelata un'idea terribile, probabilmente a causa di mutazioni.

Quindi ho pensato ai modi per proteggere da mutazioni e crossover, e mi ha ricordato il lavoro che ho fatto sulla decodifica del codice QR. Nei codici QR i dati sono suddivisi in blocchi e strisce per evitare che gli errori distruggano troppa parte di dati.

Pertanto, come ColorScorePlayer, taglio il DNA in 16 pezzi e li uso come punteggio dato. Tuttavia, i punteggi sono a strisce in modo che i singoli bit di ciascun punteggio non siano adiacenti. Quindi riassumo il punteggio sia delle mosse possibili attuali sia delle mosse potenziali successive e scelgo la mossa migliore da effettuare.

Nota: questo è stato codificato / testato su MinGW. Non si compila con ottimizzazioni o con il multithreading. Non ho una vera installazione Linux o Visual Studio a portata di mano per usare un compilatore dove funzioneranno. Lo testerò rapidamente domani mattina, ma per favore fatemi sapere se si verificano problemi.

// 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 ++ (punteggio 9.7)

Non possiamo fare affidamento sull'interpretazione di blocchi del genoma come numeri perché un singolo bit-flip può avere effetti radicalmente diversi a seconda della sua posizione. Ecco perché uso semplicemente 16 segmenti a 6 bit e li punteggio sul numero di 1s. Inizialmente 111111era buono ed 000000era cattivo, e mentre non importa a lungo termine (una volta che il genoma è completamente evoluto) nella configurazione iniziale del DNA la maggior parte dei segmenti ne ha 2-4, quindi sono passato all'utilizzo 9 - (#1 - 3)^2per il punteggio, questo consente molta più libertà di movimento nei primi turni e un'evoluzione più rapida.

In questo momento guardo solo i 7 vicini più vicini, aggiungo un bias di direzione al punteggio di colore e mi muovo in una delle direzioni più alte in modo casuale.

Sebbene il punteggio stesso non sia molto alto, le mie creature raggiungono il traguardo e segnano> 1 in 3/4 dei casi.

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 un campione di punteggio su 100 schede

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

Punteggio medio geometrico: 9.76557


Il punteggio menzionato per una scheda utilizza il tasso di mutazione standard o il valore corretto?
trichoplax,

"Le mie creature raggiungono il traguardo e segnano> 1 in 3/4 dei casi" Vorrei che la metrica del punteggio lo
premiasse

5

WeightChooser | C # | Punteggi: 220.8262 in 1520 partite

Calcola il peso per la possibile prossima mossa (blu) in base al peso medio delle possibili mosse seguite (giallo)

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

RATS IN ACTION (non una risposta, ma uno strumento grafico per i robot C ++)

Dall'inizio di questa sfida, ho avuto difficoltà a capire cosa stavano affrontando i topi in pista.
Alla fine ho hackerato il controller e ho scritto uno strumento laterale per ottenere una rappresentazione grafica di una traccia.
Alla fine ho fatto un po 'più di hacking e ho aggiunto una visualizzazione dei possibili percorsi del DNA di un dato topo.

La mappa è estremamente ingombra e richiede un po 'di tempo per abituarsi, ma ho trovato abbastanza utile capire come funzionavano i miei robot.

Ecco un esempio:

traccia campione

Probabilmente dovrai ingrandire per vedere qualcosa, quindi ecco solo la prima metà:

half track (nessun gioco di parole previsto)

Inizialmente, diamo un'occhiata ai percorsi del topo. C'è un percorso per ogni possibile posizione iniziale (di solito 15, a volte un po 'meno). Di solito tendono a fondersi, portando idealmente a un'unica posizione di vittoria.

I percorsi sono rappresentati da grandi frecce diritte. Il colore descrive il risultato:

  • verde: vincere
  • giallo: ciclo infinito
  • marrone: sbattere a parete
  • rosso: sfortunato incidente

Nell'esempio, abbiamo 12 posizioni di partenza vincenti, una che porta a un loop infinito e due a una morte estenuante (essendo teletrasportato in una trappola, come sembra).

Le discontinuità del percorso sono dovute a teletrasporti, che è possibile seguire con le frecce curve corrispondenti.

Ora per i simboli colorati. Rappresentano il significato dei 16 colori (quelli grigi rappresentano ciò che vede un topo).

  • muro: quadrato
  • teletrasporto: 4 stelle ramificate
  • rilevatore di trappole: piccolo ottagono

i colori vuoti sono ... beh ... vuoti.

I teletrasporto hanno frecce in uscita che indicano la loro destinazione.

I rilevatori di trappole hanno anche frecce che indicano la trappola, che è raffigurata come un cerchio rosso.
In un caso su 9, la trappola si trova nella stessa cella del suo rivelatore, nel qual caso vedrai il piccolo ottagono in cima al cerchio rosso.

È il caso della trappola giallo pallida in questo esempio.
Puoi anche vedere i rilevatori di trappole malva che puntano verso il basso verso la loro trappola indicata.

Nota che a volte il cerchio rosso di una trappola sarà nascosto sotto un muro. Entrambi sono letali, quindi il risultato è lo stesso in caso di teletrasporto.
Si noti inoltre che una trappola potrebbe trovarsi su un teletrasporto, nel qual caso il teletrasporto ha la precedenza (cioè il ratto viene teletrasportato prima di cadere nella trappola, in effetti neutralizzando la trappola).

Infine, i simboli grigi rappresentano ciò che vedono i miei topi (ovvero il significato che il loro genoma attribuisce ai colori).

  • muro: quadrato
  • rilevatore di trappole: ottagono
  • trappola: X

Fondamentalmente, tutte le cellule sedute su un quadrato grigio sono considerate pareti dal ratto.
Le grandi X rappresentano le cellule considerate trappole, con i corrispondenti ottagoni che indicano il rilevatore che le riportava.

In questo esempio, entrambe le pareti sono identificate come tali, così come la trappola giallo pallido (che indica in effetti una cellula mortale, quindi rappresentarla come un muro è corretta).
Il rilevatore di trappole malva è stato identificato come tale (si trova su un ottagono grigio), ma la posizione della trappola non è corretta (puoi vedere che alcuni cerchi rossi non hanno croci sotto di loro).

Su 4 teletrasporto, 2 sono considerati pareti (turchese e marrone chiaro) e 2 come celle vuote (rossastre e giallastre).

Alcune celle vuote sono considerate come rilevatori di trappole o pareti. Guardando da vicino, puoi vedere che questi "rilevatori difettosi" stanno davvero vietando l'ingresso nelle cellule che metterebbero nei guai il ratto, quindi anche se non corrispondono ai colori reali, hanno uno scopo preciso.

Il codice

Bene è un casino, ma funziona piuttosto bene.

Visto dal codice del giocatore, ho aggiunto solo un'interfaccia: una funzione di traccia usata per riportare il significato di un dato DNA. Nel mio caso ho usato 3 tipi (wall, trap detector e empty) ma fondamentalmente puoi produrre qualsiasi cosa sia legata al colore (o niente se non vuoi una grafica relativa al genoma).

Ho hackerato il controller per generare un'enorme stringa di caratteri che ha raccolto la descrizione della traccia e dei colori con una "corsa a secco" del DNA del ratto da tutte le posizioni possibili.

Significa che i risultati saranno davvero significativi solo se il bot non utilizza valori casuali. Altrimenti, i percorsi visualizzati rappresenteranno solo un possibile risultato.

Infine, tutte queste tracce vengono inserite in un grande file di testo che viene successivamente letto da un'utilità PHP che produce l'output grafico.

Nella versione attuale, faccio un'istantanea ogni volta che un topo muore dopo aver raggiunto una nuova forma fisica massima (che mostra abbastanza bene il progressivo perfezionamento del genoma senza richiedere troppe istantanee) e un'istantanea finale alla fine del gioco (che mostra il DNA di maggior successo).

Se qualcuno è interessato posso pubblicare il codice.

Chiaramente questo funziona solo per i bot C ++ e dovrai scrivere una funzione di traccia ed eventualmente modificare il codice PHP se vuoi visualizzare alcuni dati specifici del genoma (le figure grigie nel mio caso).
Anche senza informazioni specifiche sul DNA, puoi vedere i percorsi seguiti dal tuo DNA su una data mappa con pochissimo sforzo.

Perché un'uscita intermedia?

Prima di tutto, C ++ non ha una libreria grafica portatile decente di cui parlare, specialmente quando si utilizza MSVC. Anche se le build di Win32 sono di solito disponibili, spesso arrivano come ripensamenti e il numero di librerie esterne, pacchetti e altre necessità unix-like necessarie rende la scrittura di un'applicazione grafica semplice e veloce un dolore terribile in una parte del corpo che la decenza previene io dalla denominazione.

Ho considerato l'uso di Qt (circa l'unico ambiente che rende GUI portatile sviluppo / grafica in C ++ una semplice e anche piacevole compito, secondo la mia opinione - probabilmente perché aggiunge un sistema di messaggistica à la Objective C che C ++ manca gravemente e fa un lavoro incredibile di memoria di limitare gestione al minimo), ma questo sembrava un sovraccarico per l'attività in corso (e chiunque volesse usare il codice avrebbe dovuto installare l'SDK biggish - difficilmente ne vale la pena, immagino).

Anche assumendo una libreria portatile, non è necessario parlare di velocità (circa un secondo per produrre un'immagine è ampiamente sufficiente), e con la sua proverbiale rigidità e il disordine intrinseco, C ++ non è certamente lo strumento migliore per il lavoro.

Inoltre, avere un output di testo intermedio aggiunge molta flessibilità. Una volta che i dati sono presenti, è possibile utilizzarli per altri scopi (ad esempio analizzando le prestazioni dei robot).

Perché PHP?

Trovo il linguaggio estremamente semplice e adattabile, molto comodo per la prototipazione. L'ho reso il mio linguaggio preferito per le sfide del codice che non richiedono prestazioni estreme.
È un linguaggio terribile per giocare a golf, però, ma il golf non è mai stato la mia tazza di tè.

Suppongo che Python o Ruby sarebbero altrettanto piacevoli da usare per lo stesso scopo, ma non ho mai avuto occasione di fare un lavoro serio con loro, e ultimamente stavo lavorando su siti web, quindi lo è PHP.

Anche se non conosci la lingua, non dovrebbe essere troppo difficile modificare il codice in base alle tue esigenze. Basta non dimenticare le $s prima delle variabili, proprio come i bei vecchi tempi di base :).


1
Per favore, condividi il tuo strumento? Non vedo né codice né link nella tua risposta.
Franky,

5

SkyWalker - Python - segna meno di 231 in 50 partite

Quindi codice prima e poi alcune spiegazioni. Spero che nulla si sia rotto durante la copia.

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

Qualche spiegazione

Secondo me la differenza principale è che non codifico tutti i colori. Invece, provo a salvare il numero di colori che sono importanti. Secondo me quei colori sono trappole, muri e teletrasporto. Il campione non ha bisogno di conoscere il colore di una buona cellula. Pertanto, il mio genoma è strutturato nel modo seguente.

  • 2 x 8 bit per le trap, i primi 4 bit sono il numero di colore, gli altri 4 sono l'offset
  • 2 x 4 punte per pareti, solo il colore
  • 4 x 7 bit per i teletrasporto, ancora 4 bit per il colore, 3 per decidere bene o male

Ciò comporta un totale di 52 bit utilizzati. Tuttavia, utilizzo solo il primo bit dei 3 decider del teletrasporto (controllo se il numero è maggiore di 3). Pertanto, gli altri 2 potrebbero essere eliminati, lasciandomi a 44 bit utilizzati.

Ad ogni turno controllo ogni campo della mia visione se è uno dei cattivi colori (+ fuori bordo -1) e lo aggiungo a un elenco di campi in cui il campione non vuole spostarsi. Nel caso di una trap, aggiungo il campo che si trova sull'offset salvato per quel colore trap.

Sulla base dell'elenco di quei campi danneggiati viene calcolata la mossa successiva. L'ordine dei campi preferiti è:

  1. inoltrare
  2. su o giù
  3. all'indietro su o giù
  4. indietro

Se sono applicabili due campi di una categoria, uno viene scelto a caso.

risultati

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

Pensieri

  • Non ho idea, se sono stato fortunato con le 50 piste o se c'è davvero un po 'di saggezza nella mia strategia.

  • Le mie corse non sembrano mai decollare e ottenere punteggi super alti, ma tendono anche a trovare almeno alcune volte l'obiettivo

  • Qualche piccola casualità è buona per non rimanere bloccati in una trappola un po 'vicino alla fine della gara

  • Penso che i colori non speciali non siano mai cattivi. Tuttavia, i loro esempi possono essere dannosi, quando si trovano sull'offset di una trappola. Pertanto, etichettare un colore se non è trappola, muro o cattivo teletrasporto non ha senso.

  • Le pareti sono i più grandi nemici

miglioramenti

In primo luogo, anche se mi mancherà vedere i quadratini neri avvicinarsi sempre più all'obiettivo è necessaria una porta C ++ per condurre più test e ottenere un risultato più significativo.

Uno dei problemi principali è che se ci sono cellule cattive (o quelle che il campione pensa male) davanti al ratto, inizia facilmente a muoversi su e giù nei circoli. Questo potrebbe essere fermato o ridotto guardando 2 mosse avanti in quei casi e impedendogli di spostarsi in un campo in cui sarebbe appena tornato indietro.

Spesso ci vuole un po 'di tempo prima che un ratto con buoni geni raggiunga l'obiettivo e inizi a diffonderlo. Forse ho bisogno di una strategia per aumentare la diversità in quei casi.

Dato che i teletrasporto sono difficili da calcolare, forse dovrei dividere la popolazione in coloro che sono rischiosi e prendere sempre buoni teletrasporto e quelli che sono più preoccupati e prenderli solo se non c'è altra scelta.

Dovrei usare la seconda metà del mio genoma in qualche modo.


Provo anche a conservare i colori, ma alla fine ho concluso che non funziona perché otterrai il doppio. Ad esempio se self.bit_chunk(16, 4)e self.bit_chunk(20, 4)con entrambi i valori 0010hai effettivamente archiviato solo informazioni su una delle due trap.
Ruut il

Avevo bisogno di aggiungere un rientro a una riga per farlo funzionare - immagino che si sia perso durante la copia e incolla. L'ho aggiunto anche qui al tuo codice.
trichoplax,

Per chiunque desideri eseguire questo: viene eseguito in python 2 e può essere eseguito in python 3 modificando la singola occorrenza di itervaluesin values.
trichoplax,

Ho ottenuto i seguenti risultati: [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] Media geometrica 122.9220309940335
trichoplax

Sembra che avremmo bisogno di eseguire più di 50 giochi per ottenere un punteggio affidabile.
trichoplax,

3

Python, NeighboursOfNeighbors, Punteggio = 259,84395 oltre 100 partite

Questa è una variante di ColorScorePlayer. Ogni 6 bit memorizza un punteggio di qualità per un quadrato. Quando il bot sta eseguendo una mossa, segna ciascuno dei 3 quadrati in avanti - diagonale su, avanti e diagonale giù. Il punteggio è la qualità del quadrato più la metà della qualità media dei prossimi 3 quadrati. Questo dà al bot uno sguardo al futuro, senza schiacciare la qualità del primo quadrato. L'algoritmo è simile a LookAheadPlayer, che non ho visto prima di scrivere questa soluzione.

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)

Mancava un rientro su una riga. Immagino che si sia perso quando si incolla. L'ho aggiunto.
trichoplax,

In esecuzione in Python 3 si è lamentato del confronto di Nessuno durante il calcolo del massimo (punteggi). Quindi sono passato else Nonealla else 0riga precedente per calcolare il tuo punteggio. Spero che rimanga invariata la tua logica (non ho apportato modifiche al codice qui su SE a parte l'aggiunta del rientro perso).
trichoplax,

In esecuzione in Python 3 ho ottenuto i seguenti punteggi per questa risposta: [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

Una media geometrica di 428.3750848244933
trichoplax,

2

ROUS (Roditore di dimensioni insolite), Java, punteggio = 0

Questo frantuma l'ambiente circostante per decidere dove andare. Dato che il controller Java non funziona, non ho punteggi per questo. Questo andrà molto lontano se troverà alcuni teletrasporto per aiutarlo.Questo tende a estinguersi e ad arrestare il controller di tanto in tanto. Ciò è probabilmente dovuto al fatto che l'ambiente naturale è la Palude di fuoco.

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;
    }
}

1
Il controller Java ora funziona.
Martin Ender,

3
All'inizio pensavo che dovessi rendere un omaggio all'antica Russia, ma a quanto pare lo è stato per Rob Reiner.

Il punteggio minimo possibile è 1
trichoplax,

@trichoplax ... crash the controller ...
TheNumberOne

Oh, capisco, quindi succede abbastanza spesso che non riesci a raggiungere la fine di una corsa?
trichoplax,

2

Lookahead di colore grigio (C ++, ~ 1,35)

Questo non sta andando molto bene in media, ma in rare occasioni si esibisce in modo brillante. Sfortunatamente, ci viene assegnato un punteggio geometrico medio (1,35) e non il punteggio massimo (20077).

Questo algoritmo funziona semplicemente usando codici grigi a 4 bit per mappare il punteggio di ogni colore da qualche parte da -2 a 2 (con un orientamento verso l'intervallo [-1..1]), e calcola il punteggio della tessera di ogni mossa e le sue mosse successive . Utilizza anche un codice grigio a 2 bit per determinare il moltiplicatore per il riquadro stesso e il fattore di distorsione per spostarsi a destra. (I codici grigi sono molto meno suscettibili ai grandi salti a causa delle mutazioni, anche se in realtà non fanno alcun favore per il crossover punto di codice medio ...)

Inoltre non fa assolutamente nulla per provare a gestire le trappole in modo particolare, e sospetto che potrebbe essere la rovina (anche se non ho aggiunto alcuna strumentazione al controller per testare questa teoria).

Per ogni possibile mossa determina un punteggio e tra tutte le mosse con il punteggio più alto sceglie in modo casuale.

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())];
}

Nella mia corsa più recente, ho ottenuto i punteggi: 1 1 1 1 1 1 1 46 1 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

Vorrei poter ottenere più dei 20077 e meno degli 1. :)


1
L'uso del codice gray è un'idea grayt! ;)
matovitch il

1
+1 per i codici Gray. Tuttavia, un genoma completamente resistente alle mutazioni danneggerà un po 'la diversità. E tra un punteggio di 20.000 non è nemmeno vicino al massimo che puoi raggiungere. Se un topo sviluppa la capacità di correre la pista da qualsiasi possibile posizione di partenza, diventa in effetti immortale e acquisisce un enorme punteggio di fitness. Il suo genoma domina rapidamente, portando a una popolazione di circa 50.000 ratti e un punteggio di pochi milioni.

2

C ++, TripleScore, Punteggio: 100 ~ 400

Prima di tutto, il mio punteggio varia notevolmente su più corse (principalmente a causa del numero di 1).

Il nucleo calcola il punteggio di 5 direzioni: su, giù, avanti-avanti, avanti e avanti-giù. Innanzitutto vengono calcolati il ​​punteggio su e giù, quindi i risultati vengono confrontati con il valore di rimanere sul posto. Se rimanere sul posto è meglio che spostarsi verso l'alto o verso il basso, queste direzioni non saranno scelte (quindi deve andare avanti). Questo per evitare che rimbalzi (su, giù, su, giù, ...) tra 2 punti.

Ora vengono segnate le altre 3 direzioni: in avanti, in avanti e in basso. Da tutte le direzioni investigate vengono mantenute quelle con il punteggio più alto e 1 di queste viene scelta a caso.

Punteggio di una direzione: TripleScore calcola il punteggio di un movimento usando 3 punteggi secondari:

  • Il punteggio del colore della destinazione (dipende dal DNA, come in colorScorePlayer)
  • Il punteggio di andare avanti (dipende dal DNA)
  • Il punteggio massimo di fare una mossa in avanti dalla destinazione (moltiplicato per un fattore memorizzato nel dna)

Come per le altre risposte, il punteggio dipende in larga misura dal numero di 1 punteggi restituiti.

#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

Questo ratto altamente non deterministico calcola la probabilità di andare in uno spazio dal suo vicinato. I primi 16 slot nel genoma rappresentano i 16 colori. 1 in uno slot significa che il colore è buono da calpestare, 0 significa cattivo. I successivi 16 mantengono lo stesso per lo spazio davanti al bersaglio e così via.

Il vantaggio principale dell'approccio probabilistico è che è quasi impossibile rimanere a lungo dietro un muro. Lo svantaggio è che non otterrai quasi mai un topo quasi perfetto.


+1 per originalità. Che tipo di punteggio hai ottenuto?

In realtà non l'ho ancora provato ...
MegaTom

Hai dimenticato di dare cun valore iniziale? Non sembra essere definito quando lo si utilizza nel primo if.
Martin Ender,

@ MartinBüttner sì, l'ho dimenticato. Lo riparerò ora.
MegaTom,

Non conosco bene Ruby, ma il tuo codice non funziona sotto Ruby2.1.5. coordsnon è un elenco, si utilizza al &&posto di ande si è dimenticata la parentesi, e anche dopo aver corretto tutto ciò non si delimitano i valori RNG in modo da ottenere una direzione vuota. Questo pseudo-codice o qualcosa è pensato per essere eseguito con una sorta di dialetto Ruby?

2

Java, RunningStar, Score = 1817.050970291959 oltre 1000 giochi

Questo bot utilizza il codice colore Run-Bonus con la tecnica di StarPlayer .

Aggiornamento: risolto controller 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

Non particolarmente innovativo, ma è il mio unico tentativo che ha funzionato bene.

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)

Fondamentalmente, codifica quattro colori (ciascuno 4 bit) per evitare, nel genoma. Quindi passa a un colore non presente in tale elenco. Se tutti i colori sono cattivi, salta comunque in avanti verso l'ignoto.


Probabilmente avrebbe dovuto chiamarlo "RedQueen" :)
plannapus,

1

Java - IAmARobotPlayer - Punteggio 3.7

Ho appena creato questo topo robotico per il confronto con un altro programma (non molto interessante finora) che ho realizzato. Non segna bene complessivamente ma se segna da qualche parte, otterrà molti ratti. L'idea è che guarderà solo le tre celle di fronte, ogni cella è buona o cattiva. Questo dà un numero binario. Quindi cercherà questo numero nel suo genoma, prenderà i tre bit consecutivi, li convertirà anche in un numero e intraprenderà l'azione memorizzata con questo numero. Quindi agisce sempre allo stesso modo quando incontra la stessa situazione.

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];

    }
}

Risultato:

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

Esemplari prudenti - C ++ - segna circa 2030 oltre 200 corse

Questo utilizza la parte colorata (16x4 bit) del DNA codificante da Blind Faith ma lascia il resto (36 bit) del DNA completamente inutilizzato.

La codifica per un colore è:

  • 10XX - per quadrati sicuri;
  • 11XX - per quadrati letali; e
  • Da 0000 a 0111 - per gli 8 tipi di quadrati dei trap.

Dove X indica bit non utilizzati. Dato che solo i colori 2 su 16 sono trappole che useranno tutti e 4 i loro bit (e solo se la trap è sfalsata, che sarà il caso 8 volte su 9), in genere ci saranno 64 bit non utilizzati - la teoria è che le mutazioni che influenzano uno qualsiasi di questi bit inutilizzati non rovineranno il genoma e la stabilità è migliore di qualsiasi altra fantasia che possa usare quei bit rimanenti.

Gli esemplari quindi lo usano per pianificare un percorso sicuro all'interno di una griglia 7x7 centrata su se stessi (il 5x5 la loro visione consente più 1 quadrato su ciascun lato per consentire trappole offset) dando priorità allo spostamento della massima distanza in avanti dopo 3 mosse.

Inizialmente ho iniziato a costruire alcuni controlli per assicurarmi che il fatto che il colore su cui si trova il campione non sia letale corrisponda al genoma e che contrassegni eventuali colori errati come quadrati di sicurezza UNSURE (e i loro quadrati adiacenti) - tuttavia ha aggiunto un valore significativo complicazione per un guadagno minimo o nullo rispetto alla marcatura di quei quadrati come SICURI e all'uccisione di alcuni esemplari aggiuntivi. Tornerò su questo se ho 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};
}

Punteggi campione:

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

Punteggio massimo durante il test: 8.150.817 campioni salvati.


Ora l'hai fatto ... Volevo salvare il pathing per dopo, ma non potevo lasciare incontrastati i tuoi cauti roditori :) A quanto pare, il pathing funziona ancora meglio con una codifica più efficiente. Anche la tua idea di estendere l'area del percorso a 7x7 sembra promettente. Vedrò se posso usarlo.

Attualmente sto facendo 2000 corse per questo ... dopo i primi 900 la media sembra stabilizzarsi intorno al 600, che è abbastanza lontano dal 2000. Ti dispiacerebbe rileggerlo anche da parte tua, per vedere se il 2000 era solo un colpo di fortuna?
Martin Ender,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.