Lab Rat Race: un exercice sur les algorithmes génétiques

113

C'est le défi bimensuel n ° 3. Thème: Algorithmes Génétiques

Ce défi est un peu une expérience. Nous voulions voir ce que nous pouvions faire, par défi, avec des algorithmes génétiques. Tout n’est peut-être pas optimal, mais nous avons fait de notre mieux pour le rendre accessible. Si cela fonctionne, qui sait ce que nous pourrions voir à l'avenir. Peut-être un roi génétique de la colline?

La spec est assez longue! Nous avons essayé de séparer la spécification dans The Basics - le strict minimum que vous devez savoir pour commencer à jouer avec le framework et soumettre une réponse - et The Gory Details - la spécification complète, avec tous les détails concernant le contrôleur, sur lesquels pourrait écrire le vôtre.
Si vous avez des questions, n'hésitez pas à nous rejoindre dans le chat!

Vous êtes un chercheur en psychologie comportementale. Nous sommes vendredi soir et vos collègues et vous décidez de vous amuser et d'utiliser vos rats de laboratoire pour une petite course de rats. En fait, avant de nous attacher trop émotionnellement à eux, appelons-les des spécimens .

Vous avez mis en place une petite piste de course pour les spécimens et, pour la rendre plus intéressante, vous avez placé des murs, des pièges et des téléporteurs sur la piste. Maintenant, vos spécimens sont toujours des rats… ils n'ont aucune idée de ce qu'est un piège ou un téléporteur. Tout ce qu'ils voient, ce sont des choses de couleurs différentes. De plus, ils n'ont aucune mémoire. Tout ce qu'ils peuvent faire, c'est prendre des décisions en fonction de leur environnement actuel. Je suppose que la sélection naturelle choisira les spécimens qui savent éviter un piège parmi ceux qui ne le font pas (cette course va prendre un certain temps ...). Que les jeux commencent!

Exemple d'image du conseil utilisé

† 84 465 spécimens ont été blessés dans la réalisation de ce défi.

Les bases

Il s’agit d’un jeu à joueur unique (vos collègues et vous ne vouliez pas mélanger les populations afin que chacun ait construit son propre circuit). La piste de course est une grille rectangulaire, haute de 15 cellules et large de 50 cellules. Vous commencez avec 15 échantillons sur des cellules aléatoires (pas nécessairement distinctes) sur le bord gauche (où x = 0 ). Vos spécimens doivent essayer d’atteindre l’objectif correspondant à n’importe quelle cellule à x ≥ 49 et 0 ≤ y ≤ 14 (les spécimens peuvent dépasser de la piste à droite). Chaque fois que cela se produit, vous obtenez un point. Vous commencez également le jeu avec 1 point. Vous devriez essayer de maximiser vos points après 10 000 tours.

Plusieurs spécimens peuvent occuper la même cellule et ne vont pas interagir.

À chaque tour, chaque spécimen voit une grille 5x5 de leur environnement (avec lui-même au centre). Chaque cellule de cette grille contiendra une couleur -1à 15. -1représente les cellules qui sont hors limites. Votre spécimen meurt s'il sort des limites. Quant aux autres couleurs, elles représentent des cellules vides, des pièges, des murs et des téléporteurs. Mais votre spécimen ne sait pas quelle couleur représente quoi et vous non plus. Il y a quelques contraintes cependant:

  • 8 couleurs représenteront des cellules vides.
  • 4 couleurs représenteront un téléporteur. Un téléporteur enverra le spécimen à une certaine cellule dans son voisinage 9x9. Ce décalage sera le même pour tous les téléporteurs de la même couleur.
  • 2 couleurs représenteront les murs. S'installer dans un mur revient à rester immobile.
  • 2 couleurs représenteront un piège. Un piège indique que l' une des 9 cellules dans son voisinage immédiat est mortelle (pas nécessairement la cellule de piège elle-même). Ce décalage sera le même pour tous les pièges de la même couleur.

Maintenant, à propos de cette sélection naturelle ... chaque spécimen a un génome, qui est un nombre de 100 bits. De nouveaux spécimens seront créés en croisant deux spécimens existants, puis en effectuant une légère mutation du génome. Plus un spécimen est réussi, plus ses chances de reproduction sont grandes.

Voici donc votre tâche: vous écrirez une seule fonction, qui reçoit en entrée la grille de couleurs 5x5 qu'un spécimen voit, ainsi que son génome. Votre fonction retournera un mouvement (Δx, Δy) pour l'échantillon, où Δx et Δy seront chacun l'un de ceux-ci {-1, 0, 1}. Vous ne devez pas conserver de données entre les appels de fonction. Cela inclut l’utilisation de vos propres générateurs de nombres aléatoires. Votre fonction recevra un GNA que vous êtes libre d'utiliser à votre guise.

Le score de votre soumission sera la moyenne géométrique du nombre de points sur 50 pistes aléatoires. Nous avons constaté que ce score est sujet à une assez grande variance. Par conséquent, ces scores seront préliminaires . Une fois ce défi terminé, une date limite sera annoncée. À la fin de la date limite, 100 conseils seront choisis au hasard et toutes les soumissions seront rediffusées sur ces 100 conseils. Sentez-vous libre de mettre une note estimée dans votre réponse, mais nous noterons chaque soumission nous-mêmes pour éviter que quelqu'un triche.

Nous avons fourni des programmes de contrôleur dans une poignée de langues. Actuellement, vous pouvez rédiger votre soumission en Python (2 ou 3), Ruby , C ++ , C # ou Java . Le contrôleur génère les tableaux, lance le jeu et fournit un cadre pour l'algorithme génétique. Tout ce que vous avez à faire est de fournir la fonction de déplacement.

Attendez, alors qu'est-ce que je fais exactement avec le génome?

Le défi consiste à comprendre cela!

Comme les spécimens n'ont pas de mémoire, tout ce que vous avez à un tour donné est une grille de couleurs 5x5 qui ne vous dit rien. Vous devrez donc utiliser le génome pour atteindre l'objectif. L'idée générale est que vous utilisiez des parties du génome pour stocker des informations sur les couleurs ou la disposition de la grille, et que votre bot base ses décisions sur les informations supplémentaires stockées dans le génome.

Bien sûr, vous ne pouvez réellement rien y stocker manuellement. Ainsi, les informations réelles stockées ici seront initialement complètement aléatoires. Mais l'algorithme génétique sélectionnera bientôt les spécimens dont le génome contient la bonne information tout en éliminant ceux qui ont la mauvaise information. Votre objectif est de trouver un mappage à partir des bits du génome et de votre champ de vision en un mouvement, ce qui vous permet de trouver rapidement le chemin qui mène à l'objectif et qui évolue constamment vers une stratégie gagnante.

Cela devrait être suffisant pour vous aider à démarrer. Si vous le souhaitez, vous pouvez ignorer la section suivante et sélectionner votre contrôleur de votre choix dans la liste des contrôleurs en bas (qui contient également des informations sur l'utilisation de ce contrôleur particulier).

Continuez à lire si vous voulez tout ...

The Gory Détails

Cette spécification est complète. Tous les contrôleurs doivent implémenter ces règles.

Tous les caractères aléatoires utilisent une distribution uniforme, sauf indication contraire.

Génération de piste:

  • La piste est une grille rectangulaire, X = 53 cellules de large et Y = 15 cellules de hauteur. Les cellules avec x ≥ 49 sont des cellules cibles (où x est basé sur zéro).
  • Chaque cellule a une couleur unique et peut être ou ne pas être mortelle - les cellules ne sont pas mortelles sauf spécification contraire d'un des types de cellules ci-dessous.
  • Il existe 16 couleurs de cellules différentes, étiquetées de 0à 15, dont la signification changera de jeu en match. En outre, -1représente les cellules qui sont hors limites - celles-ci sont mortelles .
  • Choisissez 8 couleurs aléatoires . Ce seront des cellules vides (sans effet).
  • Choisissez 4 couleurs plus aléatoires . Ce sont des téléporteurs. Pour deux de ces couleurs, choisissez un décalage autre que zéro dans le voisinage 9x9 (de (-4, -4) à (4,4) sauf (0,0)). Pour les deux autres couleurs, inversez ces décalages. Si un spécimen monte sur un téléporteur, il est immédiatement déplacé de ce décalage.
  • Choisissez 2 couleurs plus aléatoires . Ce sont des pièges. Pour chacune de ces couleurs, choisissez un décalage dans le voisinage 3x3 (de (-1, -1) à (1,1)). Un piège indique que la cellule à cet offset est mortelle . Remarque: la cellule de piégeage en elle-même n'est pas nécessairement mortelle.
  • Les 2 couleurs restantes sont des murs qui empêchent le mouvement. Si vous essayez de vous déplacer sur une cellule murale, le mouvement restera immobile. Les cellules murales elles-mêmes sont mortelles .
  • Pour chaque cellule de la grille sans objectif, choisissez une couleur aléatoire. Pour chaque cellule cible, choisissez une couleur vide aléatoire .
  • Pour chaque cellule au bord gauche de la piste, déterminez si l'objectif peut être atteint dans un délai de 100 tours (selon les règles d' ordre des tours ci-dessous). Si tel est le cas, cette cellule est une cellule de départ admissible . S'il y a moins de 10 cellules de départ, ignorez la piste et générez-en une nouvelle.
  • Créez 15 spécimens, chacun avec un génome aléatoire et un âge 0 . Placez chaque échantillon sur une cellule de départ aléatoire.

Ordre de rotation:

  1. Les étapes suivantes seront effectuées, dans l’ordre, pour chaque échantillon. Les spécimens n'interagissent pas ou ne se voient pas et peuvent occuper la même cellule.
    1. Si le spécimen a 100 ans , il meurt. Sinon, incrémentez son âge de 1.
    2. Le champ de vision du spécimen est donné - une grille de couleurs 5x5, centrée sur le spécimen - et renvoie un mouvement dans son voisinage 3x3. Les déplacements en dehors de cette plage entraîneront l'arrêt du contrôleur.
    3. Si la cellule cible est un mur, le déplacement est modifié en (0,0).
    4. Si la cellule cible est un téléporteur, l'échantillon est déplacé par le décalage du téléporteur. Remarque: cette étape est effectuée une fois , pas de manière itérative.
    5. Si la cellule actuellement occupée par l'échantillon (potentiellement après avoir utilisé un téléporteur) est mortelle, l'échantillon meurt. C'est la seule fois où les spécimens meurent (à l'exception de l'étape 1.1. Ci-dessus). En particulier, un nouveau spécimen apparaissant dans une cellule mortelle ne mourra pas immédiatement, mais aura une chance de quitter la cellule dangereuse en premier.
    6. Si le spécimen occupe une cellule de but, marquez un point, déplacez le spécimen dans une cellule de départ aléatoire et réinitialisez son âge à 0.
  2. S'il reste moins de deux spécimens sur le plateau, la partie se termine.
  3. Créer 10 nouveaux spécimens à l’âge 0 . Chaque génome est déterminé (individuellement) par les règles de reproduction ci-dessous. Placez chaque échantillon sur une cellule de départ aléatoire.

Reproduction:

  • Lorsqu'un nouveau spécimen est créé, choisissez deux parents distincts au hasard, en privilégiant les spécimens qui ont progressé plus à droite. La probabilité qu'un échantillon soit choisi est proportionnelle à son score de condition physique actuel . Le score de forme d'un spécimen est

    1 + x + 50 * nombre de fois qu'il a atteint l'objectif

    x est l'index horizontal basé sur 0. Les spécimens créés au même tour ne peuvent pas être choisis comme parents.

  • Parmi les deux parents, choisissez-en un au hasard pour prendre le premier fragment du génome.

  • Maintenant que vous parcourez le génome, changez de parent avec une probabilité de 0,05 et continuez à prendre des bits du parent résultant.
  • Mutatez le génome entièrement assemblé: pour chaque bit, retournez-le avec une probabilité de 0,01 .

