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! †
† 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
. -1
repré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,-1
repré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:
- 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.
- Si le spécimen a 100 ans , il meurt. Sinon, incrémentez son âge de 1.
- 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.
- Si la cellule cible est un mur, le déplacement est modifié en (0,0).
- 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.
- 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.
- 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.
- S'il reste moins de deux spécimens sur le plateau, la partie se termine.
- 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
où 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.md
les 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
pygame
outkinter
.
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.
la source
'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.'
Réponses:
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
Exemple de résultats:
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:
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
11
et10
), mais les pièges nécessitent 4 bits (0ttt
oùttt
repré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
11xx
et mauvais que10xx
), 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:
Ye rongeurs, voici les ennemis de votre espèce
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:
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.
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:
(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.
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.
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.
la source
^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^
signifie ton Je peux deviner le reste, mais ça me pose problème?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)
Episode IV: se repérer sur la grille
Résultats
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:
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:
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:
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%
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.
la source
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
Rubis
C ++
C #
Java
Le joueur marque de manière assez incohérente. Voici 50 pistes aléatoires:
la source
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.
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.
la source
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:
Code
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:
la source
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 ++.
la source
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.
Exemple de notes:
la source
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]
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.
la source
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){
at
pour le[]
corriger.The FITTEST - Score moyen géométrique: ~ 922 (2K pistes)
Mon approche est de:
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é:
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:
dnarange
, exemple: 4bits1011
deviendront 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]dnaStreakRange
fonction (définie ci-dessous), exemple: 4bits 1011 deviendront1x1 + 0x1 + 1x1+ 1x2 = 4
. Valeurs possibles (pour 4 bits): [0, 1, 2, 3, 6, 10]dnaCountRange
fonction (définie ci-dessous), exemple: 4bits 1011 deviendront1x1 + 0x1 + 1x1 + 1x1 = 3
. Valeurs possibles (pour 4 bits): [0, 1, 2, 3, 4]La différence entre les méthodes de stockage sont:
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:
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:
weightMove
variable.Regardez ce qui est au-delà:
x2
ety2
boucles) quelle est la meilleure option (via lamainSubScore
variable). La colonne la plus à droite de cette zone 3x3 est en tête.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]):
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:
et 4 bacs directionnels
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.
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
inInverse
etinReverse
.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, cetteinInverse
espè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
inReverse
est é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 cetteinInverse
approche est queinReverse
cela 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
inInverse
etinReverse
en 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.
Les nombres premiers
17
et31
sont 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):
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.
la source
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.
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
. Siab = 01
, la couleur est un piège etcde
code sa direction (huit possibilités). Siab ≠ 01
, la couleur n'est pas un piège, et son poids esta + 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).
la source
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.
la source
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
1
s. Au début,111111
c'était bon et000000
c'é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'utilisation9 - (#1 - 3)^2
pour 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.
Et un échantillon marquant sur 100 planches
Note moyenne géométrique: 9.76557
la source
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)
la source
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:
Vous aurez probablement besoin de zoomer pour voir quoi que ce soit, alors voici juste la première moitié:
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:
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).
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).
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
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.
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.
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:
Si deux champs d'une catégorie sont applicables, l'un est choisi de manière aléatoire.
Résultats
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.
la source
self.bit_chunk(16, 4)
etself.bit_chunk(20, 4)
avoir à la fois la valeur que0010
vous avez effectivement que les informations stockées sur l' un des deux pièges.itervalues
tovalues
.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.
la source
else None
àelse 0
la 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).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.la source
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.
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. :)
la source
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:
Comme pour les autres réponses, le score dépend grandement du nombre de scores 1 renvoyés.
la source
Ruby - ProbabilisticScorePlayer
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.
la source
c
une valeur initiale? Il ne semble pas être défini lorsque vous l'utilisez dans le premierif
.coords
n’est pas une liste, vous utilisez à la&&
place de laand
parenthè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?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.
la source
LeapForward, Python 2
Pas particulièrement novateur, mais c’est ma seule tentative qui a bien fonctionné.
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.
la source
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.
Résultat:
la source
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:
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.
Exemple de partitions:
Score maximum pendant le test: 8 150 817 spécimens sauvegardés.
la source