Notation:

  • Une partie dure 10 000 tours.
  • Les joueurs commencent le jeu avec 1 point (pour permettre l'utilisation de la moyenne géométrique).
  • Chaque fois qu'un spécimen atteint le but, le joueur marque un point.
  • Pour l'instant, la soumission de chaque joueur se déroulera sur 50 parties, chacune avec une piste aléatoire différente.
  • L'approche ci-dessus résulte en plus de variance qu'il n'est souhaitable. Une fois ce défi terminé, une date limite sera annoncée. À la fin de la date limite, 100 conseils seront choisis au hasard et toutes les soumissions seront rediffusées sur ces 100 conseils.
  • Le score global d'un joueur est la moyenne géométrique des scores de ces jeux individuels.

Les contrôleurs

Vous pouvez choisir l’un des contrôleurs suivants (car ils sont fonctionnellement équivalents). Nous les avons tous testés, mais si vous trouvez un bogue, souhaitez améliorer le code ou les performances, ou ajoutez une fonctionnalité telle que la sortie graphique, envoyez un problème ou envoyez une demande d'extraction sur GitHub! Vous pouvez également ajouter un nouveau contrôleur dans une autre langue!

Cliquez sur le nom de la langue de chaque contrôleur pour accéder au bon répertoire sur GitHub, qui contient README.mdles instructions d'utilisation exactes.

Si vous n'êtes pas familier avec git et / ou GitHub, vous pouvez télécharger l'intégralité du référentiel au format ZIP à partir de la page d'accueil (voir le bouton dans la barre latérale).

Python

  • Le plus minutieusement testé. Ceci est notre implémentation de référence.
  • Fonctionne avec Python 2.6+ et Python 3.2+!
  • C'est très lent. Nous recommandons de l’utiliser avec PyPy pour une accélération substantielle.
  • Prend en charge la sortie graphique à l'aide de pygameou tkinter.

Rubis

  • Testé avec Ruby 2.0.0. Devrait fonctionner avec les nouvelles versions.
  • C'est aussi assez lent, mais Ruby peut être pratique pour prototyper une idée de soumission.

C ++

  • Nécessite C ++ 11.
  • Prend éventuellement en charge le multithreading.
  • De loin le contrôleur le plus rapide du peloton.

C #

  • Utilise LINQ, il nécessite donc .NET 3.5.
  • Plutôt lent.

Java

  • Pas particulièrement lent. Pas particulièrement rapide.

Classement préliminaire

Tous les scores sont préliminaires. Néanmoins, si quelque chose ne va pas ou est obsolète, faites-le moi savoir. Notre exemple de soumission est répertorié à des fins de comparaison, mais pas en conflit.

  Score   | # Games | User               | Language   | Bot           
===================================================================================
2914.13   |   2000  | kuroi neko         | C++        | Hard Believers
1817.05097|   1000  | TheBestOne         | Java       | Running Star
1009.72   |   2000  | kuroi neko         | C++        | Blind faith
 782.18   |   2000  | MT0                | C++        | Cautious Specimens
 428.38   |         | user2487951        | Python     | NeighborsOfNeighbors
 145.35   |   2000  | Wouter ibens       | C++        | Triple Score
 133.2    |         | Anton              | C++        | StarPlayer
 122.92   |         | Dominik Müller     | Python     | SkyWalker
  89.90   |         | aschmack           | C++        | LookAheadPlayer
  74.7    |         | bitpwner           | C++        | ColorFarSeeker
  70.98   |   2000  | Ceribia            | C++        | WallGuesser
  50.35   |         | feersum            | C++        | Run-Bonus Player
  35.85   |         | Zgarb              | C++        | Pathfinder
 (34.45)  |   5000  | Martin Büttner     | <all>      | ColorScorePlayer
   9.77   |         | DenDenDo           | C++        | SlowAndSteady
   3.7    |         | flawr              | Java       | IAmARobotPlayer
   1.9    |         | trichoplax         | Python     | Bishop
   1.04   |   2000  | fluffy             | C++        | Gray-Color Lookahead

Crédits

Ce défi était un énorme effort de collaboration:

  • Nathan Merril: A écrit les contrôleurs Python et Java. Transforme le concept de défi du roi de la colline en une course de rats.
  • trichoplax: test de jeu. Travaillé sur le contrôleur Python.
  • feersum: écrit le contrôleur C ++.
  • VisualMelon: écrit le contrôleur C #.
  • Martin Büttner: Concept. A écrit le contrôleur Ruby. Playtesting. Travaillé sur le contrôleur Python.
  • T Abraham: Playtesting. Testé Python et passé en revue les contrôleurs C # et C ++.

Tous les utilisateurs ci-dessus (et probablement quelques autres que j'ai oubliés) ont contribué à la conception générale du défi.

Mise à jour du contrôleur C ++

Si vous utilisez le C ++ avec Visual Studio et le multithreading, vous devriez obtenir la dernière mise à jour à cause d'un bogue lié à l'ensemencement de leur générateur de nombres aléatoires, qui permet de générer des cartes dupliquées.

Martin Ender
la source
3
Quelqu'un ne pourrait-il pas créer un algorithme génétique pour trouver l'algorithme génétique optimal pour ce problème?
mbomb007
1
@ anon3202 Eh bien, cela vous donnerait bien sûr plus d’informations sur la configuration de la piste puisque vous pourriez savoir où vous vous trouvez. Pour l’essentiel, nous voulions garder l’interface simple pour les robots, et en faire un problème purement local, où vous aurez besoin du génome pour déterminer quelle solution locale est la plus bénéfique pour votre progrès mondial.
Martin Ender
1
@matovitch Voir la section 5 de la section Ordre du tour du Gory Détails (spécification complète):'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 le
1
J'ai peaufiné le code C ++ pour afficher l'exemple de moyenne, stddev, stderr et l'intervalle de configuration à 99% (avant votre log / exp "géométrique") et j'ai fait une découverte surprenante. La réponse "Blind Faith" avait "Moyenne d'échantillonnage de 116529 + - 2,78337e + 010 (99%) stddev = 7,77951e + 010" après 50 points. "Réduire l'intervalle de confiance à 50% n'améliore en rien la situation. La moyenne géométrique était plus stable cependant: "Moyenne de 159.458 + - 117262 (99%) stddev = 32.6237" (Avant sa mise à jour de 800 points)
Mooing Duck le
1
J'ai expérimenté le taux de mutation et je pense que le défi serait plus intéressant (et que les contrôleurs fonctionneraient beaucoup plus vite) si la probabilité était augmentée de 0,01 à 0,0227, ce qui donne à un ADN seulement 10% de chances de passer. mutation inchangée au lieu de 37% avec la valeur actuelle. Cela évite des explosions de population ridicules (ce qui permet de gagner beaucoup de temps de calcul) et évite beaucoup d'échecs dus à une diversité insuffisante. Les scores individuels sont plus bas, mais comme plus de courses produisent des gagnants, la moyenne mondiale tend à augmenter.

Réponses:

37

La foi aveugle - C ++ - semble dépasser 800 (!) Sur 2000 courses

Génome de codage couleur avec un retour de piste mystérieux et un moyen de dissuasion efficace

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

Exemple de résultats:

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

Basé sur le test involontairement long de feersum, je pense que 2000 exécutions sont suffisantes pour produire un résultat suffisamment stable.
Comme mon contrôleur modifié affiche la moyenne géométrique actuelle après chaque analyse, j'ai confirmé visuellement que la variation entre les 50 dernières exécutions était relativement faible (+ - 10 points).

Qu'est-ce qui motive ces créatures?

Au lieu de donner des priorités égales à chaque couleur, je considère ces valeurs possibles:

  1. bien -> le rat pense pouvoir y aller en toute sécurité
  2. mauvais -> le rat n'ira pas là
  3. piège -> le rat considérera que la position du piège est mauvaise et que la cellule indiquant le piège est bonne .
    Bien que je sois trop paresseux pour le renommer, il s'agit plutôt d'un "détecteur de danger" indiquant l'emplacement (supposé) d'un véritable piège, d'un mur, d'un téléporteur attendant d'envoyer le vagabond sans méfiance dans un lieu déplaisant ou même à l'entrée d'un mort. -fin. En bref, un endroit où un rat avisé préfère ne pas aller.

les bons ou les mauvais gènes ne prennent que 2 bits à stocker (par exemple 11et 10), mais les pièges nécessitent 4 bits ( 0ttttttreprésente l'un des 8 emplacements "dangereux" possibles).

Pour garder chaque gène cohérent (c. -à-en conservant son sens après été mélangé dans un génome complètement différent, qui exige que chaque gène codant pour la couleur d'être à un endroit fixe), toutes les valeurs sont codées sur 4 bits (si bien est attribué un code 11xxet mauvais que 10xx), pour un total de 16 * 4 = 64 bits.

Les 36 bits restants sont utilisés comme "anti-wall-bangers" (plus sur cela plus tard). Les 25 couleurs environnantes sont hachées dans un index de ces 36 bits. Chaque bit indique une direction verticale préférée (vers le haut ou vers le bas), utilisée lorsqu'il existe un choix possible entre deux cellules.

La stratégie est la suivante:

  • décoder chaque couleur en fonction du génome (ou rapport de contrôleur direct pour les cellules "mauvaises" hors piste)
  • construire une carte des environs immédiats (3x3 cellules, 8 voisins possibles)
  • calculer une signature des environs (un hachage des 25 couleurs sauf les cellules hors piste)
  • choisissez une direction verticale préférée dans la signature (parmi 36 seaux de hachage)
  • essayez de vous déplacer vers un voisin désigné comme "bon", en commençant par les plus proches du but et en commençant par la direction verticale préférée
  • si aucun «bon» voisin ne peut être trouvé, essayez de reculer d'une cellule (risquant ainsi d'être victime d'un malheureux accident et en évitant de toute façon d'améliorer votre condition physique)

Ye rongeurs, voici les ennemis de votre espèce

le mur redouté boucle de téléportation

La pire chose qui puisse arriver à une population est de ne pas avoir encore gagné, mais beaucoup de rats collés soit contre un mur, soit dans une boucle de téléportation infinie suffisamment proche du but pour avoir une chance dominante d'être sélectionnés pour la reproduction .
Contrairement aux rats écrasés dans un piège ou téléportés dans les murs, ces rongeurs ne seront tués que par la vieillesse.
Ils n'ont aucun avantage concurrentiel sur leurs cousins ​​bloqués 3 cellules dès le début, mais ils auront tout le temps nécessaire pour se reproduire génération après génération jusqu'à ce que leur génome devienne dominant, nuisant ainsi à la diversité génétique sans raison valable.

Pour atténuer ce phénomène, l’idée est de faire en sorte que les enfants de ces mauvais et mauvais rats aient plus de chances d’éviter de suivre les traces de leur ascendance.
L’indication de la direction verticale n’est longue que de 1 bit (en gros, il faut dire "essayez de monter ou de descendre en premier dans ces environnements"), et quelques bits sont susceptibles d’avoir un effet sur le trajet suivi. Les mutations et / ou les croisements doivent donc avoir une impact significatif.
De nombreux enfants auront un comportement différent et ne finiront pas par se cogner la tête contre le même mur (parmi les cadavres de leurs ancêtres affamés).
La subtilité ici est que cette indication n'est pas le facteur dominant dans le comportement du rat. L’interprétation des couleurs prévaudra toujours dans la plupart des cas (le choix haut / bas n’aura d’importance que s’il existe effectivement deuxet ce que le rat considère comme une couleur inoffensive n’est pas un téléporteur qui attend de le jeter dans un mur).

Pourquoi cela semble-t-il fonctionner?

Je ne sais toujours pas précisément pourquoi.

La chance absolue qui reste un mystère non résolu est la logique de cartographie des pièges. C’est sans aucun doute la pierre angulaire du succès, mais il fonctionne de manière mystérieuse.

Avec le codage utilisé, un génome aléatoire produira 25% d'identificateurs de couleur «bons», 25% «mauvais» et 50% «pièges».
les identifiants "trap" produiront à leur tour des estimations "bonnes" et "mauvaises" en corrélation avec l'environnement 5x5.
En conséquence, un rat à un endroit donné "verra" le monde comme un mélange de couleurs stables et contextuelles "aller / non-aller".

Comme le mécanisme anti-cliquetis assez efficace semble l'indiquer, le pire type d'élément sur la piste est le mur redouté (et son cousin la boucle de téléportation, mais je suppose que ceux-ci sont beaucoup moins courants).

En conclusion, un programme réussi doit avant tout permettre de développer des rats capables de détecter des positions qui conduiront à une lente famine sans atteindre l'objectif.

Même sans "deviner" les deux couleurs représentant les murs, les couleurs du "piège" semblent contribuer à leur évitement en permettant à un rat de contourner quelques obstacles non pas parce qu'il a "vu" les murs, mais parce que l'estimation du "piège" a exclu ces derniers. des cellules murales particulières dans ces environnements particuliers.

Même si le rat tente de se rapprocher de l'objectif (ce qui pourrait laisser penser que les indicateurs de piège les plus "utiles" sont ceux indiquant un danger devant), je pense que toutes les directions de piège ont à peu près la même influence: un piège indiquant "le danger derrière "situé à 2 cellules devant un rat a la même influence que celle qui indique" danger à venir "lorsque le rat se tient juste au-dessus de celui-ci.

Pourquoi ce mélange a-t-il la propriété de faire que le génome converge avec autant de succès est bien au-delà de mes calculs, malheureusement.

Je me sens plus à l'aise avec la force de dissuasion murmurante. Cela a fonctionné comme prévu, mais bien au-dessus de mes attentes (le score a été multiplié par quatre).

J'ai fortement piraté le contrôleur pour afficher des données. Voici quelques pistes:

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

Ici, une race de super-rat est apparue tôt (la piste permettait probablement de courir en ligne droite et certains rats chanceux des toutes premières générations avaient le bon ADN pour en tirer avantage). Le nombre de spécimens à la fin correspond à peu près à la moitié du maximum théorique d’environ 100 000 rats, ce qui signifie que près de la moitié des créatures ont acquis la capacité de survivre indéfiniment sur cette piste (!).
Bien entendu, le résultat obtenu est simplement obscène - tout comme le temps de calcul.

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

Ici, nous pouvons voir le raffinement du génome à l'œuvre. La lignée entre les deux derniers génomes apparaît clairement. Les bonnes et les mauvaises évaluations sont les plus significatives. Les indications de piège semblent osciller jusqu'à ce qu'elles se stabilisent en un piège "utile" ou se transforment en bien ou en mal .

Il semble que les gènes de couleur possèdent quelques caractéristiques utiles:

  • ils ont une signification autonome
    (une couleur spécifique doit être manipulée de manière spécifique).
    Chaque code de couleur peut être jeté dans un génome complètement différent sans modifier radicalement le comportement - à moins que la couleur ne soit réellement déterminante (typiquement un mur ou un téléporteur menant à une boucle infinie).
    C’est moins le cas avec un codage prioritaire de base, car la couleur la plus prioritaire est la seule information utilisée pour décider de la destination. Ici, toutes les "bonnes" couleurs sont égales, ainsi une couleur donnée ajoutée à la liste des "bonnes" aura moins d'impact.
  • ils sont relativement résistants aux mutations;
    le codage bon / mauvais ne comporte que 2 bits significatifs sur 4, et l'emplacement de la trappe peut être modifié la plupart du temps sans modifier de manière significative le comportement du rat.
  • ils sont petits (4 bits), de sorte que la probabilité d'être détruit par un croisement est très faible.
  • les mutations produisent soit des modifications sans danger des changements significatifs
    Un gène qui se mute en "bon" aura soit peu d'effet (s'il correspond par exemple à une cellule vide, il pourrait permettre de trouver un nouveau chemin plus court, mais cela pourrait aussi conduire le rat directement dans un piège) ou dramatique (si la couleur représente un mur, le nouveau rat risque fort de rester coincé quelque part).
    Un gène basculant pour "piéger" priverait le rat d'une couleur essentielle ou n'aura aucun effet notable.
    Une mutation d'un emplacement de piège importera seulement s'il y a effectivement un piège (ou quelque chose de nocif) devant vous, qui a une probabilité relativement petite (je dirais quelque chose comme 1/3).

Enfin, je suppose que les 36 derniers bits contribuent non seulement à éviter le blocage des rats, mais également à les répartir plus uniformément sur la piste, préservant ainsi la diversité génétique jusqu’à ce qu’un génome gagnant émerge et devienne dominant à travers la partie codant la couleur.

La poursuite des travaux

Je dois dire que je trouve ces petites bestioles fascinantes.
Merci encore à tous les contributeurs à cet excellent défi.

Je songe à abattre davantage le contrôleur pour afficher des données plus importantes, comme l’ascendance d’un rat ayant réussi.

J'aimerais aussi beaucoup voir ces rats en action, mais ce langage C ++ b ** ch rend la création - encore moins l'animation - d'images (parmi beaucoup d'autres choses) une corvée désordonnée.

En fin de compte, je voudrais produire au moins une explication du système de piège et éventuellement l’améliorer.

Contrôleur de piratage

Si quelqu'un est intéressé, je peux publier les modifications que j'ai apportées au contrôleur.
Ils sont sales et bon marché, mais ils font le travail.

Je ne suis pas averti de GitHub, donc cela devrait passer par un simple post.

Communauté
la source
16
A obtenu un score de 208,14 avec 10 000 jeux. J'essayais de le tester pour 1000, mais je ne me suis jamais rendu compte que j'avais tapé un 0 supplémentaire, cela a donc pris plus de 7 heures.
Feersum
LOL merci tout de même. En comparant avec mes deux 1000 courses, il semble qu’environ 2000 courses pourraient alors produire un résultat stable.
Que ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^signifie ton Je peux deviner le reste, mais ça me pose problème?
Mooing Duck
J'envisageais de créer un contrôleur "de débogage" séparé, qui s'exécute un rat à la fois, et chaque fois qu'un nouveau rat est généré, il montre l'ADN des deux parents et de l'enfant (via une fonction personnalisable). Cela faciliterait beaucoup l'examen du fonctionnement du rat.
Mooing Duck
2
cela représente les 36 bits indicateurs "haut / bas", mais dans ces exemples, l'ADN gagnant est déjà devenu dominant et ne varie donc pas beaucoup.
18

Dur croyants - C ++ - (téléporteurs améliorés): plus de 10.000 pour 2000 courses

(Ceci est une évolution de la foi aveugle , vous voudrez peut-être escalader un autre mur de texte avant celui-ci)

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

Episode IV: se repérer sur la grille

Résultats

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

Je suis passé à g ++ / MinGW et à 3 threads.
Le code généré par GNU est plus de deux fois plus rapide que celui de Microsoft.
Pas étonnant, avec leur implémentation STL épouvantable.

Téléporteurs

L'effet du téléporteur dépend fortement de la position. Jusqu'à présent, j'étais heureux de considérer un téléporteur comme étant toujours bon (considéré comme un espace vide) ou toujours mauvais (considéré comme un mur afin qu'aucun rongeur ne le prenne jamais).

C'est un modèle trop grossier.
Un téléporteur donné peut propulser un rat en avant jusqu'à quelques cellules du but, mais une fois sur place, le même téléporteur peut rejeter le rat du tableau.
Un tel téléporteur sera très probablement reconnu comme passable (car il augmente la condition physique plus rapidement que lorsqu'il "marche" vers le même emplacement x), devient partie intégrante du génome dominant et tue presque tous les rats qui lui font confiance comme étant "toujours en sécurité".
Les rats n'ayant aucun moyen de connaître leur position X, la seule solution pour détecter ces téléporteurs perfides consiste à décider de les intercepter en se basant sur les seules données contextuelles disponibles, à savoir la grille de couleurs 5x5.

Pour ce faire, j'ai défini 4 types de gènes de couleur:

  • détecteur de piège de danger
  • vide passable n'importe où sur la piste
  • bloc interdit partout sur la piste
  • faisceau vu comme vide ou bloc en fonction de l'environnement

L'idée est d'essayer de distinguer un téléporteur en regardant ses 8 voisins immédiats. Comme la probabilité d'avoir 8 voisins identiques à un endroit donné est très faible, cela devrait permettre d'identifier une instance unique de chaque téléporteur.

Les 8 couleurs voisines peuvent être combinées pour former une signature locale, qui est invariante par rapport à la position dans le labyrinthe. Malheureusement, les 8 voisins ne sont visibles que pour les cellules situées dans le carré intérieur du champ de vision 3x3. Par conséquent, les signatures seront inexactes sur le bord du champ de vision.
Néanmoins, cela nous donnera une information contextuelle constante dans le voisinage immédiat, ce qui est suffisant pour augmenter la probabilité de naviguer avec succès dans les téléporteurs.

les gènes de faisceau ont un champ variable de 2 bits.
Pour une signature locale de téléporteur donnée, il y a une chance sur quatre que la cellule de faisceau soit considérée comme infranchissable. Chaque valeur du champ sélectionne l'une de ces quatre possibilités.
En conséquence, une mutation du gène de faisceau sur ces 2 bits passera par 4 significations contextuelles possibles de la couleur.

En outre, les couleurs les plus importantes à deviner sont toujours les murs et les pièges. Cela signifie que nous devrions permettre la détection des téléporteurs uniquement après que les rats ont appris où se trouvent les parois et les pièges.

Ceci est fait en mettant à jour les signatures locales seulement sparringly. Le critère actuel pour la mise à jour d'une signature locale doit correspondre à la couleur d'une couleur identifiée comme téléporteur potentiel.

Le codage utilise 5 bits par gène de couleur et regroupe les types pour libérer les 3 bits les moins significatifs afin de coder une valeur 0..7:

  • 4 danger
  • 4 vides
  • 4 bloc
  • 4 faisceaux

Chaque gène de faisceau a 1/4 chance d'être considéré comme un bloc et 3/4 chance d'être considéré comme vide, donc 4 faisceaux représentent en moyenne 1 bloc et 3 vides.

La proportion moyenne représentée par un écart aléatoire de 16 couleurs est donc:

  • 4 danger
  • 7 vides
  • 5 bloc

Ce mélange semble donner les meilleurs résultats jusqu’à présent, mais je n’ai pas fini de le peaufiner.

Mutabilité génétique

Une chose est sûre: les valeurs de code choisies pour représenter les types de gènes sont essentielles. Inverser deux valeurs peut coûter 2000 points ou plus.

Ici encore, la raison pour laquelle cela dépasse mes calculs.

Mon hypothèse est que les probabilités de mutation d'un type à un autre doivent être équilibrées, sinon, comme dans une matrice de Markow, les probabilités cumulatives tendent à restreindre les valeurs au sous-ensemble ayant les probabilités de transition entrantes les plus élevées.

Chemin à la rescousse

Pathing réduira considérablement le nombre de cellules visitées, ne permettant de tester que les plus susceptibles de mener à l'objectif. Ainsi, non seulement certaines impasses fréquentes sont évitées, mais les codes de couleur erronés sont également beaucoup plus susceptibles d'être découverts plus tôt.
En conséquence, le temps de convergence est fortement diminué.

Cependant, cela n’aide pas à résoudre les cartes où le génome est incapable de produire une représentation correcte de la piste.

Que faire avec des abrutis?

Après avoir visuellement regardé la piste, j'ai compris pourquoi une stratégie par défaut qui tente d'aller de l'avant même lorsqu'il semble n'y avoir rien d'autre que des murs en façade est en effet préférable à la retenue.
"murs" peuvent être en réalité des téléporteurs qui produisent tellement de résultats malheureux que le génome les cartographie comme des obstacles à ne jamais franchir, mais dans de rares cas, un cas particulier de ce téléporteur coquin peut avoir un effet positif (ou du moins non létal) Par conséquent, le prendre au lieu de revenir en arrière augmente les chances de trouver le chemin de la victoire.

Convergence précoce

Il me semble que le taux de mutation est un peu trop faible (du moins pour mes rongeurs).

Le réglage actuel de 0,01 donne à un ADN 37% de chances de survivre au processus de mutation. Changer le paramètre à 0.0227 diminue cette probabilité à environ 10%

La formule mystérieuse est une mutation de 1 bit P = le génome entier 1-P intact au 1/100 , 100 étant la longueur en bits du génome.

Par exemple, pour une probabilité de 10%, mutation du bit P 1 = 1 - 0.1 1/100 = 0.0277
Pour une probabilité de 5%, P = 1 - 0.05 1/100 = 0.0295 En
inversant la formule, nous trouvons que 0.01 donne 37% de chances d'être inchangé par mutation.

J'ai refait exactement le même test (avec une séquence fixe de semences aléatoires) avec une probabilité de 10%.
Sur beaucoup de cartes, les échecs précédents se sont transformés en succès (limités). Par contre, les énormes explosions de population ont été moins nombreuses (ce qui a eu l’effet secondaire intéressant d’accélérer considérablement les calculs).
Même si les scores très élevés (un million et plus) étaient moins fréquents, le nombre de courses plus réussies était plus que suffisant pour compenser.
Au final, la moyenne est passée de 1400+ à environ 2000.

Le réglage de P à 5%, au contraire, a produit une moyenne d’environ 600.
Je suppose que le taux de mutation était si élevé que le génome de rats gagnants a été transféré trop souvent en variants moins efficaces.

Comment ça marche

Avec les détecteurs de téléporteurs ajoutés, le nombre de jeux ayant échoué (score <10) a considérablement diminué.
Sur un essai de 2000 courses, il n'y avait qu'un tiers des échecs.
La moyenne géométrique n'a augmenté que de 2900 à 3300, mais ce nombre ne reflète pas l'amélioration.

Les couleurs vides sont souvent perçues comme des rayons et des dangers (généralement 2 à 5). Le génome "utilise" ces couleurs pour bloquer les chemins qui pourraient causer des problèmes aux rats.

Le génome est assez efficace pour deviner les pièges (c'est-à-dire qu'une fois que les rats sont capables d'atteindre l'objectif, les couleurs représentant les détecteurs de pièges réels sont devinées environ 90% du temps).
Il utilise également les nouveaux codes de faisceau pour les téléporteurs, bien que plus rarement (probablement parce que les téléporteurs "traîtres" sont moins communs que les pièges et que d'autres couleurs de faisceau / danger évoluent pour bloquer le chemin jusqu'aux derniers cas de ces traîtres).

À en juger par le nombre de jeux où un génome gagnant apparaît après 5000 tours ou plus, je pense que cette nouvelle race bénéficierait grandement d'un taux de mutation accru.

Communauté
la source
Comme il y a un nombre pair de pièges, de vides, de murs et de téléports, il vous suffit de 3 bits pour stocker les proportions avec précision (même si vous considérez des pièges == murs). En outre, avez-vous envisagé / écarté l’idée d’utiliser des bits de décalage de piège non utilisés dans le système anti-claquage? Puisque l'objectif est de ne pas hériter des parents, vous pouvez utiliser tous les éléments de l'anti-wallbanging. Aucune raison pour qu'ils soient uniques, je ne penserais pas.
Mooing Duck
1
@MooingDuck J'ai testé votre idée de réutiliser des bits de décalage, mais cela a échoué. Comme je le craignais, réutiliser une information à deux fins différentes ne semble pas fonctionner. Supposons, par exemple, que les bits de décalage d'une couleur donnée soient nécessaires à un génome pour choisir la direction verticale appropriée dans un chemin donné. Cette couleur ne peut plus représenter un piège significatif sans détruire le chemin qui dépend des mêmes données. J'ai aussi essayé d'utiliser 6 bits, mais comme je craignais, les 4 restants anti-mur étaient trop rares.
1
Bon à savoir, mais là, j’ai suggéré deux idées: l’une consistait à utiliser tous les bits (en en réutilisant certains) et l’autre, à utiliser les bits inutilisés avec décalage du piège pour les murs / vides. Avez-vous essayé les deux? (Je comprends tout à fait que si tu ne veux pas essayer, tu es à peine obligé d'essayer si tu ne veux pas.)
Mooing Duck
1
J'ai essayé les deux, et les deux ont échoué. Les compensations de piège sont importantes même lorsqu'un gène ne les utilise pas, car ce gène peut toujours se reconvertir en une couleur de piège. Dans ce cas, le décalage de piège aura probablement muté dans le contexte le plus rentable et perdra sa signification. . Maintenant, il va revenir à la valeur de compensation rentable et détruire les chemins des rats qui en dépendaient comme indicateurs contextuels. Je pense avoir vu le cas d'une telle oscillation avec mon outil graphique, mais il n'est pas facile de montrer un exemple clair de ce problème.
16

ColorScorePlayer, note préliminaire 22

C'est le bot que vous voyez à l'œuvre dans le GIF dans le défi.

C'était notre bot de test tout au long de la phase de développement. Il utilise le génome pour stocker un score de qualité pour chacune des 16 couleurs. Ensuite, il effectue le mouvement en avant qui le déplace sur la couleur avec le meilleur score (et ne se déplace jamais -1). En cas d'égalité, un mouvement aléatoire entre les cellules d'attache est sélectionné.

Nous avons porté ce lecteur dans toutes les langues du contrôleur, il constitue donc un exemple d'utilisation:

Python

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)

Rubis

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

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

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

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

        restricted_coords.sample(random: @rng)
    end
end

C ++

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

C #

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

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

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

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

Java

package game.players;

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

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

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

Le joueur marque de manière assez incohérente. Voici 50 pistes aléatoires:

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
Martin Ender
la source
12

ColorFarSeeker, C ++ ≈ 74,7

Ce défi est vraiment très amusant et simple si vous l’essayez.

Ne soyez pas rebutés par la description longue.
Il suffit de visiter le GitHub et de vérifier les choses ... tout sera beaucoup plus clair! :)

Le simulateur C ++ est fortement recommandé pour sa rapidité. Même après avoir traduit mon programme python en C ++, la simulation python ne s'est toujours pas arrêtée.

Ceci est une variante améliorée de ColorScore. Pour tirer le meilleur parti de sa vue 5x5, il considère les déplacements 2 étapes à l’aide d’une fonction pondérée. Un pas en avant donne un poids plus élevé car il a un effet plus immédiat sur la survie. Avancez de 2 pas et perdez du poids.

Essaie d'avancer, mais si on ne voit pas de mouvement sûr ... puis tente de côté ... et si tout échoue, on recule de manière aléatoire.

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

}

But:

Il y a pas mal de 1 ... ce qui peut être un peu déprimant lorsque vous voyez la console crachant 1 après l'autre. Comme une planète avec tout le nécessaire pour la vie, mais aucun signe de civilisation avancée des rats ...
Ensuite, la pointe occasionnelle. :)

Hmm ... apparemment, j'ai eu de la chance pour mon premier lot de courses, obtenant une géométrie de 300+. Les scores fluctuent beaucoup. Quoi qu'il en soit, avec plus de simulations, il est probablement plus proche de 74.

Les scores de mes courses: 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 6 377 638 5540 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 1 1 108379 70122 97238 1 5 2 23 104794 1 10476 59245 1 204 1 1 12 1 29641 1 314894 18785 13 1 3 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 1 51997 1 1 1 1 1 119191 1 31 4 3 1 161422 207 1 64 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 214365 1 40147 2 213952 1 29 3 1 2144435 5 4502444 72111 1 1 1 1 1 774547

Vectorisé
la source
1
J'ai eu une moyenne géométrique de 74,7 avec 1000 jeux, bon travail.
Feersum
8

Bishop - Python, note préliminaire 1.901

L'évêque se déplace toujours en diagonale, de sorte que la moitié du tableau est inaccessible lors d'un parcours donné, mais cela signifie moins de mouvements potentiels à coder, de sorte que chaque fragment du génome peut représenter un mouvement (l'évêque ne recule jamais). Le bit à référencer est choisi en fonction du bloc de carrés 3x3 devant (à droite) du spécimen. Le meilleur mouvement pour une situation donnée n’est jamais qu’une mutation.

Ce bot apprend rapidement au début, mais heurte ensuite souvent un plafond avant d’atteindre l’arrivée, vraisemblablement là où l’un des deux problèmes suivants se produit:

  • Deux ou plusieurs parties du tableau correspondent au même bit mais nécessitent des déplacements différents.
  • Certaines planches ne sont pas praticables en utilisant uniquement des mouvements en diagonale.

Code

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

En dépit de ces limitations, l’évêque réussit très bien, avec des spécimens individuels effectuant chacun plusieurs tours du tableau. J'avais pensé que sur un tour donné, un spécimen ne pouvait se déplacer que sur la moitié du plateau (équivalent uniquement aux carrés noirs ou aux carrés blancs d'un échiquier). Cependant, comme l'a souligné Martin Büttner, un téléporteur peut déplacer un spécimen d'un carré noir à un carré blanc ou inversement, de sorte que sur la plupart des panneaux, ils ne seront pas limités.

(Il y a deux paires de types de téléporteurs appariés et chacune a une probabilité de 0,5 d'avoir un décalage qui déplace un spécimen dans l'autre moitié des carrés noirs et blancs. Ainsi, la probabilité qu'un tableau ne comporte que des téléporteurs la moitié de la planche par tour n’est que de 0,25.)

Les scores montrent que les triomphes occasionnels sont entrecoupés de longues périodes d’échec:

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

trichoplax
la source
8

Joueur bonus: Moyenne géométrique 50.35 (test de 5000 parties)

Ce bot marque des carrés par leurs couleurs individuelles basées sur une section d'ADN de 6 bits comme le lecteur de score de couleur, mais avec un système de numération différent. Ce bot était motivé par l'idée qu'il est plutôt arbitraire qu'un des bits modifie la valeur du score de 32, tandis qu'un autre ne le fait que de 1. Il attribue la valeur n (n + 1) / 2 à une série de n 1 bits consécutifs. De plus, il ajoute un mécanisme de randomisation pour éviter de rester bloqué. Il fera un coup en avant aléatoire avec une chance sur 30.

À titre de comparaison, le joueur avec le score de couleur a marqué 30 à 35 en quelques tests de 1000 matchs. Fait intéressant, le score de jeu maximum du joueur avec un score de couleur se situait dans une fourchette de 3 à 5 millions, alors que le maximum du bonus de course n'était que de 200 000. Le bonus de course bénéficie du système de score moyen logarithmique en obtenant un score différent de zéro de manière plus cohérente.

L'exécution de 5000 jeux prenait environ 20 minutes avec 6 threads sur le contrôleur 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)]};
}
feersum
la source
par curiosité, combien de temps a duré le test des 5000 pistes? Mes rats ont besoin de plus d'une heure pour réaliser 1000 pistes. Je devrais donc laisser l'ordinateur fonctionner toute la nuit pour reproduire votre scénario de test.
@kuroineko La réponse à votre question figurait déjà dans ma réponse.
Feersum
Oops désolé. Je vais alors essayer votre code sur mon PC pour voir quel rôle joue le matériel informatique dans la différence de vitesse. Et peut-être essayer d'utiliser gcc au lieu de MSVC. J'ai constaté une amélioration de 30% des performances par rapport à MSVC par rapport à quelques autres bits de code chargés de calculs.
Votre code a duré un peu plus de 20 minutes pour 1000 pistes sur mon [email protected] avec 4 threads. Le score était d'environ 56 . Cela semble vouloir dire que mon PC est 5 fois plus lent que le vôtre et que mon code serait environ 6 fois plus lent sur une machine donnée (mais avoir un meilleur score implique mécaniquement un temps de calcul plus long). Puisque je suis trop fauché pour acheter un nouveau PC, il est temps de
8

StarPlayer | C ++ | Score: 162 (basé sur 500 parties jouées)

Ce joueur essaie d'utiliser A * pour trouver la meilleure voie à suivre. Il assigne les pondérations de la même manière que ColorDePlayer et tente de retrouver son chemin vers le bord droit de la vue. L'implémentation n'est pas la plus jolie que j'ai jamais faite mais elle n'est pas trop lente du moins.

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

Exemple de notes:

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 1 6 6 6 10 4889 12 608 1

Anton
la source
1
En 1000 matchs, j'ai obtenu un score de 133,2, bien.
Feersum
7

WallGuesser - A marqué 113,266 dans un test de 1000 parties

Codage

J'ai fait un encodage 6 bits / couleur vraiment simple. Décoder la couleur [n]

  • Somme chaque bit dans le génome jusqu’à 96
  • Si la somme est> = 4, alors ce carré est bloqué
  • Si le score total est <= 4 alors son score final est égal à 2 ^ de son score total

En répandant les bits d'une couleur dans le génome, j'augmente les chances que des bits des deux parents soient utilisés pour chaque couleur.

Mouvement

J'utilise une recherche basée sur A * (je suis sûr que ce n'est pas très efficace) pour rechercher le chemin le moins coûteux vers n'importe lequel des carrés du bord droit. Si une couleur correspond à, "bloquée", elle ne sera jamais entrée par la recherche. Si la recherche ne trouve pas de chemin, elle suppose que ce rat n’est pas apte à se reproduire et essaie de le terminer en déplaçant l’un vers la gauche.

Réduire le nombre de rats inaptes

Depuis que mon génome est en train de deviner quelles places sont des téléporteurs muraux ou arriérés, les rats qui n'ont pas de conjectures (aucune couleur qui correspond à bloquée) ne sont pas très en forme. Pour essayer de supprimer ces rats si aucune couleur n'est marquée comme bloquée, TOUTES LES couleurs sont marquées comme étant bloquées et le rat se déplacera toujours d'un vers la gauche.

FAIRE

À l'heure actuelle, le comportement n'est pas aléatoire et il est donc facile pour les rats de rester coincés.

#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;
}
Ceribia
la source
Hm, ça ne compile pas pour moi g++ -std=c++11 .\wallguesser.cpp -O2 -o .\wallguesser.exe. Je reçois beaucoup d'erreurs mais la première est.\wallguesser.cpp:47:19: error: 'dna_t' has no member named 'at' if (d.at(i) == true){
Martin Ender
Pas de problème, il suffit de changer atpour le []corriger.
feersum
7

The FITTEST - Score moyen géométrique: ~ 922 (2K pistes)

Mon approche est de:

  1. Découvrez ce qui tue l'espèce et définissez le comportement souhaité (fonctionnel)
  2. Implémenter le comportement souhaité dans le code (technique)
  3. Donnez-lui une priorité . Est-ce plus important ou moins important que les autres comportements souhaités?
  4. Optimisez le score moyen géométrique en modifiant les paramètres des solutions.

J'ai testé plus de 2000 ensembles de paramètres avec les mêmes 50 graines. Les séries les plus prometteuses ont été sélectionnées et ont été évaluées à l'aide de 250 graines identiques et celles ayant le rang le plus élevé ont été entrées pour la prochaine série de tests. J'ai donc réussi à créer un algorithme génétique pour trouver l'algorithme génétique optimal pour ce problème, comme suggéré par l' utilisateur mbomb007 .

Le comportement souhaité:

  1. Les espèces devraient apprendre quelles couleurs sont sans danger et lesquelles sont mauvaises.
  2. L’espèce devrait principalement décider où aller s’installer en se basant sur les 3 cellules situées juste devant, mais si aucun bon mouvement n’est disponible, il faut envisager des mouvements verticaux ou rétrogrades.
  3. L'espèce devrait également regarder ce qui se trouve au-delà des 8 cellules autour de lui et l'utiliser dans la prise de décision
  4. L'espèce devrait apprendre à identifier les pièges .
  5. Certaines espèces supposent à tort que les murs sont bons et essaient de s'y déplacer tout le temps et restent donc coincées devant les murs. S'ils sont l'espèce avec le score le plus élevé à ce moment-là, leur ADN basé sur une hypothèse erronée concernant le mur est dupliqué plusieurs fois chez le nouveau-né . Après un certain temps, toutes les espèces sont coincées devant les murs et aucune d’elles n’atteint l’objectif de marquer des points. Comment arrêter les abrutis?

Méthodes de stockage de données:

Nous voulons que les espèces apprennent des choses, s’adaptent à leur environnement, deviennent les plus aptes. Inévitablement, cela ne fonctionne que si l'apprentissage peut être mémorisé. L'apprentissage sera "stocké" dans les 100 bits d'ADN. C'est une façon étrange de stocker, car nous ne pouvons pas changer la valeur de notre ADN. Nous supposons donc que l’ADN stocke déjà des informations sur les bons et les mauvais mouvements. Si, pour une espèce donnée, les informations correctes sont stockées dans son ADN, il progressera rapidement et produira de nombreuses nouvelles espèces avec son ADN.

J'ai découvert que le score moyen géométrique était sensible à la manière dont l'information était stockée. Supposons que nous lisions les 4 premiers bits des 100 bits de l'ADN et souhaitons le stocker dans une variable entière. Nous pouvons le faire de plusieurs manières:

  1. stockage de données décimales: en utilisant la fonction "intégrée" dnarange, exemple: 4bits 1011deviendront 1x2 ^ 3 + 0x2 ^ 2 + 1x2 ^ 1 + 1x2 ^ 0 = 15. Valeurs possibles (pour 4 bits): [0, 1 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  2. streaks data storage: en utilisant la dnaStreakRangefonction (définie ci-dessous), exemple: 4bits 1011 deviendront 1x1 + 0x1 + 1x1+ 1x2 = 4. Valeurs possibles (pour 4 bits): [0, 1, 2, 3, 6, 10]
int dnaStreakRange(dna_t &d, int start, int chunklen) {
    int score = 0;
    int streak = 0;
    for(int i = 0; i < chunklen; i++) {
        if(d[start + i])
            score += ++streak;
        else
            streak = 0;
    };  
    return score;
}
  1. Stockage de données en bits: en utilisant la dnaCountRangefonction (définie ci-dessous), exemple: 4bits 1011 deviendront 1x1 + 0x1 + 1x1 + 1x1 = 3. Valeurs possibles (pour 4 bits): [0, 1, 2, 3, 4]
int dnaCountRange(dna_t &d, int start, int chunklen) {
    int score = 0;
    for(int i = 0; i < chunklen; i++) {
        if(d[start + i])
            score ++;
    };  
    return score;
}

La différence entre les méthodes de stockage sont:

  • La méthode de stockage décimal est vulnérable pour un seul changement dans l’ADN. Lorsque la valeur en bits passe de 1011 à 0011, sa valeur passe de 3 à 2, ce qui constitue un changement mineur.
  • La méthode de stockage décimal est homogène . Chacune des valeurs possibles a le même changement à se produire. La probabilité que vous lisiez la valeur de 15 à partir d'un bloc de mémoire de stockage 4 bits est de 1/16 = 6%. La méthode de stockage de traînées n'est pas homogène . La probabilité qu'une valeur de 4 bits de traînée soit inférieure ou égale à 6 est égale à (15-3) / 16 = 81% (toutes les 16 combinaisons sauf 0111,1110,111). Ci-dessous un visuel qui montre la forme de la distribution. Comme vous pouvez le voir sur la flèche bleue, la probabilité qu'une traînée sur 4 bits soit inférieure ou égale à 6 est de 81%: visualisation de la distribution des types de stockage décimal, streak et bitum pour des nombres binaires longs de 4,5 et 6 bits

Prioriser les solutions.

Lorsque ColorScorePlayer a identifié deux transferts avec des scores identiques, un choix arbitraire est effectué. IMHO, vous ne devriez jamais utiliser la fonction de v.rng.rint()fonction aléatoire . Au lieu de cela, vous devriez utiliser cette opportunité de scores égaux pour évaluer les solutions d’effets de second ordre.

Les effets de premier ordre ont la plus haute priorité. Si des scores égaux sont atteints, la solution avec la priorité 2 prévaut et ainsi de suite. En peaufinant les paramètres d'une solution, vous pouvez influencer la probabilité que des scores égaux se produisent et modifier ainsi le poids des solutions de priorité 1 et de priorité 2.

Mise en œuvre du comportement souhaité

Apprenez quelles couleurs sont sans danger:

  • 33% des 16 couleurs sont mauvaises et par conséquent, lorsque le score d'un déplacement est inférieur à 63/3, le déplacement n'est pas autorisé. Par conséquent threshold = 63/3=21, où 63 est le score maximal pour 6 bits et 33% = 1/3 (peut être recherché dans le graphique ci-dessus).

Si aucun bon mouvement n'est disponible, déplacez-vous verticalement ou en arrière:

  • Lorsqu'aucun mouvement en avant n'est autorisé, les mouvements verticaux seront comparés les uns aux autres de la même manière. Si aucun mouvement vertical n'est également autorisé, les mouvements en arrière sont classés. Ceci est réalisé via la weightMovevariable.

Regardez ce qui est au-delà:

  • Lorsque 2 ou 3 coups ont des scores identiques, la case 3x3 entourant ces coups déterminera (via x2et y2boucles) quelle est la meilleure option (via la mainSubScorevariable). La colonne la plus à droite de cette zone 3x3 est en tête.
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};
}

Score: 123 (2K pistes)

50 premiers scores (18 matchs n'ont marqué qu'un point):

1 10 1 79947 3 1 11 125 7333287 23701 310869 53744 1 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

Identifier les pièges:

J'ai examiné l'ADN de l'espèce avec le score le plus élevé lorsqu'un jeu arbitraire s'est terminé en utilisant le stockage a bitsum4 (le score de couleur a donc une plage [0,4]):

  • marqué 0: téléportation en arrière, les deux murs, 1x coffre-fort
  • marqué 1: Piège en arrière (si inoffensif), Téléportation en arrière, 1x coffre-fort
  • marqué 2: Piège en avant (si dangereux), 1x sûr
  • a marqué 3: téléporteur en avant, 5x en toute sécurité
  • marqué 4: téléporteur avant, 1x coffre-fort

On peut en conclure que les murs et les téléports obtiennent un score correct. Les pièges ne sont pas identifiés car ils dépendent de la direction et de la couleur d'origine, tandis que les scores sont calculés en fonction de la couleur de la destination. Par conséquent, il est également nécessaire de stocker des données sur la couleur d'origine v(0,0). Dans un monde idéal, nous aimerions stocker des informations pour 16 couleurs x 8 directions x 3 bits = 384 bits.

Malheureusement, seuls 100 bits sont disponibles et nous ne pouvons pas tout utiliser car nous avons également besoin de mémoire pour la solution décrite ci-dessus. Nous allons donc faire 4 bacs de couleurs:

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

et 4 bacs directionnels

  • 0: se déplacer verticalement ou en arrière,
  • 1: aller de l'avant,
  • 2: aller de l'avant,
  • 3: avancer vers le bas

Lorsque le score décimal est égal ou supérieur à 4 (100,101,110,111), il est supposé qu'un piège est associé à cette cellule. Par conséquent, ce déplacement ne sera pas sélectionné lorsque des scores égaux sont obtenus. L'identification des pièges est donc un effet de second ordre et «regarder au-delà» sera une troisième solution prioritaire.

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

Score: 580 (2K pistes)

50 premiers scores (13 matchs n'ont marqué qu'un point):

28.044 14.189 1 2.265.670 2.275.942 3 122.769 109.183 601.366 61.643 205.949 47.563 138.680 1 107.1 858.66 31 2 29 1 89,519 22 100 908 1430 1 2 3 4 3 7 7 ... 713

La mauvaise hypothèse à propos du mur est répétée plusieurs fois chez les nouveau-nés par des abrutis:

Certaines espèces supposent à tort que les murs sont bons et essaient de s'y déplacer tout le temps et restent donc coincées devant les murs. Ils peuvent également rester bloqués dans des boucles infinies de téléporteurs. L'effet est le même dans les deux cas.

Le problème principal est qu’après quelques centaines d’itérations, certains gènes deviennent très dominants . Si ce sont les «bons» gènes, vous pouvez obtenir des scores très élevés (> 1 million de points). Si ceux-ci sont incorrects, vous êtes bloqué, car vous avez besoin de la diversité pour trouver les «bons» gènes.

Combats de débiles: Solution 1: inversion des couleurs

La première solution que j'ai essayée consistait à utiliser une partie de la mémoire inutilisée, qui reste très diverse. Supposons que vous ayez alloué 84 bits à votre mémoire couleur et à la mémoire de recherche d’interruptions. Les 16 bits restants seront très diversifiés. On peut remplir 2 variables décimales8 qui ont des valeurs sur l'intervalle [0,255] et qui sont homogènes, ce qui signifie que chaque valeur a une chance de 1/256. Les variables seront appelées inInverseet inReverse.

Si inInverseégale à 255 (une chance sur 256), nous inverserons l'interprétation des notes de couleur . Ainsi, le mur que les imbéciles supposent être sûr à cause de son score élevé, obtiendra un score faible et deviendra donc un mauvais mouvement. L'inconvénient est que cela affectera également les gènes de «droits», nous aurons donc des scores moins élevés. De plus, cette inInverseespèce devra se reproduire et ses enfants auront également une partie de l'ADN dominant. La partie la plus importante est que cela ramène la diversité.

Si la valeur inReverseest égale à 255 (une chance sur 256), nous inverserons l'ordre des positions de stockage des notes de couleur . Ainsi, avant que la couleur 0 soit stockée dans les bits 0-3. Maintenant, la couleur 15 sera stockée dans cette position. La différence avec cette inInverseapproche est que inReversecela annulera le travail accompli jusqu'à présent. Nous sommes de retour à la case départ. Nous avons créé une espèce qui possède des gènes similaires à ceux du début du jeu (à l'exception du piège qui trouve la mémoire)

Via l'optimisation, il est testé s'il est judicieux d'utiliser le inInverseet inReverseen même temps. Après l'optimisation, il a été conclu que le score n'augmentait pas. Le problème est que nous avons une population plus diversifiée, mais cela affecte également le «bon ADN». Nous avons besoin d'une autre solution.

Combats de débiles: Solution 2: code de hachage

L’espèce a 15 positions de départ possibles et il ya actuellement une trop grande chance qu’il suive exactement le même chemin s’il commence à la même position de départ. S'il est un abruti qui aime les murs, il restera coincé sur le même mur encore et encore. S'il réussit par hasard à atteindre un mur très éloigné, il commencera à dominer le pool d'ADN avec ses fausses hypothèses. Ce dont nous avons besoin, c’est que sa progéniture emprunte un chemin légèrement différent (car pour lui, c’est trop tard de toute façon), et ne reste pas coincée sur le mur très éloigné, mais sur un mur plus proche . Ceci peut être réalisé en introduisant un hashcode .

Un code de hachage doit avoir pour but d'identifier et d'étiqueter de manière unique la position actuelle sur le tableau. Le but n'est pas de savoir quelle est la position (x, y), mais de répondre aux questions de mes ancêtres ont-ils déjà été à cet endroit?

Supposons que vous ayez le tableau complet devant vous et que vous fassiez un jpg de chaque carré possible de 5 cellules sur 5. Vous vous retrouveriez avec (53-5) x (15-5) = 380 images. Donnons à ces images un nombre compris entre 1 et 380. Notre code de hachage doit être considéré comme un identifiant, mais il est différent du fait qu’il ne se situe pas entre 1 et 330, mais qu’il manque un IDS, par exemple 563, 3424, 9424, 21245, etc.

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

Les nombres premiers 17et 31sont là pour empêcher les informations ajoutées au début dans la boucle de disparaître. Plus tard, nous verrons comment intégrer notre code de hachage au reste du programme.

Remplaçons le mécanisme de sous-notation "regarde au-delà de" par un autre mécanisme de sous-notation. Lorsque deux ou trois cellules ont des scores principaux égaux, il y aura 50% de chances que la première soit sélectionnée, 50% de chances que les cellules du bas soient sélectionnées et 0% de chances que celle du milieu le soit. La chance ne sera pas déterminée par le générateur aléatoire, mais par des bits de la mémoire , car nous nous assurons ainsi que dans la même situation, le même choix est fait.

Dans un monde idéal (où nous avons une quantité infinie de mémoire), nous calculons un code de hachage unique pour notre situation actuelle, par exemple 25881, puis passons à l'emplacement de mémoire 25881 et lisons-le si nous devons sélectionner la cellule supérieure ou inférieure (lorsque est un score égal). De cette manière, nous nous retrouverions exactement dans la même situation (lorsque, par exemple, nous irions au conseil d’administration pour la deuxième fois et commencerions au même poste), nous prendrions les mêmes décisions. Puisque nous n'avons pas de mémoire infinie, nous appliquerons un modulo de la taille de la mémoire disponible au hashcode . Le hashcode actuel est bon dans le sens où la distribution après l'opération modulo est homogène.

Lorsque la progéniture voyage sur le même tableau avec un ADN légèrement modifié, il prendra dans la plupart des cas (> 99%) exactement la même décision. Mais plus il avance, plus il a de chances que son chemin soit différent de ses ancêtres. Ainsi, les chances qu’il reste coincé sur ce mur très éloigné sont minimes. Bien que coincé sur le même mur que son ancêtre à proximité, il est relativement grand, mais ce n’est pas si grave, car il ne générera pas beaucoup de progénitures. Sans l' approche du hashcode , les chances de rester coincé sur le mur proche et éloigné sont presque les mêmes.

Optimisation

Après l'optimisation, il a été conclu que la table d'identification des interrupteurs n'était pas nécessaire et que 2 bits par couleur suffisaient. Le reste de la mémoire 100-2x16 = 68 bits est utilisé pour stocker le code de hachage. Il semble que le mécanisme de code de hachage puisse éviter les pièges.

J'ai optimisé pour 15 paramètres. Ce code comprenait le meilleur ensemble de paramètres modifiés (jusqu'à présent):

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

Score: 922 (2K pistes)

50 premiers scores (9 matchs n'ont marqué qu'un point):

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 31.12 133.399 2 3 3 3 3 3 3 6 6 6 736 avec plus de 3 000

Ceci est mon tout premier programme C ++. Comme la plupart d'entre vous, j'ai maintenant de l'expérience dans l'analyse des gnomes. Je tiens à remercier les organisateurs, car j’ai vraiment aimé travailler sur ce projet.

Si vous avez des commentaires, s'il vous plaît laissez un commentaire ci-dessous. Toutes mes excuses pour les longs textes.

Ruut
la source
Je trouve votre analyse des pièges très intéressante.
Avez-vous essayé une autre fonction de hachage, comme par exemple enregistrer les 25 valeurs de couleur sous forme de 12,5 mots de 16 bits et prendre un modulo? Je ne suis pas convaincu que la congruence des nombres premiers donne une meilleure homogénéité, mais je ne suis pas un grand mathématicien.
Aussi, avez-vous envisagé d'ajouter un algorithme de cheminement? Cela semble être un facteur d’amélioration considérable, quel que soit le génome, dans la mesure où il limitera les mouvements à ceux qui testent la capacité du génome uniquement le long de chemins beaucoup plus susceptibles de mener à une position gagnante.
Kuroi, merci pour vos commentaires. Je n'ai pas essayé le xoring, car je ne suis pas si familier avec les opérations binaires en c ++. Je suppose que vous voulez dire 12,5 mots de 8 bits? Utilisez-vous xoring?
Ruut
Vous pouvez consulter mon code «croyants durs» pour voir quel type de fonction de hachage j'utilise. Fondamentalement, je saute les cellules hors piste et considère les couleurs sur piste comme des parties d'ordre haut et bas d'un mot de 16 bits. Tous ces mots sont cumulés avec les XOR dans un registre qui est ensuite divisé par la taille de la table de hachage. Tant que la valeur hash max (65535) est bien supérieure à la taille de la table (<100), le modulo a un bon pouvoir d’étalement. Je l'ai testé sur un large ensemble de grilles générées de manière aléatoire et il semble avoir une bonne homogénéité.
6

Pathfinder, C ++, note préliminaire 35.8504 (50 rounds)

Une refonte complète! J'ai porté mon algorithme en C ++ et l'ai légèrement modifié, mais le score n'est toujours pas très élevé, probablement parce que les rats se cognent la tête contre les murs. J'en ai assez d'essayer d'améliorer cela, alors je vais le laisser pour l'instant.


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

Explication

L'idée générale est de classer chaque couleur en tant que piège ou non, puis d'assigner des instructions aux pièges et des poids aux non-pièges, et d'essayer de suivre le chemin du poids minimal jusqu'au bord droit de la grille de vision.

Dans les 80 premiers bits du génome, chaque couleur est classée selon 5 bits abcde. Si ab = 01, la couleur est un piège et cdecode sa direction (huit possibilités). Si ab ≠ 01, la couleur n'est pas un piège, et son poids est a + b + 2*(c + d + e).

Ensuite, nous initialisons une grille 3x7, qui représente le champ de vision du rat à sa droite, avec des couleurs "inconnues". Les bits 80 à 84 codent le poids des cellules inconnues de la même manière que les couleurs sans piège, et les bits 85 à 89 codent un poids commun pour les pièges. Nous remplissons la grille avec les poids, calculons les chemins les plus courts et ajoutons un poids supplémentaire (codé dans les bits 90 à 95) aux cellules situées directement au-dessus et au-dessous du rat pour décourager les déplacements indirects. Les bits 95 à 99 encodent un poids cible. Si le poids minimal d'un chemin est inférieur à celui-ci, le rat est probablement coincé quelque part et continue à se déplacer de manière aléatoire (mais ne fait jamais marche arrière). Sinon, il suit le chemin du poids minimal. Avec une faible probabilité en fonction du poids empêchant le pas de côté, le rat choisit plutôt le trajet du poids du second au minimum. C'est pour éviter de se coincer contre les murs (mais cela ne semble pas très bien fonctionner pour le moment).

Zgarb
la source
Ran votre mise en œuvre sur mon ordinateur. A pris quelques heures. Il obtient un score moyen de 7,848433940863856 points. pastebin.com/d50GcwnK
Jakube
@ Jakube Merci beaucoup! C'est bien pire que ce à quoi je m'attendais, mais maintenant que je regarde à nouveau le code, je vois plusieurs bugs et autres bizarreries. J'essaierai de porter cela au C ++ plus tard pour pouvoir l'analyser moi-même.
Zgarb
5

LookAheadPlayer C ++ .90 89,904

Ma pensée initiale était de rechercher 4 bits qui correspondent à la couleur que je cherchais, en utilisant les bits suivants comme partition. Cela s'est avéré être une idée terrible, probablement à cause de mutations.

J'ai donc réfléchi aux moyens de protection contre les mutations et les croisements, et cela m'a rappelé le travail que j'ai effectué sur le décodage du code QR. Dans les codes QR, les données sont divisées en blocs et mises en bandes pour éviter que des erreurs ne détruisent trop d'une partie donnée des données.

Par conséquent, à l'instar de ColorScorePlayer, j'ai coupé l'ADN en 16 morceaux et les ai utilisés comme score. Cependant, les scores sont entrelacés de sorte que les bits individuels de chaque score ne soient pas adjacents. Je résume ensuite le score des mouvements possibles actuels et potentiels et je choisis le meilleur mouvement à effectuer.

Remarque: ceci a été codé / testé sur MinGW. Il ne compilerait pas avec des optimisations ni avec le multithreading. Je n'ai pas d'installation Linux ou de Visual Studio à portée de main pour utiliser un compilateur où cela fonctionnera. Je vais le tester rapidement demain matin, mais s'il vous plaît laissez-moi savoir si vous rencontrez des problèmes.

// 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];
}
aschmack
la source
5

SlowAndSteady C ++ (score 9,7)

Nous ne pouvons pas compter sur l'interprétation de morceaux du génome sous forme de nombres, car un seul basculement sur un bit peut avoir des effets radicalement différents en fonction de sa position. C'est pourquoi j'utilise simplement 16 segments de 6 bits et les marque sur le nombre de 1s. Au début, 111111c'était bon et 000000c'était mauvais, et bien que cela n'ait pas d'importance à long terme (une fois le génome complètement évolué) dans la configuration initiale de l'ADN, la plupart des segments en ont 2-4, alors je suis passé à l'utilisation 9 - (#1 - 3)^2pour la notation, permet beaucoup plus de liberté de mouvement lors des premiers tours et une évolution plus rapide.

Pour le moment, je ne regarde que les 7 voisins les plus proches, ajoute un biais de direction au score de couleur et me déplace dans l'une des directions les plus hautes au hasard.

Bien que le score lui-même ne soit pas très élevé, mes créatures atteignent la ligne d'arrivée et atteignent> 1 sur 3/4 des cas.

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

Et un échantillon marquant sur 100 planches

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

Note moyenne géométrique: 9.76557

DenDenDo
la source
Le score que vous mentionnez pour un tableau utilise-t-il le taux de mutation standard ou votre valeur ajustée?
Trichoplax
« mes bestioles atteignent la ligne d'arrivée et le score> 1 3/4 des cas » Je souhaite que la métrique de notation récompensa
Sparr
5

WeightChooser | C # | Score: 220.8262 aux Jeux de 1520

Calcule le poids pour le prochain mouvement possible (bleu) en fonction du poids moyen des mouvements possibles suivis (jaune)

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
Alexander Herold
la source
5

RATS IN ACTION (pas une réponse, mais un outil graphique pour les robots C ++)

Depuis le début de ce défi, j'ai eu du mal à comprendre à quoi les rats étaient réellement confrontés sur la piste.
Finalement, j'ai piraté le contrôleur et écrit un outil latéral pour obtenir une représentation graphique d'une piste.
J'ai finalement fait un peu plus de piratage et ajouté une visualisation des chemins possibles de l'ADN d'un rat donné.

La carte est extrêmement encombrée et demande un peu d’habitude, mais j’ai trouvé très utile de comprendre le fonctionnement de mes robots.

Voici un exemple:

piste d'échantillon

Vous aurez probablement besoin de zoomer pour voir quoi que ce soit, alors voici juste la première moitié:

half track (sans jeu de mots)

Au début, regardons les chemins du rat. Il existe un chemin pour chaque point de départ possible (généralement 15, parfois un peu moins). Habituellement, ils ont tendance à fusionner, conduisant idéalement à un seul lieu de victoire.

Les chemins sont représentés par de grandes flèches droites. La couleur décrit le résultat:

  • vert: gagner
  • jaune: boucle infinie
  • marron: claquement au mur
  • rouge: accident malheureux

Dans l'exemple, nous avons 12 positions de départ gagnantes, une menant à une boucle infinie et deux à une mort exténuante (être téléporté dans un piège, comme il apparaît).

Les discontinuités du chemin sont dues aux téléportations, que vous pouvez suivre avec les flèches incurvées correspondantes.

Maintenant pour les symboles colorés. Ils représentent la signification des 16 couleurs (les gris représentent ce que voit un rat).

  • mur: carré
  • téléporteur: étoile à 4 branches
  • détecteur de piège: petit octogone

les couleurs vides sont ... bien ... vides.

Les téléporteurs ont des flèches sortantes pointant vers leur destination.

Les détecteurs de piège ont également des flèches indiquant le piège, qui est représenté comme un cercle rouge.
Dans un cas sur 9, le piège est situé dans la même cellule que son détecteur, auquel cas vous verrez le petit octogone au-dessus du cercle rouge.

C'est le cas du piège jaune pâle dans cet exemple.
Vous pouvez également voir les détecteurs de piège mauve pointés vers le piège indiqué.

Notez que parfois le cercle rouge d'un piège sera caché sous un mur. Les deux sont mortels, le résultat est le même en cas de téléportation.
Notez également qu'un piège peut être situé sur un téléporteur, auquel cas le téléporteur a la priorité (c'est-à-dire que le rat est téléporté avant de tomber dans le piège, ce qui neutralise en fait le piège).

Enfin, les symboles gris représentent ce que mes rats voient (c’est-à-dire le sens que leur génome attribue aux couleurs).

  • mur: carré
  • détecteur de piège: octogone
  • piège: X

Fondamentalement, toutes les cellules assises sur un carré gris sont considérées comme des murs par le rat.
Les gros X représentent des cellules considérées comme des pièges, les octogones correspondants indiquant le détecteur qui les a signalés.

Dans cet exemple, les deux murs sont identifiés comme tels, de même que le piège jaune pâle (indiquant bien une cellule mortelle, donc le représenter comme un mur est correct).
Le détecteur de piège mauve a été identifié en tant que tel (il repose sur un octogone gris), mais l'emplacement du piège est incorrect (vous pouvez voir que certains cercles rouges n'ont pas de croix sous eux).

Sur 4 téléporteurs, 2 sont considérés comme des murs (turquoise et beige) et 2 comme des cellules vides (rougeâtres et jaunâtres).

Quelques cellules vides sont considérées comme des détecteurs de pièges ou des murs. En regardant de plus près, vous pouvez voir que ces "détecteurs défectueux" interdisent en effet l'entrée dans les cellules qui pourraient causer des problèmes au rat. Ainsi, même si elles ne correspondent pas aux couleurs réelles, elles ont un objectif précis.

Le code

Eh bien, c'est un gâchis, mais cela fonctionne plutôt bien.

Vu à partir du code du joueur, je n’ai ajouté qu’une interface: une fonction de trace permettant de rapporter la signification d’un ADN donné. Dans mon cas, j'ai utilisé 3 types (mur, détecteur de piège et vide), mais vous pouvez en principe produire tout ce qui est lié à la couleur (ou rien du tout si vous ne voulez pas de graphisme lié au génome).

J'ai piraté le contrôleur pour générer une énorme chaîne de caractères associant la description de la piste et des couleurs à un "parcours à blanc" de l'ADN du rat depuis tous les emplacements possibles.

Cela signifie que les résultats ne seront vraiment significatifs que si le bot n'utilise pas de valeurs aléatoires. Sinon, les chemins affichés ne représenteront qu'un résultat possible.

Enfin, toutes ces traces sont placées dans un gros fichier texte qui sera lu plus tard par un utilitaire PHP qui produit la sortie graphique.

Dans la version actuelle, je prends un cliché chaque fois qu'un rat meurt après avoir atteint une nouvelle forme physique maximale (cela montre assez bien le raffinement progressif du génome sans nécessiter trop de clichés), et un cliché final en fin de partie (qui montre ADN le plus réussi).

Si quelqu'un est intéressé, je peux publier le code.

Il est clair que cela ne fonctionne que pour les bots C ++, et vous devrez écrire une fonction de trace et éventuellement modifier le code PHP si vous souhaitez afficher des données spécifiques au génome (les chiffres en gris dans mon cas).
Même sans informations spécifiques à l'ADN, vous pouvez voir les chemins suivis par votre ADN sur une carte donnée avec très peu d'effort.

Pourquoi une sortie intermédiaire?

Tout d’abord, C ++ n’a pas de bibliothèque graphique portable décente, en particulier lors de l’utilisation de MSVC. Même si les versions Win32 sont généralement disponibles, elles viennent souvent après coup, et le nombre de bibliothèques externes, de paquetages et autres fonctions similaires à Unix rend l'écriture d'une application graphique simple et rapide une douleur terrible pour une partie du corps que la décence empêche moi de nommer.

J'ai envisagé d'utiliser Qt (à peu près le seul environnement qui rend le développement graphique / graphique portable en C ++ une tâche simple et même agréable, à mon humble avis, probablement parce qu'il ajoute un système de messagerie à l' Objectif C qui manque cruellement à C ++ et fait un travail incroyable en limitant la mémoire. gestion au strict minimum), mais cela ressemblait à une surdose pour la tâche à accomplir (et toute personne souhaitant utiliser le code devrait installer le SDK colossal - cela ne vaut vraiment pas la peine, je suppose).

Même en supposant une bibliothèque portable, il n’est pas nécessaire de parler de vitesse (une seconde environ pour produire une image est largement suffisante), et avec sa rigidité proverbiale et son désordre inhérent, C ++ n’est certainement pas le meilleur outil pour ce travail.

De plus, le fait d'avoir une sortie texte intermédiaire ajoute beaucoup de flexibilité. Une fois que les données sont là, vous pouvez les utiliser à d’autres fins (analyse des performances des robots, par exemple).

Pourquoi PHP?

Je trouve le langage extrêmement simple et adaptable, très pratique pour le prototypage. J'en ai fait le langage des animaux de compagnie pour les défis de code qui n'exigent pas de performances extrêmes.
C'est une langue terrible pour le golf, mais le golf n'a jamais été ma tasse de thé.

Je suppose que python ou Ruby seraient tout aussi agréables à utiliser dans le même but, mais je n’ai jamais eu l’occasion de faire un travail sérieux avec eux, et je travaillais sur des sites Web récemment, alors PHP, c’est.

Même si vous ne connaissez pas la langue, il ne devrait pas être trop difficile de modifier le code pour l'adapter à vos besoins. N'oubliez pas le $s avant les variables, tout comme les bons vieux jours basiques :).


la source
1
Voulez-vous s'il vous plaît partager votre outil? Je ne vois ni code ni lien dans votre réponse.
Franky
5

SkyWalker - Python - marque moins de 231 en 50 parties

Code donc d'abord et ensuite quelques explications. J'espère que rien ne s'est cassé en copiant.

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

Quelques explications

À mon avis, la principale différence est que je ne code pas toutes les couleurs. Au lieu de cela, j'essaie de sauvegarder le nombre de couleurs qui sont importantes. À mon avis, ces couleurs sont les pièges, les murs et les téléporteurs. Le spécimen n'a pas besoin de connaître la couleur d'une bonne cellule. Par conséquent, mon génome est structuré de la manière suivante.

  • 2 x 8 bits pour les traps, les 4 premiers bits sont le numéro de couleur, les 4 autres le décalage
  • 2 x 4 bits pour les murs, juste la couleur
  • 4 x 7 bits pour les téléporteurs, encore 4 bits pour la couleur, 3 pour décider du bon ou du mauvais

Cela fait un total de 52 bits utilisés. Cependant, je n'utilise que le premier bit des 3 décisions du téléporteur (je vérifie si le nombre est supérieur à 3). Par conséquent, les 2 autres pourraient être supprimés, me laissant à 44 bits utilisés.

À chaque tour, je vérifie chaque champ de ma vision s'il s'agit de l'une des mauvaises couleurs (+ le hors-tableau -1) et l'ajoute à une liste de champs que le spécimen ne souhaite pas déplacer. Dans le cas d'un piège, j'ajoute le champ qui se trouve sur l'offset enregistré pour cette couleur de piège.

En fonction de la liste de ces champs incorrects, le prochain mouvement est calculé. L'ordre des champs préférés est:

  1. vers l'avant
  2. haut ou bas
  3. vers le haut ou vers le bas
  4. en arrière

Si deux champs d'une catégorie sont applicables, l'un est choisi de manière aléatoire.

Résultats

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

Pensées

  • Je ne sais pas du tout si j'ai eu de la chance avec les 50 points ou s'il y a de la sagesse dans ma stratégie.

  • Mes courses ne semblent jamais décoller et obtenir des scores super élevés, mais ils ont également tendance à trouver au moins quelques fois le but.

  • Quelques petits aléas sont bien pour ne pas rester coincés dans un piège certains où près de la fin de la course

  • Je pense que les couleurs non spéciales ne sont jamais mauvaises. Cependant, leurs instances peuvent être mauvaises, lorsqu'elles sont en décalage d'un piège. Ainsi, étiqueter une couleur comme mauvaise si ce n'est pas un piège, un mur ou un téléporteur n'a aucun sens.

  • Les murs sont les plus grands ennemis

Améliorations

Premièrement, même si je vais manquer de voir les carrés noirs se rapprocher de plus en plus de l'objectif, un port C ++ est nécessaire pour effectuer davantage de tests et obtenir un résultat plus significatif.

L'un des principaux problèmes est que s'il y a de mauvaises cellules (ou celles que le spécimen pense mauvaises) devant le rat, il commence facilement à monter et à descendre en cercles. Cela pourrait être stoppé ou réduit en regardant 2 pas en avant dans ces cas et en l'empêchant de se déplacer vers un champ où il ne ferait que revenir en arrière.

Il faut souvent attendre assez longtemps avant qu'un rat doté de bons gènes atteigne son objectif et commence à le propager. J'ai peut-être besoin d'une stratégie pour augmenter la diversité dans ces cas.

Comme les téléporteurs sont difficiles à calculer, je devrais peut-être diviser la population en personnes à risque et prendre toujours de bons téléporteurs et ceux qui sont plus concernés et ne les prendre que s'il n'y a pas d'autre choix.

Je devrais utiliser la seconde moitié de mon génome d'une manière ou d'une autre.

Dominik Müller
la source
J'essaie aussi de stocker les couleurs, mais à la fin j'ai conclu que cela ne fonctionnait pas, car vous obtiendrez des doubles. Par exemple , si self.bit_chunk(16, 4)et self.bit_chunk(20, 4)avoir à la fois la valeur que 0010vous avez effectivement que les informations stockées sur l' un des deux pièges.
Ruut
J'avais besoin d'ajouter l'indentation à une ligne pour que cela fonctionne - je suppose que cela s'est perdu lors du copier-coller. Je l'ai ajouté à votre code ici aussi.
Trichoplax
Pour tous ceux qui souhaitent exécuter ceci: Il fonctionne en python 2 et peut être exécuté en python 3 en modifiant l’occurrence unique de itervaluesto values.
Trichoplax
J'ai eu les résultats suivants: [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] Moyenne géométrique 122,9220309940335
trichoplax
On dirait que nous aurions besoin de courir plus de 50 jeux pour obtenir un score fiable.
Trichoplax
3

Python, NeighboursOfNeothers, Score = 259.84395 plus de 100 jeux

Ceci est une variante de ColorScorePlayer. Tous les 6 bits stockent un score de qualité pour un carré. Lorsque le bot se déplace, il marque chacune des 3 cases suivantes: diagonale vers le haut, avant et diagonale vers le bas. Le score correspond à la qualité du carré plus la moitié de la qualité moyenne des 3 carrés suivants. Cela donne au bot un aperçu de l'avenir, sans pour autant nuire à la qualité du premier carré. L'algorithme est similaire à LookAheadPlayer, que je n'avais pas vu avant d'écrire cette solution.

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)
utilisateur2487951
la source
Il manquait une indentation sur une ligne. Je suppose que ça s'est perdu lors du collage. Je l'ai ajouté.
trichoplax
En cours d'exécution en python 3, il s'est plaint de ne rien comparer lors du calcul de max (scores). J'ai donc changé else Noneà else 0la ligne précédente pour calculer votre score. Espérons que cela laisse votre logique inchangée (je n’ai apporté aucune modification à votre code ici sur SE, mis à part l’ajout de l’indentation perdue).
Trichoplax
En cours d'exécution en python 3, j'ai obtenu les résultats suivants pour cette réponse: [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, 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
Une moyenne géométrique de 428.3750848244933
trichoplax
2

ROUS (rongeur de taille inhabituelle), Java, score = 0

Cela hache les environs pour décider où aller. En raison du fait que le contrôleur Java ne fonctionne pas, je n'ai pas de partition pour cela. Cela n'ira que très loin s'il trouve quelques téléporteurs pour l'aider.Cela a tendance à disparaître et à planter le contrôleur de temps en temps. Ceci est probablement dû au fait que son environnement naturel est le Fire Swamp.

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;
    }
}
Le numéro un
la source
1
Le contrôleur Java fonctionne maintenant.
Martin Ender
3
Au début, je pensais que vous rendiez un hommage à l’ancienne Russie, mais c’est apparemment à Rob Reiner.
Le score minimum possible est de 1
trichoplax le
@trichoplax ...
TheNumberOne
Oh, je vois - alors ça arrive assez souvent, tu ne peux pas atteindre la fin d'une course?
Trichoplax
2

Lookahead de couleur grise (C ++, ~ 1.35)

Celui-ci ne va pas très bien en moyenne, mais à de rares occasions, il exécute brillamment. Malheureusement, nous sommes notés sur la moyenne géométrique (1,35) et non sur le score maximum (20077).

Cet algorithme fonctionne en utilisant simplement des codes gris 4 bits pour mapper le score de chaque couleur entre -2 et 2 (avec un biais vers la plage [-1..1]), et calcule le score de la tuile de chaque mouvement et ses prochains mouvements. . Il utilise également un code gris à 2 bits pour déterminer le multiplicateur de la vignette elle-même, ainsi que le facteur de polarisation pour le déplacement vers la droite. (Les codes gris sont beaucoup moins sensibles aux grands sauts en raison de mutations, bien qu'ils ne favorisent pas vraiment le croisement à mi-code ...)

De plus, cela ne fait absolument rien d'essayer de manipuler les pièges spécialement, et je soupçonne que cela pourrait être la chute (bien que je n'ai ajouté aucune instrumentation au contrôleur pour tester cette théorie).

Pour chaque mouvement possible, il détermine un score et, parmi tous les mouvements avec le score le plus élevé, il choisit au hasard.

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

Lors de ma dernière course, j'ai eu les scores suivants: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1

J'aimerais pouvoir obtenir plus de 20077 et moins de 1. :)

duveteux
la source
1
Utiliser le code gris est une idée géniale! ;)
matovitch
1
+1 pour les codes gris. Cependant, un génome totalement résilient à la mutation nuira beaucoup à la diversité. Et Btw, un score de 20.000 n’est même pas proche du maximum que vous pouvez obtenir. Si certains rats développent la capacité de gérer la piste à partir de n'importe quel point de départ possible, ils deviennent immortels et acquièrent un énorme score de condition physique. Son génome domine rapidement, menant à une population pouvant atteindre près de 50 000 rats et une vingtaine de millions.
2

C ++, TripleScore, Score: 100 ~ 400

Tout d'abord, mon score varie énormément sur plusieurs analyses (principalement en raison du nombre de 1).

Le noyau calcule le score de 5 directions: en haut, en bas, en avant, en haut et en bas. Tout d'abord, les scores de haut en bas sont calculés, puis les résultats sont comparés à la valeur de rester en place. S'il est préférable de rester en place plutôt que de monter ou de descendre, ces directions ne seront pas choisies (elles doivent donc avancer). Ceci permet d'éviter les rebonds (haut, bas, haut, bas, ...) entre 2 points.

Maintenant, les 3 autres directions sont marquées: avance en avant, droite en avant et avant en bas. Toutes les directions étudiées conservent les scores les plus élevés et l’un d’eux choisi au hasard.

Marquer une direction: TripleScore calcule le score d'un mouvement en utilisant 3 sous-scores:

  • Le score de la couleur de la destination (dépend de l'adn, comme dans colorScorePlayer)
  • Le score d'aller de l'avant (dépend de l'adn)
  • Le score maximum de passage à l’avance à partir de la destination (multiplié par un facteur stocké dans l’ADN)

Comme pour les autres réponses, le score dépend grandement du nombre de scores 1 renvoyés.

#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)];
}
Wouter ibens
la source
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

Ce rat hautement non déterministe calcule la probabilité d'aller sur un espace en fonction de son voisinage. Les 16 premières cases du génome représentent les 16 couleurs. 1 dans une fente signifie que la couleur est bonne pour marcher, 0 signifie mauvais. Les 16 suivants conservent la même chose pour l’espace devant votre cible, et ainsi de suite.

Le principal avantage de l'approche probabiliste est qu'il est presque impossible de rester longtemps derrière un mur. L'inconvénient est que vous n'obtiendrez presque jamais un rat presque parfait.

MegaTom
la source
+1 pour l'originalité. Quel genre de score as-tu obtenu?
Je ne l'
ai
Avez-vous oublié de donner cune valeur initiale? Il ne semble pas être défini lorsque vous l'utilisez dans le premier if.
Martin Ender
@ MartinBüttner oui j'ai oublié. Je vais le réparer maintenant.
MegaTom
Je ne connais pas très bien Ruby, mais votre code ne fonctionne pas sous Ruby2.1.5. coordsn’est pas une liste, vous utilisez à la &&place de la andparenthèse oubliée, et même après avoir corrigé tout cela, vous ne limitez pas les valeurs de RNG, vous obtenez donc une direction vide. Est-ce que ce pseudo-code, ou quelque chose destiné à être exécuté avec une sorte de dialecte Ruby?
2

Java, RunningStar, Score = 1817.050970291959 plus de 1000 jeux

Ce bot utilise le code couleur de Run-Bonus avec la technique de StarPlayer .

Mise à jour: contrôleur java fixe.

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;
        }
    }
}
Le numéro un
la source
2

LeapForward, Python 2

Pas particulièrement novateur, mais c’est ma seule tentative qui a bien fonctionné.

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)

En gros, il code quatre couleurs (chacune de 4 bits) à éviter, dans le génome. Il passe ensuite à une couleur qui ne figure pas dans cette liste. Si toutes les couleurs sont mauvaises, cela avance toujours vers l'inconnu.

planificateur
la source
J'aurais probablement dû l'appeler "RedQueen" :)
plannapus
1

Java - IAmARobotPlayer - Score 3.7

Je viens de créer ce rat robot pour la comparaison avec un autre programme (pas très intéressant jusqu'à présent) que j'ai créé. Dans l’ensemble, il ne marque pas très bien, mais s’il réussit quelque part, il aura beaucoup de rats. L’idée est qu’il ne regarde que les trois cellules en face, chaque cellule étant bonne ou mauvaise. Cela donne un nombre binaire. Ensuite, il va rechercher ce numéro dans son génome, prendre les trois bits consécutifs, les convertir également en un nombre et exécuter l'action stockée sous ce nombre. Donc, il agit toujours de la même manière quand il rencontre la même situation.

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

    }
}

Résultat:

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
flawr
la source
1

Spécimens prudents - C ++ - scores environ 2030 sur 200 courses

Ceci utilise la partie couleur (16x4 bits) de l’ADN codant pour Blind Faith mais laisse le reste (36 bits) de l’ADN entièrement inutilisé.

Le codage pour une couleur est soit:

  • 10XX - pour les places sûres;
  • 11XX - pour les carrés mortels; et
  • 0000 à 0111 - pour les 8 types de pièges.

Où X indique les bits non utilisés. Étant donné que seules 2 des 16 couleurs sont des interruptions qui utiliseront les 4 bits (et uniquement si l'offset est décalé, ce qui sera le cas 8 fois sur 9), il y aura généralement 64 bits inutilisés. - la théorie est que les mutations qui affectent ces bits inutilisés ne vont pas détruire le génome et que la stabilité est meilleure que toute solution sophistiquée pouvant utiliser ces bits restants.

Les spécimens l'utilisent ensuite pour planifier une route sûre dans une grille 7x7 centrée sur eux-mêmes (la vision 5x5 leur permet plus un carré de chaque côté pour permettre des pièges décalés) en donnant la priorité à la plus grande distance après 3 déplacements.

Au départ, j’ai commencé à construire des contrôles pour vérifier que le fait que la couleur sur laquelle le spécimen est posé n’est pas mortelle correspond au génome et signale toutes les couleurs erronées en tant que carrés de sécurité UNSURE (et leurs carrés adjacents). complication pour un gain minime à nul par rapport au marquage SÉCURITAIRE de ces carrés et à la mort de quelques spécimens supplémentaires. J'y reviendrai si j'en ai le temps.

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

Exemple de partitions:

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

Score maximum pendant le test: 8 150 817 spécimens sauvegardés.

MT0
la source
Maintenant, vous l'avez fait ... Je voulais économiser Pathing pour plus tard, mais je ne pouvais pas laisser vos rongeurs prudents sans être contrariés :) Comme il apparaît, le pathing fonctionne encore mieux avec un codage plus efficace. Votre idée d'étendre la zone de cheminement à 7x7 semble également prometteuse. Je verrai si je peux l'utiliser.
Je fais actuellement 2000 courses pour cela ... après les 900 premiers, la moyenne semble s'être stabilisée autour de 600, ce qui est assez éloigné de 2000. Souhaitez-vous la réévaluer aussi, pour voir si les 2000 étaient juste un coup de chance?
Martin Ender