Lab Rat Race: eine Übung in genetischen Algorithmen


113

Dies ist die 14-tägige Herausforderung Nr. 3. Thema: Genetische Algorithmen

Diese Herausforderung ist ein bisschen experimentell. Wir wollten herausfinden, was wir mit genetischen Algorithmen herausfordern können. Es mag nicht alles optimal sein, aber wir haben unser Bestes gegeben, um es zugänglich zu machen. Wenn dies klappt, wer weiß, was wir in Zukunft sehen könnten? Vielleicht ein genetischer King of the Hill?

Die Spezifikation ist ziemlich lang! Wir haben versucht, die Spezifikation in "The Basics" zu unterteilen - das absolute Minimum, das Sie benötigen, um mit dem Framework zu spielen und eine Antwort zu senden - und "The Gory Details" - die vollständige Spezifikation mit allen Details zum Controller, auf deren Grundlage Sie arbeiten könnte deine eigene schreiben.
Wenn Sie irgendwelche Fragen haben, können Sie gerne mit uns chatten!

Du bist ein Forscher in der Verhaltenspsychologie. Es ist Freitagabend und Sie und Ihre Kollegen beschließen, Spaß zu haben und Ihre Laborratten für ein kleines Rattenrennen zu verwenden. Nennen wir sie tatsächlich Exemplare , bevor wir uns zu emotional an sie binden .

Sie haben eine kleine Rennstrecke für die Exemplare eingerichtet, und um es interessanter zu machen, haben Sie ein paar Mauern und Fallen und Teleporter über die Strecke gelegt. Jetzt sind deine Exemplare immer noch Ratten ... sie haben keine Ahnung, was eine Falle oder ein Teleporter ist. Sie sehen nur einige Dinge in verschiedenen Farben. Sie haben auch keinerlei Gedächtnis - alles, was sie tun können, ist Entscheidungen auf der Grundlage ihrer aktuellen Umgebung zu treffen. Ich vermute, dass die natürliche Selektion die Exemplare heraussucht, die wissen, wie man einer Falle aus dem Weg geht, die es nicht wissen (dieses Rennen wird eine Weile dauern ...). Lasst die Spiele beginnen!

Beispielbild der verwendeten Karte

† 84.465 Exemplare wurden bei dieser Herausforderung verletzt.

Die Grundlagen

Dies ist ein Einzelspieler-Spiel (Sie und Ihre Kollegen wollten die Bevölkerung nicht verwechseln, also baute jeder seine eigene Rennstrecke). Die Rennstrecke ist ein rechteckiges Gitter, 15 Zellen hoch und 50 Zellen breit. Sie beginnen mit 15 Proben in zufälligen (nicht unbedingt unterschiedlichen) Zellen am linken Rand (wobei x = 0 ). Ihre Proben sollten versuchen, das Ziel zu erreichen, bei dem es sich um eine beliebige Zelle bei x ≥ 49 und 0 ≤ y ≤ 14 handelt (die Proben können die Spur nach rechts überschreiten). Jedes Mal, wenn dies passiert, bekommst du einen Punkt. Sie starten das Spiel auch mit 1 Punkt. Sie sollten versuchen, Ihre Punkte nach 10.000 Runden zu maximieren .

Mehrere Proben können dieselbe Zelle belegen und interagieren nicht.

Jedes Exemplar sieht in jeder Runde ein 5x5-Raster seiner Umgebung (mit sich selbst in der Mitte). Jede Zelle dieses Gitters enthält eine Farbe -1für 15. -1Stellt Zellen dar, die außerhalb der Grenzen liegen. Ihre Probe stirbt, wenn sie sich außerhalb der Grenzen bewegt. Die anderen Farben repräsentieren leere Zellen, Fallen, Wände und Teleporter. Aber Ihr Exemplar weiß nicht, welche Farbe was darstellt, und Sie auch nicht. Es gibt jedoch einige Einschränkungen:

  • 8 Farben stehen für leere Zellen.
  • 4 Farben repräsentieren einen Teleporter. Ein Teleporter sendet die Probe an eine bestimmte Zelle in seiner 9x9-Nachbarschaft. Dieser Versatz ist für alle Teleporter der gleichen Farbe gleich.
  • 2 Farben repräsentieren Wände. Sich in eine Wand zu bewegen ist dasselbe wie still zu stehen.
  • 2 Farben repräsentieren eine Falle. Eine Falle zeigt an, dass eine der 9 Zellen in ihrer unmittelbaren Nachbarschaft tödlich ist (nicht unbedingt die Falle selbst). Dieser Versatz ist für alle Überfüllungen derselben Farbe gleich.

Nun zu dieser natürlichen Selektion ... jedes Exemplar hat ein Genom, das eine Zahl mit 100 Bits ist. Neue Exemplare werden durch Kreuzung zweier vorhandener Exemplare und anschließende leichte Mutation des Genoms erzeugt. Je erfolgreicher ein Exemplar ist, desto größer ist seine Reproduktionswahrscheinlichkeit.

Hier ist also Ihre Aufgabe: Sie schreiben eine einzelne Funktion, die als Eingabe das 5x5-Farbraster erhält, das eine Probe sieht, sowie ihr Genom. Ihre Funktion gibt eine Bewegung (Δx, Δy) für die Probe zurück, wobei Δx und Δy jeweils eine von sind {-1, 0, 1}. Sie dürfen keine Daten zwischen Funktionsaufrufen beibehalten. Dies beinhaltet die Verwendung eigener Zufallszahlengeneratoren. Ihre Funktion wird mit einem gesetzten RNG ausgestattet, das Sie nach Belieben verwenden können.

Die Bewertung Ihres Beitrags ist das geometrische Mittel der Punktzahl auf 50 zufälligen Tracks. Wir haben festgestellt, dass diese Punktzahl einiges an Varianz aufweist. Daher sind diese Ergebnisse vorläufig . Sobald diese Herausforderung endet, wird eine Frist bekannt gegeben. Am Ende der Frist werden 100 Boards nach dem Zufallsprinzip ausgewählt und alle Einsendungen werden auf diesen 100 Boards neu bewertet. Fühlen Sie sich frei, eine geschätzte Punktzahl in Ihre Antwort einzutragen, aber wir werden jede Einreichung selbst bewerten, um sicherzustellen, dass niemand betrügt.

Wir haben Steuerungsprogramme in einer Handvoll Sprachen bereitgestellt. Derzeit können Sie Ihren Beitrag in Python (2 oder 3), Ruby , C ++ , C # oder Java schreiben . Der Controller generiert die Bretter, führt das Spiel aus und stellt einen Rahmen für den genetischen Algorithmus bereit. Sie müssen lediglich die Bewegungsfunktion bereitstellen.

Warten Sie, was genau mache ich mit dem Genom?

Die Herausforderung besteht darin, das herauszufinden!

Da die Exemplare kein Gedächtnis haben, ist alles, was Sie in einer bestimmten Runde haben, ein 5x5-Raster von Farben, die Ihnen nichts bedeuten. Sie müssen also das Genom verwenden, um das Ziel zu erreichen. Die allgemeine Idee ist, dass Sie Teile des Genoms verwenden, um Informationen über die Farben oder das Rasterlayout zu speichern, und Ihr Bot seine Entscheidungen auf der Grundlage der zusätzlichen Informationen, die im Genom gespeichert sind.

Natürlich können Sie dort nichts manuell speichern. Die dort gespeicherten Informationen sind also zunächst völlig zufällig. Der genetische Algorithmus wird jedoch bald diejenigen Exemplare auswählen, deren Genom die richtigen Informationen enthält, während diejenigen, die die falschen Informationen enthalten, getötet werden. Ihr Ziel ist es, eine Zuordnung von den Genombits und Ihrem Blickfeld zu einer Bewegung zu finden, mit der Sie schnell einen Weg zum Ziel finden und die sich konsequent zu einer Gewinnstrategie entwickelt.

Dies sollten genügend Informationen sein, um Ihnen den Einstieg zu erleichtern. Wenn Sie möchten, können Sie den nächsten Abschnitt überspringen und den Controller Ihrer Wahl aus der Liste der Controller unten auswählen (die auch Informationen zur Verwendung dieses bestimmten Controllers enthält).

Lesen Sie weiter, wenn Sie alle wollen ...

Die blutigen Details

Diese Spezifikation ist vollständig. Alle Controller müssen diese Regeln implementieren.

Jede Zufälligkeit verwendet eine gleichmäßige Verteilung, sofern nicht anders angegeben.

Track-Generierung:

  • Die Spur ist ein rechteckiges Gitter, X = 53 Zellen breit und Y = 15 Zellen hoch. Zellen mit x ≥ 49 sind Zielzellen (wobei x auf Null basiert).
  • Jede Zelle hat eine einzige Farbe und kann tödlich sein oder auch nicht - Zellen sind nicht tödlich, es sei denn, einer der folgenden Zelltypen gibt dies an.
  • Es gibt 16 verschiedene Zellenfarben, die von 0bis beschriftet sind 15und deren Bedeutung sich von Spiel zu Spiel ändert. Stellt außerdem -1Zellen dar, die außerhalb der Grenzen liegen - diese sind tödlich .
  • Wähle 8 zufällige Farben . Dies sind leere Zellen (die keine Auswirkung haben).
  • Wähle 4 weitere zufällige Farben . Das sind Teleporter. Wählen Sie für zwei dieser Farben einen Versatz ungleich Null in der Nachbarschaft 9x9 (von (-4, -4) bis (4,4) mit Ausnahme von (0,0)). Invertieren Sie für die beiden anderen Farben diese Offsets. Wenn ein Exemplar auf einen Teleporter tritt, wird es sofort um diesen Versatz bewegt.
  • Wähle 2 weitere zufällige Farben . Das sind Fallen. Wählen Sie für jede dieser Farben einen Versatz im 3x3-Bereich (von (-1, -1) bis (1,1)). Eine Falle zeigt an, dass die Zelle an diesem Versatz tödlich ist . Hinweis: Die Falle selbst ist nicht unbedingt tödlich.
  • Die 2 verbleibenden Farben sind Wände, die die Bewegung behindern. Der Versuch, auf eine Wandzelle zu gelangen, führt dazu, dass die Bewegung stillsteht. Wandzellen selbst sind tödlich .
  • Wählen Sie für jede Nicht-Ziel-Zelle des Rasters eine zufällige Farbe. Wählen Sie für jede Zielzelle eine zufällige leere Farbe.
  • Bestimmen Sie für jede Zelle am linken Rand der Spur, ob das Ziel innerhalb von 100 Runden erreicht werden kann (gemäß den folgenden Regeln für die Reihenfolge der Runden). Wenn ja, ist diese Zelle eine zulässige Startzelle . Wenn weniger als 10 Startzellen vorhanden sind, verwerfen Sie die Spur und generieren Sie eine neue.
  • Erstellen Sie 15 Exemplare mit einem zufälligen Genom und einem Alter von 0 Jahren . Legen Sie jede Probe auf eine zufällige Startzelle.

Turn Reihenfolge:

  1. Die folgenden Schritte werden der Reihe nach für jede Probe ausgeführt. Die Proben interagieren nicht oder sehen sich nicht und können dieselbe Zelle einnehmen.
    1. Wenn die Probe 100 Jahre alt ist , stirbt sie. Andernfalls erhöhen Sie das Alter um 1.
    2. Die Probe erhält ihr Sichtfeld - ein 5x5-Farbraster, das auf der Probe zentriert ist - und gibt eine Bewegung in ihrer 3x3-Nachbarschaft zurück. Bei Bewegungen außerhalb dieses Bereichs wird der Controller beendet.
    3. Wenn die Zielzelle eine Wand ist, wird der Zug in (0,0) geändert.
    4. Wenn die Zielzelle ein Teleporter ist, wird die Probe um den Versatz des Teleporters bewegt. Hinweis: Dieser Schritt wird nur einmal und nicht iterativ ausgeführt.
    5. Wenn die derzeit von der Probe besetzte Zelle (möglicherweise nach Verwendung eines Teleporters) tödlich ist, stirbt die Probe. Dies ist das einzige Mal, dass Proben sterben (abgesehen von Schritt 1.1. Oben). Insbesondere eine neue Probe, die auf einer tödlichen Zelle erscheint, stirbt nicht sofort ab, sondern hat die Chance, die gefährliche Zelle zuerst zu verlassen.
    6. Wenn die Probe eine Zielzelle belegt, erzielen Sie einen Punkt, verschieben Sie die Probe in eine zufällige Startzelle und setzen Sie ihr Alter auf 0 zurück.
  2. Wenn sich weniger als zwei Exemplare auf dem Brett befinden, endet das Spiel.
  3. Erstellen Sie 10 neue Exemplare mit dem Alter von 0 Jahren . Jedes Genom wird (einzeln) durch die folgenden Zuchtregeln bestimmt. Legen Sie jede Probe auf eine zufällige Startzelle.

Zucht:

  • Wenn ein neues Exemplar erstellt wird, wählen Sie nach dem Zufallsprinzip zwei verschiedene Eltern mit einer Tendenz zu Exemplaren, die weiter nach rechts vorgerückt sind. Die Wahrscheinlichkeit, dass eine Probe ausgewählt wird, ist proportional zu ihrem aktuellen Fitness-Score . Der Fitness-Score eines Exemplars beträgt

    1 + x + 50 * Häufigkeit, mit der das Ziel erreicht wurde

    Dabei ist x der auf 0 basierende horizontale Index. Exemplare, die im selben Zug erstellt wurden, können nicht als Eltern ausgewählt werden.

  • Wählen Sie aus den beiden Elternteilen einen zufälligen aus, dem Sie das erste Genomstück entnehmen möchten.

  • Wechseln Sie jetzt, während Sie durch das Genom gehen, mit einer Wahrscheinlichkeit von 0,05 die Eltern und nehmen Sie dem resultierenden Elternteil weitere Teile ab.
  • Mutieren Sie das vollständig zusammengesetzte Genom: Drehen Sie es für jedes Bit mit einer Wahrscheinlichkeit von 0,01 um .

Wertung:

  • Ein Spiel dauert 10.000 Runden.
  • Die Spieler beginnen das Spiel mit 1 Punkt (um die Verwendung des geometrischen Mittels zu ermöglichen).
  • Jedes Mal, wenn eine Probe das Ziel erreicht, erhält der Spieler einen Punkt.
  • Derzeit wird die Einreichung jedes Spielers für 50 Spiele mit jeweils einer anderen zufälligen Spur ausgeführt.
  • Der obige Ansatz führt zu mehr Varianz als wünschenswert ist. Sobald diese Herausforderung endet, wird eine Frist bekannt gegeben. Am Ende der Frist werden 100 Boards nach dem Zufallsprinzip ausgewählt und alle Einsendungen werden auf diesen 100 Boards neu bewertet.
  • Die Gesamtpunktzahl eines Spielers ist das geometrische Mittel der Punktzahlen dieser einzelnen Spiele.

Die Controller

Sie können einen der folgenden Controller auswählen (da sie funktional gleichwertig sind). Wir haben alle getestet, aber wenn Sie einen Fehler entdecken, den Code oder die Leistung verbessern oder eine Funktion wie eine grafische Ausgabe hinzufügen möchten, senden Sie uns bitte ein Problem oder senden Sie eine Pull-Anfrage auf GitHub! Gerne können Sie auch einen neuen Controller in einer anderen Sprache hinzufügen!

Klicken Sie auf den Namen der Sprache für jeden Controller, um das richtige Verzeichnis auf GitHub aufzurufen, das eine README.mdmit genauen Verwendungsanweisungen enthält .

Wenn Sie nicht mit Git und / oder GitHub vertraut sind, können Sie das gesamte Repository als ZIP von der Startseite herunterladen (siehe Schaltfläche in der Seitenleiste).

Python

  • Am gründlichsten getestet. Dies ist unsere Referenzimplementierung.
  • Funktioniert sowohl mit Python 2.6+ als auch mit Python 3.2+!
  • Es ist sehr langsam. Wir empfehlen, es mit PyPy auszuführen, um eine erhebliche Beschleunigung zu erzielen.
  • Unterstützt die grafische Ausgabe mit pygameoder tkinter.

Rubin

  • Getestet mit Ruby 2.0.0. Sollte mit neueren Versionen funktionieren.
  • Es ist auch ziemlich langsam, aber Ruby kann nützlich sein, um eine Idee für eine Einreichung zu erstellen.

C ++

  • Benötigt C ++ 11.
  • Unterstützt optional Multithreading.
  • Mit Abstand der schnellste Controller im Haufen.

C #

  • Verwendet LINQ, daher ist .NET 3.5 erforderlich.
  • Eher langsam.

Java

  • Nicht besonders langsam. Nicht besonders schnell.

Vorläufige Rangliste

Alle Ergebnisse sind vorläufig. Sollte dennoch etwas falsch oder veraltet sein, lassen Sie es mich bitte wissen. Unser Beispielbeitrag ist zum Vergleich aufgeführt, jedoch nicht in Konkurrenz.

  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

Credits

Diese Herausforderung war eine enorme gemeinsame Anstrengung:

  • Nathan Merril: Hat Python- und Java-Controller geschrieben. Verwandelte das Herausforderungskonzept von einem King-of-the-Hill in ein Rat Race.
  • Trichoplax: Spieltest. Arbeitete auf Python-Controller.
  • feersum: Schrieb C ++ Controller.
  • VisualMelon: Schrieb C # -Controller.
  • Martin Büttner: Konzept. Schrieb Ruby-Controller. Spieltesting. Arbeitete auf Python-Controller.
  • T Abraham: Spieltesting. Python getestet und C # - und C ++ - Controller getestet.

Alle oben genannten Benutzer (und wahrscheinlich ein paar weitere, die ich vergessen habe) haben zum Gesamtdesign der Herausforderung beigetragen.

C ++ Controller Update

Wenn Sie C ++ mit Visual Studio und Multithreading verwenden, sollten Sie das neueste Update erhalten, da ein Fehler beim Seeding des Zufallszahlengenerators aufgetreten ist, durch den doppelte Boards erstellt werden können.


3
Könnte nicht jemand einfach einen genetischen Algorithmus erstellen, um den optimalen genetischen Algorithmus für dieses Problem zu finden?
mbomb007

1
@ anon3202 Nun, das würde dir natürlich mehr Informationen über das Streckenlayout geben, da du abschätzen könntest, wo du bist. Im Grunde wollten wir die Schnittstelle für Bots einfach halten und sie zu einem rein lokalen Problem machen, bei dem Sie das Genom benötigen, um herauszufinden, welche lokale Lösung für Ihren globalen Fortschritt am vorteilhaftesten ist.
Martin Ender

1
@matovitch Siehe Abschnitt 5 des Abschnitts " Turn order " der Gory Details (vollständige Spezifikation):'In particular, a new specimen which spawns on a lethal cell will not die immediately, but has a chance to move off the dangerous cell first.'
Trichoplax

1
Ich habe den C ++ - Code optimiert, um den Mittelwert, stddev, stderr und das 99% -Konfidenzintervall (vor Ihrem "geometrischen" Protokoll / Exp) anzuzeigen, und eine verblüffende Entdeckung gemacht. Die "Blind Faith" -Antwort hatte "Sample Mean of 116529 + - 2.78337e + 010 (99%) stddev = 7.77951e + 010" nach 50 Durchläufen. Wenn das Konfidenzintervall auf 50% gesenkt wird, verbessert sich die Situation überhaupt nicht merklich. Der geometrische Mittelwert war stabiler: "Mittelwert von 159,458 + - 117262 (99%) stddev = 32,6237" (vor seinem 800-Punkte-Update)
Mooing Duck

1
Ich habe einige Experimente mit der Mutationsrate durchgeführt, und ich denke, die Herausforderung wäre interessanter (und die Controller würden viel schneller ablaufen), wenn die Wahrscheinlichkeit von 0,01 auf 0,0227 erhöht würde, was einer DNA nur 10% Chancen gibt, durchzukommen Mutation unverändert statt 37% mit dem aktuellen Wert. Dies vermeidet lächerliche Bevölkerungsexplosionen (was wiederum viel Rechenzeit spart) und vermeidet viele Ausfälle aufgrund unzureichender Diversität. Einzelne Punkte sind niedriger, aber da mehr Läufe Sieger hervorbringen, steigt der globale Durchschnitt tendenziell an.

Antworten:


37

Blindes Vertrauen - C ++ - scheint in 2000 Läufen über 800 (!) Zu liegen

Farbcodierungsgenom mit einem mysteriösen Track-Feedback und einer wirksamen Wall-Banging-Abwehr

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

Beispielergebnisse:

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

Basierend auf dem unfreiwillig langen Test von feersum denke ich, dass 2000 Durchläufe ausreichen, um ein akzeptabel stabiles Ergebnis zu erzielen.
Da mein modifizierter Controller nach jedem Lauf den aktuellen geometrischen Mittelwert anzeigt, habe ich visuell bestätigt, dass die Abweichung über die letzten 50 Läufe relativ gering war (+ - 10 Punkte).

Was bringt diese Tiere zum Ticken?

Anstatt jeder Farbe die gleichen Prioritäten zuzuweisen, berücksichtige ich die folgenden möglichen Werte:

  1. gut -> die Ratte glaubt, dass es dort sicher gehen kann
  2. schlecht -> die Ratte wird nicht dorthin gehen
  3. trap -> Die Ratte betrachtet die Position der Falle als schlecht und die Zelle als gut .
    Obwohl ich zu faul bin, es umzubenennen, ist dies eher ein "Gefahrenmelder", der den (vermeintlichen) Ort einer tatsächlichen Falle, einer Mauer, eines Teleporters anzeigt, der darauf wartet, den ahnungslosen Wanderer an einen unangenehmen Ort oder sogar den Eingang eines Toten zu schicken -Ende. Kurz gesagt, ein Ort, an den eine weise Ratte lieber nicht gehen würde.

Gute oder schlechte Gene benötigen zum Speichern nur 2 Bits (zum Beispiel 11und 10), für Fallen sind jedoch 4 Bits erforderlich ( 0tttwobei ttteiner der möglichen 8 "gefährlichen" Speicherorte angegeben ist).

Um jedes Gen konsistent zu halten (dh seine Bedeutung beizubehalten, nachdem es in ein völlig anderes Genom eingemischt wurde, was erfordert, dass sich jedes Farbcodierungsgen an einem festen Ort befindet), werden alle Werte mit 4 Bits codiert (so gut ist codiert wie 11xxund so schlecht wie) 10xx) für insgesamt 16 * 4 = 64 Bit.

Die restlichen 36 Bits werden als "Anti-Wall-Banger" verwendet (dazu später mehr). Die 25 umgebenden Farben werden in einen Index dieser 36 Bits gehasht. Jedes Bit gibt eine bevorzugte vertikale Richtung (aufwärts oder abwärts) an, die verwendet wird, wenn eine mögliche Wahl zwischen zwei Zellen besteht.

Die Strategie ist wie folgt:

  • Dekodiere jede Farbe nach Genom (oder direkter Kontrollbericht für "schlechte" Zellen außerhalb der Spur)
  • Baue eine Karte der unmittelbaren Umgebung (3x3 Zellen, 8 mögliche Nachbarn)
  • Berechnen Sie eine Signatur der Umgebung (ein Hash der 25 Farben mit Ausnahme von Off-Track-Zellen)
  • Wähle eine bevorzugte vertikale Richtung aus der Signatur (unter 36 Hash-Eimern)
  • Versuchen Sie, sich zu einem Nachbarn zu bewegen, der als "gut" eingestuft ist. Beginnen Sie dabei mit dem Nachbarn, der dem Ziel am nächsten ist, und gehen Sie zuerst in die bevorzugte vertikale Richtung
  • Wenn kein "guter" Nachbar gefunden werden kann, versuchen Sie, eine Zelle zurückzuschieben (möglicherweise Opfer eines unglücklichen Unfalls, und vermeiden Sie es auf jeden Fall, die Fitness zu verbessern).

Ihr Nagetiere, seht die Feinde eurer Art

die gefürchtete Wand Teleportationsschleife

Das Schlimmste, was einer Population passieren kann, ist, noch keinen Sieger hervorgebracht zu haben, aber viele Ratten stecken entweder an einer Wand oder in einer endlosen Teleportationsschleife, die nahe genug am Ziel ist, um eine dominante Chance zu haben, für die Zucht ausgewählt zu werden .
Im Gegensatz zu Ratten, die in einer Falle zerquetscht oder in Wände teleportiert werden, werden diese Nagetiere nur im Alter getötet.
Sie haben von Anfang an keinen Wettbewerbsvorteil gegenüber ihren Cousins, die 3 Zellen stecken, aber sie haben genügend Zeit, um Generation für Generation von Cretins zu züchten, bis ihr Genom dominant wird, wodurch die genetische Vielfalt ohne guten Grund stark beeinträchtigt wird.

Um dieses Phänomen abzumildern, besteht die Idee darin, die Nachkommen dieser bösen, bösen Ratten eher daran zu hindern, den Schritten ihrer Vorfahren zu folgen.
Die vertikale Richtungsanzeige ist nur 1 Bit lang (im Grunde genommen "zuerst in dieser Umgebung auf- oder absteigen"), und es ist wahrscheinlich, dass sich einige Bits auf den verfolgten Pfad auswirken. Daher sollten Mutationen und / oder Überkreuzungen a erhebliche Auswirkungen.
Viele Nachkommen werden sich anders verhalten und nicht mit dem Kopf gegen dieselbe Wand stoßen (zwischen den Leichen ihrer verhungerten Vorfahren).
Die Subtilität hier ist, dass diese Anzeige nicht der dominierende Faktor im Verhalten der Ratte ist. Die Farbinterpretation wird in den meisten Fällen immer noch vorherrschen (die Auswahl nach oben / unten ist nur dann von Bedeutung, wenn es tatsächlich zwei "gute" gibt.und was die Ratte als harmlose Farbe ansieht, ist kein Teleporter, der darauf wartet, sie in eine Wand zu werfen.

Warum scheint es zu funktionieren?

Ich weiß immer noch nicht genau warum.

Der absolute Glücksfall, der ein ungelöstes Rätsel bleibt, ist die Trap-Mapping-Logik. Es ist ohne Zweifel der Grundstein für den Erfolg, aber es funktioniert auf seine eigene mysteriöse Weise.

Mit der verwendeten Codierung erzeugt ein zufälliges Genom 25% "gute", 25% "schlechte" und 50% "gefangene" Farbidentifikatoren.
Die "Trap" -Identifikatoren erzeugen wiederum "gute" und "schlechte" Schätzungen in Korrelation mit der 5x5-Umgebung.
Infolgedessen "sieht" eine Ratte an einem bestimmten Ort die Welt als eine Mischung aus stabilen und kontextuellen "go / no go" -Farben.

Wie der recht erfolgreiche Anti-Banging-Mechanismus zu zeigen scheint, ist die gefürchtete Wand (und ihre Cousine die Teleportationsschleife) das schlimmste Element auf der Strecke, aber ich denke, diese sind weitaus weniger verbreitet.

Die Schlussfolgerung ist, dass ein erfolgreiches Programm es vor allem schaffen muss, Ratten zu entwickeln, die in der Lage sind, Positionen zu erkennen, die zu einem langsamen Hunger führen, ohne das Ziel zu erreichen.

Selbst ohne die beiden Farben zu "erraten", die Wände darstellen, scheinen die "Fallen" -Farben zur Vermeidung von Wänden beizutragen, indem eine Ratte einige Hindernisse umgehen kann, nicht weil sie die Wände "sah", sondern weil die "Fallen" -Schätzung diese ausschloss besondere Wandzellen in dieser besonderen Umgebung.

Obwohl die Ratte versucht, sich dem Ziel zu nähern (was dazu führen könnte, dass die "nützlichsten" Fallenindikatoren diejenigen sind, die auf eine Gefahr im Vordergrund hinweisen), denke ich, dass alle Fallenrichtungen ungefähr den gleichen Einfluss haben: eine Falle, die auf eine "Gefahr im Hintergrund" hinweist "2 Zellen vor einer Ratte gelegen" hat den gleichen Einfluss wie eine, die "Gefahr voraus" anzeigt, wenn die Ratte direkt darüber steht.

Warum diese Mischung die Eigenschaft hat, dass das Genom so erfolgreich konvergiert, kann ich leider nicht nachvollziehen.

Ich fühle mich wohler mit der wandschlagenden Abschreckung. Dies funktionierte wie geplant, jedoch weit über meinen Erwartungen (die Punktzahl wurde im Grunde mit vier multipliziert).

Ich habe den Controller stark gehackt, um einige Daten anzuzeigen. Hier sind ein paar Läufe:

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

Hier tauchte früh eine Rasse von Superratten auf (die Strecke durfte wahrscheinlich geradeaus verlaufen, und einige glückliche Ratten hatten in den ersten Generationen die richtige DNA, um davon zu profitieren). Die Anzahl der Exemplare am Ende ist ungefähr die Hälfte der theoretischen Höchstzahl von 100.000 Ratten, was bedeutet, dass fast die Hälfte der Tiere die Fähigkeit erlangt hat, diese bestimmte Spur auf unbestimmte Zeit (!) Zu überleben.
Natürlich ist die resultierende Punktzahl einfach obszön - wie übrigens auch die Rechenzeit.

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

Hier können wir die Genomverfeinerung bei der Arbeit sehen. Die Linie zwischen den letzten beiden Genomen ist klar erkennbar. Die guten und schlechten Bewertungen sind am wichtigsten. Die Fallenanzeigen scheinen zu oszillieren, bis sie sich entweder zu einer "nützlichen" Falle stabilisieren oder zu gut oder schlecht mutieren .

Es scheint, dass die Farbgene einige nützliche Eigenschaften haben:

  • Sie haben eine in sich geschlossene Bedeutung
    (eine bestimmte Farbe muss auf eine bestimmte Weise behandelt werden).
    Jede Farbkodierung kann in ein völlig anderes Genom geworfen werden, ohne das Verhalten dramatisch zu ändern - es sei denn, die Farbe ist tatsächlich (typischerweise) entscheidend eine Mauer oder ein Teleporter, der zu einer Endlosschleife führt).
    Dies ist bei einer grundlegenden Prioritätskodierung weniger der Fall, da die Farbe mit der höchsten Priorität die einzige Information ist, die zur Entscheidung über den Verschiebungsort verwendet wird. Hier sind alle "guten" Farben gleich, so dass eine bestimmte Farbe, die der "guten" Liste hinzugefügt wird, weniger Auswirkungen hat.
  • Sie sind relativ widerstandsfähig gegenüber Mutationen.
    Die gute / schlechte Kodierung hat nur 2 signifikante Bits von 4 und die Position der Falle kann die meiste Zeit geändert werden, ohne das Verhalten der Ratte signifikant zu verändern.
  • Sie sind klein (4 Bit), daher ist die Wahrscheinlichkeit, durch eine Frequenzweiche zerstört zu werden, sehr gering.
  • Mutationen erzeugen entweder harmlose oder bedeutungslose Veränderungen.
    Ein zu "gut" mutierendes Gen hat entweder nur geringe Wirkung (wenn es beispielsweise einer leeren Zelle entspricht, kann es einen neuen, kürzeren Weg finden, aber das könnte auch die Ratte direkt hineinführen eine Falle) oder eine dramatische (wenn die Farbe eine Wand darstellt, bleibt die neue Ratte sehr wahrscheinlich irgendwo hängen).
    Ein Gen, das sich in die "Falle" dreht, entzieht der Ratte entweder eine essentielle Farbe oder hat keine wahrnehmbare Wirkung.
    Eine Mutation der Position einer Falle ist nur dann von Bedeutung, wenn tatsächlich eine Falle (oder etwas Schädliches) in Sicht ist, die mit relativ geringer Wahrscheinlichkeit (ich würde so etwas wie 1/3 sagen) vorliegt.

Schließlich schätze ich, dass die letzten 36 Bits nicht nur dazu beitragen, dass Ratten nicht hängen bleiben, sondern auch, dass Ratten gleichmäßiger auf der Strecke verteilt werden, wodurch die genetische Vielfalt erhalten bleibt, bis ein siegreiches Genom entsteht und durch den Farbcodierungsteil dominant wird.

Weitere Arbeit

Ich muss sagen, ich finde diese kleinen Lebewesen faszinierend.
Nochmals vielen Dank an alle Mitwirkenden dieser hervorragenden Herausforderung.

Ich denke darüber nach, den Controller weiter zu schlachten, um signifikantere Daten anzuzeigen, wie die Abstammung einer erfolgreichen Ratte.

Ich würde diese Ratten auch sehr gerne in Aktion sehen, aber dieses C ++ b ** ch einer Sprache macht das Erstellen - geschweige denn das Animieren - von Bildern (unter vielen anderen Dingen) zu einer chaotischen Aufgabe.

Am Ende möchte ich zumindest eine Erklärung des Fallensystems erstellen und möglicherweise verbessern.

Controller-Hacking

Wenn jemand interessiert ist, kann ich die Änderungen, die ich an der Steuerung vorgenommen habe, veröffentlichen.
Sie sind dreckig und billig, aber sie machen den Job.

Ich bin kein GitHub-Fan, also müsste das nur ein Beitrag sein.


16
Erreichte eine Punktzahl von 208,14 bei 10.000 Spielen. Ich habe versucht, es für 1000 zu testen, aber ich habe nie bemerkt, dass ich eine zusätzliche 0 eingegeben habe, also hat es über 7 Stunden gedauert.
Feersum

LOL trotzdem danke. Im Vergleich zu meinen zwei 1000 Läufen scheint es dann, als ob ungefähr 2000 Läufe ein stabiles Ergebnis liefern könnten.

Was ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^bedeuten Sie? Den Rest kann ich mir denken, aber ich habe Probleme damit?
Mooing Duck

Ich habe darüber nachgedacht, einen separaten "Debug" -Controller zu erstellen, der jeweils eine Ratte ausführt. Jedes Mal, wenn eine neue Ratte generiert wird, wird die DNA beider Eltern und des Kindes angezeigt (über eine anpassbare Funktion). Das würde es viel einfacher machen, die Funktionsweise der Ratte zu untersuchen.
Mooing Duck

2
Das entspricht den 36 "Auf / Ab" -Indikatorbits, aber in diesen Beispielen ist die Gewinner-DNA bereits dominant geworden, sodass sie nicht sehr unterschiedlich sind.

18

Harte Gläubige - C ++ - (verbesserte Teleporter): 10.000+ für 2000 Läufe

(Dies ist eine Entwicklung des blinden Glaubens . Vielleicht möchten Sie vor dieser eine weitere Textwand erklimmen.)

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

Folge IV: Wir orientieren uns an der Startaufstellung

Ergebnisse

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

Ich wechselte zu g ++ / MinGW und 3 Threads.
Der von GNU generierte Code ist mehr als doppelt so schnell wie der von Microsoft.
Kein Wunder, was mit ihrer entsetzlichen STL-Implementierung.

Teleporter

Der Teleporter-Effekt ist stark positionsabhängig. Bisher war ich froh, einen Teleporter als immer gut (als leeren Raum gesehen) oder immer schlecht (als Mauer gesehen, so dass kein Nagetier ihn jemals nehmen würde) zu betrachten.

Dies ist ein zu grobes Modell.
Ein gegebener Teleporter kann eine Ratte vorwärts treiben, bis einige Zellen vom Ziel entfernt sind, aber sobald er dort ist, kann derselbe Teleporter die Ratte vom Brett werfen.
Ein solcher Teleporter wird höchstwahrscheinlich als passabel eingestuft (da er die Fitness schneller erhöht als beim "Gehen" zu derselben x-Position), Teil des dominanten Genoms werden und fast alle Ratten töten, die ihm als "immer sicher" vertrauen.
Da die Ratten keine Möglichkeit haben, ihre X-Position zu kennen, besteht die einzige Lösung zum Erkennen dieser tückischen Teleporter darin, auf der Grundlage der einzigen verfügbaren Kontextdaten, dh des 5x5-Farbrasters, zu entscheiden, ob sie darauf treten sollen.

Dazu habe ich 4 Arten von Farbgenen definiert:

  • Gefahrenfalle Detektor
  • leer überall auf der Strecke befahrbar
  • Blockieren Sie verboten irgendwo auf der Strecke
  • Der Strahl wird je nach Umgebung als leer oder blockiert betrachtet

Die Idee ist, einen Teleporter anhand seiner unmittelbaren 8 Nachbarn zu unterscheiden. Da die Wahrscheinlichkeit, 8 identische Nachbarn an einem bestimmten Ort zu haben, sehr gering ist, sollte dies die Identifizierung einer eindeutigen Instanz jedes Teleporters ermöglichen.

Die 8 Nachbarfarben können zu einer lokalen Signatur kombiniert werden, die für die Position im Labyrinth unveränderlich ist. Leider sind die 8 Nachbarn nur für Zellen sichtbar, die sich im inneren Quadrat des 3x3-Sichtfelds befinden, sodass die Signaturen am Rand des Sichtfelds ungenau sind.
Dies gibt uns jedoch eine konstante kontextbezogene Information in der unmittelbaren Nachbarschaft, die ausreicht, um die Wahrscheinlichkeit zu erhöhen, dass Teleporter erfolgreich navigieren.

Beam- Gene haben ein variables Feld von 2 Bits.
Für eine gegebene lokale Signatur des Teleporters gibt es eine Chance von vier, dass die Strahlzelle als unpassierbar betrachtet wird. Jeder Wert des Feldes wählt eine dieser vier Möglichkeiten aus.
Infolgedessen durchläuft eine Strahlgenmutation auf diesen 2 Bits 4 mögliche kontextbezogene Bedeutungen der Farbe.

Außerdem sind die wichtigsten Farben, die zu erraten sind, noch Wände und Fallen. Das heißt, wir sollten die Erkennung von Teleportern erst zulassen, nachdem die Ratten erfahren haben, wo sich die Mauern und Fallen befinden.

Dies geschieht, indem die lokalen Signaturen nur sparringly aktualisiert werden. Das aktuelle Kriterium für die Aktualisierung einer lokalen Signatur muss in der Nähe einer Farbe liegen, die als potenzieller Teleporter identifiziert wurde.

Die Codierung verwendet 5 Bits pro Farbgen und Gruppentypen, um die 3 niederwertigen Bits für die Codierung eines 0..7-Werts freizugeben:

  • 4 gefahr
  • 4 leer
  • 4 block
  • 4 Strahl

Jedes Strahlengen hat eine 1/4-Chance, als Block betrachtet zu werden, und eine 3/4-Chance, als leer betrachtet zu werden, sodass 4 Strahlen im Durchschnitt 1 Block und 3 leere Strahlen darstellen.

Der durchschnittliche Anteil, der durch eine zufällige Verteilung von 16 Farben dargestellt wird, ist somit:

  • 4 gefahr
  • 7 leer
  • 5 Block

Diese Mischung scheint die besten Ergebnisse zu liefern, aber ich bin noch nicht fertig damit, sie zu optimieren.

Genveränderlichkeit

Eines ist sicher: Die für die Darstellung der Gen-Typen gewählten Code-Werte sind kritisch. Das Invertieren zweier Werte kann 2000 Punkte oder mehr kosten.

Auch hier liegt der Grund außerhalb meiner Mathematik.

Ich vermute, dass die Mutationswahrscheinlichkeiten von einem Typ zu einem anderen ausgeglichen sein müssen, da die kumulativen Wahrscheinlichkeiten, wie in einer Markow-Matrix, dazu neigen, die Werte auf die Teilmenge mit den höchsten eingehenden Übergangswahrscheinlichkeiten zu beschränken.

Weg zur Rettung

Durch das Pathing wird die Anzahl der besuchten Zellen drastisch reduziert, sodass nur die Zellen getestet werden können, die am wahrscheinlichsten zum Ziel führen. So werden nicht nur häufige Sackgassen vermieden, sondern falsche Farbcodes werden auch viel häufiger früher entdeckt.
Infolgedessen wird die Konvergenzzeit stark verringert.

Dies hilft jedoch nicht beim Lösen der Karten, bei denen das Genom keine ordnungsgemäße Darstellung der Spur erzeugen kann.

Was tun mit Idioten?

Nachdem ich mir die Strecke visuell angeschaut hatte, verstand ich, warum eine Standardstrategie, die versucht, voranzukommen, auch wenn scheinbar nur Mauern davor sind, in der Tat besser ist als Zurückhalten.
"Mauern" können in Wirklichkeit Teleporter sein, die so viele unglückliche Ergebnisse liefern, dass das Genom sie als Hindernisse ansieht, auf die man niemals treten kann, aber in seltenen Fällen kann eine bestimmte Instanz dieses ungezogenen Teleporters einen positiven (oder zumindest nicht tödlichen) Effekt haben Wenn Sie es nehmen, anstatt sich zurückzuziehen, erhöhen Sie die Chancen, einen Weg zum Sieg zu finden.

Frühe Konvergenz

Mir scheint, die Mutationsrate ist ein bisschen zu niedrig (zumindest für meine Nagetiere).

Die aktuelle Einstellung von 0,01 gibt einer DNA eine Chance von 37%, den Mutationsprozess intakt zu überleben. Durch Ändern des Parameters auf 0,0227 wird diese Wahrscheinlichkeit auf etwa 10% gesenkt.

Die mysteriöse Formel lautet P 1 Bitmutation = 1-P ganzes Genom intakt 1/100 , wobei 100 die Genombitlänge ist.

Zum Beispiel für die 10% ige Wahrscheinlichkeit, P 1 - Bit - Mutation = 1-0,1 1/100 = 0,0277
für die 5% ige Wahrscheinlichkeit, P = 1-0,05 1/100 = 0,0295
Invertieren der Formel, finden wir , daß eine 37 0,01% ergeben Chancen, unverändert durch Mutation.

Ich habe den exakt gleichen Test (mit einer festgelegten Folge von Zufallssamen) mit einer Wahrscheinlichkeit von 10% wiederholt.
Auf vielen Karten wurden aus den vorherigen Fehlern (begrenzte) Erfolge. Andererseits waren enorme Bevölkerungsexplosionen geringer (was den interessanten Nebeneffekt hatte, die Berechnung erheblich zu beschleunigen).
Obwohl die sehr hohen Punktzahlen (über eine Million) seltener vorkamen, war die Anzahl der erfolgreicheren Läufe mehr als ausreichend, um dies auszugleichen.
Am Ende stieg der Mittelwert von 1400+ auf etwa 2000.

Die Einstellung von P auf 5%
ergab dagegen einen Mittelwert von etwa 600. Ich gehe davon aus, dass die Mutationsrate so hoch war, dass sich das Genom der siegreichen Ratten zu oft in weniger effiziente Varianten verwandelte.

Wie funktioniert das?

Mit den hinzugefügten Teleporterdetektoren sank die Anzahl der fehlgeschlagenen Spiele (Punktzahl <10) erheblich.
Bei einem Test mit 2000 Durchläufen gab es nur 1/3 der Fehler.
Das geometrische Mittel stieg nur von 2900 auf 3300, aber diese Zahl spiegelt die Verbesserung nicht wider.

Leere Farben werden häufig als Strahlen und Gefahren erraten (normalerweise 2 bis 5). Das Genom "benutzt" diese Farben, um Wege zu blockieren, die Ratten in Schwierigkeiten bringen würden.

Das Genom ist ziemlich gut darin, Fallen zu erraten (dh wenn Ratten das Ziel erreicht haben, werden in etwa 90% der Fälle Farben erraten, die tatsächliche Fallendetektoren darstellen).
Es werden auch die neuen Strahlencodes für Teleporter verwendet, wenn auch seltener (wahrscheinlich, weil die "tückischen" Teleporter weniger verbreitet sind als Fallen, und andere Strahlen- / Gefahrenfarben entwickeln sich, um den Weg zu den letzten Instanzen dieser Verräter zu blockieren).

Gemessen an der Anzahl der Spiele, bei denen nach 5000 Umdrehungen oder mehr ein Siegergenom entsteht, würde diese neue Rasse meiner Meinung nach erheblich von einer erhöhten Mutationsrate profitieren.


Da es eine gerade Anzahl von Fallen, leeren Wänden und Teleports gibt, benötigen Sie nur 3 Bits, um die Proportionen genau zu speichern (auch wenn Sie Fallen == Wände berücksichtigen). Haben Sie auch die Idee in Betracht gezogen / verworfen, nicht verwendete Trap-Offset-Bits für das Anti-Wallbanging zu verwenden? Da das Ziel darin besteht, nicht von den Eltern zu erben, könnte man eigentlich alle Bits im Anti-Wallbanging verwenden. Kein Grund für sie, einzigartig zu sein, würde ich nicht denken.
Mooing Duck

1
@MooingDuck Ich habe Ihre Idee getestet, Offset-Bits wiederzuverwenden, aber es ist fehlgeschlagen. Wie ich befürchtet habe, scheint die Wiederverwendung von Informationen für zwei verschiedene Zwecke nicht zu funktionieren. Angenommen, die Offset-Bits einer bestimmten Farbe werden für ein Genom benötigt, um die richtige vertikale Richtung in einem bestimmten Pfad zu bestimmen. Diese Farbe kann keine sinnvolle Falle mehr darstellen, ohne den Pfad zu zerstören, der von denselben Daten abhängt. Ich habe auch versucht, 6 Bits zu verwenden, aber wie ich befürchtete, waren auch die restlichen 4 Anti-Wall-Banger zu knapp.

1
Gut zu wissen, aber ich schlug dort zwei Ideen vor: Eine bestand darin, alle Bits zu verwenden (einige wiederzuverwenden), und die andere bestand darin, die nicht verwendeten Fallenversatzbits für Wände / leer zu verwenden. Hast du beides probiert? (Ich verstehe völlig, wenn Sie nicht versuchen möchten, müssen Sie kaum versuchen, wenn Sie nicht möchten)
Mooing Duck

1
Ich habe beide ausprobiert und beide sind gescheitert. Trap-Offsets sind wichtig, auch wenn ein Gen sie nicht verwendet, da dieses Gen immer noch zu einer Trap-Farbe mutieren kann. In diesem Fall ist der Trap-Offset wahrscheinlich zu den Kontext-Bits mutiert, die am profitabelsten sind, und hat seine Bedeutung als Offset verloren . Jetzt mutiert es wieder zum profitablen Offset-Wert und zerstört die Pfade der Ratten, die davon abhängig waren, als Kontextindikatoren. Ich glaube, ich habe den Fall einer solchen Oszillation mit meinem Grafikwerkzeug gesehen, aber es ist nicht einfach, ein klares Beispiel für dieses Problem zu zeigen.

16

ColorScorePlayer, vorläufiges Ergebnis ≈ 22

Dies ist der Bot, den Sie in der Herausforderung im GIF sehen.

Dies war unser Testbot während der gesamten Entwicklungsphase. Es verwendet das Genom, um einen Qualitätsfaktor für jede der 16 Farben zu speichern. Dann macht er den Vorwärtszug, der ihn auf die Farbe mit der besten Punktzahl bewegt (und nie weitergeht -1). Bei einem Gleichstand wird eine zufällige Bewegung zwischen den verbundenen Zellen ausgewählt.

Wir haben diesen Player in alle Controller-Sprachen portiert, sodass er als Beispiel für die Verwendung dient:

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)

Rubin

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

Der Spieler punktet ziemlich uneinheitlich. Hier sind 50 Zufallsläufe:

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

12

ColorFarSeeker, C ++ ≈ 74.7

Diese Herausforderung macht wirklich Spaß und ist einfach, wenn Sie es versuchen.

Lassen Sie sich von der langen Beschreibung nicht abschrecken.
Besuchen Sie einfach den GitHub und probieren Sie es aus ... alles wird viel klarer! :)

Der C ++ - Simulator wird aufgrund seiner Geschwindigkeit dringend empfohlen. Auch nachdem ich mein Python-Programm in C ++ übersetzt habe, ist die Python-Simulation noch nicht beendet.

Dies ist eine verbesserte Variante des ColorScorePlayer. Um die 5x5-Ansicht optimal zu nutzen, werden mithilfe einer gewichteten Funktion Schritte in 2 Schritten berücksichtigt. Wenn Sie 1 Schritt voraus gehen, erhalten Sie ein höheres Gewicht, da dies eine unmittelbarere Auswirkung auf das Überleben hat. Bewegen Sie sich 2 Schritte vor Ihnen, erhalten Sie ein geringeres Gewicht.

Versucht sich vorwärts zu bewegen, aber wenn kein sicherer Zug zu sehen ist ... dann versucht es seitwärts ... und wenn alles andere fehlschlägt, bewegt es sich nach dem Zufallsprinzip rückwärts.

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

}

Ergebnis:

Es gibt ziemlich viele Einsen ... was ein bisschen deprimierend sein kann, wenn Sie sehen, wie die Konsole eine nach der anderen ausspuckt. Wie ein Planet mit allen Notwendigkeiten für das Leben, aber ohne Anzeichen fortgeschrittener Rattenzivilisationen ...
Dann die gelegentliche Spitze. :)

Hmm ... anscheinend hatte ich Glück für meine erste Serie von Läufen mit einem geometrischen Wert von 300+. Die Punktzahlen schwanken sehr stark. Aber mit mehr Simulatorläufen ist es wahrscheinlich näher an closer 74. (Danke, dass Sie mir beim Simulieren geholfen haben und sein superschnelles Programm)

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


1
Ich habe einen geometrischen Mittelwert von 74,7 mit 1000 Spielen, gute Arbeit.
Feersum

8

Bischof - Python, vorläufige Punktzahl 1.901

Der Bischof bewegt sich immer diagonal, so dass die Hälfte des Brettes auf einer bestimmten Wanderung nicht zugänglich ist. Dies bedeutet jedoch, dass weniger potenzielle Züge codiert werden müssen, sodass jedes einzelne Genomstück einen Zug darstellen kann (der Bischof zieht sich niemals zurück). Welches Bit referenziert werden soll, wird anhand des 3x3-Rechteckblocks vor (rechts) der Probe entschieden. Der beste Zug für eine bestimmte Situation ist immer nur eine Bit-Mutation entfernt.

Dieser Bot lernt zuerst schnell, trifft dann aber häufig eine Decke, bevor er das Ziel erreicht, vermutlich dort, wo eines der folgenden zwei Probleme auftritt:

  • Zwei oder mehr Teile des Spielbretts werden auf dasselbe Bit abgebildet, erfordern jedoch unterschiedliche Bewegungen.
  • Einige Bretter sind nur mit diagonalen Zügen nicht befahrbar.

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

Trotz dieser Einschränkungen gelingt es dem Bischof in seltenen Fällen gut, wenn einzelne Exemplare jeweils mehrere Runden auf dem Brett absolvieren. Ich hatte gedacht, dass sich ein Exemplar in einer bestimmten Runde nur auf der Hälfte des Bretts bewegen kann (das entspricht nur den schwarzen Quadraten oder nur den weißen Quadraten auf einem Schachbrett). Wie Martin Büttner jedoch betonte, kann ein Teleporter eine Probe von einem schwarzen Quadrat zu einem weißen Quadrat oder umgekehrt bewegen, so dass sie auf den meisten Brettern nicht eingeschränkt werden.

(Es gibt zwei Paare übereinstimmender Teleportertypen und jeder hat eine Wahrscheinlichkeit von 0,5, dass ein Versatz eine Probe in die andere Hälfte des schwarzen und weißen Quadrats verschiebt. Die Wahrscheinlichkeit also, dass eine Tafel nur Teleporter hat, die die Probe auf einen beschränken Die Hälfte des Boards pro Runde beträgt nur 0,25.)

Die Punktzahlen zeigen, dass die gelegentlichen Siege mit langen Phasen des Unterschreitens des Ziels durchsetzt sind:

Bewertungen: 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 6 1 8 1 10 15 1 1 12544 1 2 1 1 1 3 7554 1 1 1 1 1


8

Run-Bonus-Spieler: Geometrischer Mittelwert 50,35 (5000-Game-Test)

Dieser Bot bewertet Quadrate anhand ihrer individuellen Farben, basierend auf einem 6-Bit-DNA-Abschnitt wie der Color-Score-Player, jedoch mit einem anderen Zahlensystem. Dieser Bot wurde durch den Gedanken motiviert, dass es ziemlich willkürlich ist, dass eines der Bits den Wert der Punktzahl um 32 ändert, während ein anderes dies nur um 1 tut. Es weist einem Durchlauf von n (n + 1) / 2 den Wert von zu n aufeinanderfolgende 1 Bits. Zusätzlich wird ein Zufallsmechanismus hinzugefügt, um ein Festklemmen zu vermeiden. Es wird ein zufälliger Vorwärtszug mit einer Chance von 1 zu 30 ausgeführt.

Zum Vergleich erzielte der Farb-Score-Spieler in ein paar 1000-Game-Tests 30 bis 35 Punkte. Interessanterweise lag die maximale Spielpunktzahl des Farbspielers im Bereich von 3 bis 5 Millionen, während der maximale Laufbonus nur 200.000 betrug. Der Run-Bonus profitiert vom logarithmischen Durchschnittswertungssystem, indem er eine Punktzahl ungleich Null erhält, die einheitlicher ist.

Das Ausführen von 5000 Spielen dauerte ungefähr 20 Minuten mit 6 Threads auf dem C ++ - Controller.

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

Wie lange hat der 5000 Tracks Test aus Neugier gedauert? Meine Ratten brauchen mehr als eine Stunde, um 1000 Tracks zu absolvieren. Deshalb müsste ich den Computer die ganze Nacht laufen lassen, um Ihren Testfall zu reproduzieren.

@kuroineko Die Antwort auf deine Frage war schon in meiner Antwort.
Feersum

ups, tut mir Leid. Ich werde dann Ihren Code auf meinem PC testen, um festzustellen, welche Rolle die Hardware für den Geschwindigkeitsunterschied spielt. Und vielleicht versuchen Sie es mit gcc anstelle von MSVC. Bei einigen anderen rechenintensiven Codeteilen wurde eine Leistungssteigerung von 30% gegenüber MSVC festgestellt.

Dein Code lief in etwas mehr als 20 Minuten für 1000 Tracks auf meinem i3-2100@3.1GHz mit 4 Threads. Die Punktzahl lag bei 56 . Es scheint zu bedeuten, dass mein PC 5-mal langsamer ist als Ihr PC und mein Code auf einem bestimmten Computer etwa 6-mal langsamer ist (eine bessere Punktzahl bedeutet jedoch mechanisch eine längere Rechenzeit). Da ich zu pleite bin, um einen neuen PC zu kaufen, ist es Zeit für ein bisschen Optimierung ...

8

StarPlayer | C ++ | Punktzahl: 162 (basierend auf 500 Spieldurchläufen)

Dieser Spieler versucht mit A * den besten Weg nach vorne zu finden. Es weist Gewichte wie ColorScorePlayer zu und versucht, den Weg zum rechten Rand der Ansicht zu finden. Die Implementierung ist nicht die schönste, die ich je gemacht habe, aber zumindest nicht zu langsam.

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

Musterpartituren:

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


1
In 1000 Spielen bekam ich eine Punktzahl von 133,2, nett.
Feersum

7

WallGuesser - Erzielte 113.266 Punkte in einem 1000-Spiele-Test

Codierung

Ich habe eine wirklich einfache 6-Bit / Farb-Codierung gemacht. Farbe dekodieren [n]

  • Summiere jedes n-te Bit im Genom bis zu 96
  • Wenn die Summenpunktzahl> = 4 ist, dann sagen Sie, dass dieses Quadrat gesperrt ist
  • Wenn die Gesamtpunktzahl <= 4 ist, beträgt die Endpunktzahl 2 ^ der Gesamtpunktzahl

Indem ich die Bits für eine Farbe im gesamten Genom verteile, erhöhe ich die Wahrscheinlichkeit, dass Bits von beiden Elternteilen für jede Farbe verwendet werden.

Bewegung

Ich benutze eine A * -basierte Suche (ich bin mir sicher nicht sehr effizient), um nach dem kostengünstigsten Pfad zu einem der Quadrate am rechten Rand zu suchen. Wenn eine Farbe "gesperrt" ist, wird sie bei der Suche niemals eingegeben. Wenn die Suche keinen Pfad findet, nimmt sie an, dass diese Ratte nicht reproduzierbar ist, und versucht, ihn zu beenden, indem sie einen nach links bewegt.

Reduzierung der Anzahl nicht tauglicher Ratten

Da mein Genom effektiv errät, welche Quadrate Wand- oder Rückwärtsteleporter sind, sind Ratten, die keine Vermutungen haben (keine Farben, die blockiert werden sollen), nicht sehr fit. Um zu versuchen, diese Ratten zu entfernen, wenn keine Farbe als blockiert markiert wird, wird JEDE Farbe als blockiert markiert und die Ratte bewegt sich immer eine nach links.

MACHEN

Derzeit gibt es keine Zufälligkeit im Verhalten, so dass es für Ratten leicht ist, stecken zu bleiben.

#include "./gamelogic.cpp"

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

public:
    EditablePriorityQueue(){};

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

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

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

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

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

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

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

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

            return{ path, finalScore };
        }               

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

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

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

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

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

        }
        delete current;
    }

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

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

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

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

    return minMove;
}

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

Hm, das passt nicht zu mir g++ -std=c++11 .\wallguesser.cpp -O2 -o .\wallguesser.exe. Ich bekomme viele Fehler, aber der erste ist.\wallguesser.cpp:47:19: error: 'dna_t' has no member named 'at' if (d.at(i) == true){
Martin Ender

Kein Problem, einfach zu ändern, atum []es zu beheben .
Feersum

7

Die FITTEST - Geometric Mean Score: ~ 922 (2K läuft)

Mein Ansatz ist:

  1. Finden Sie heraus, was die Art tötet und definieren Sie das gewünschte Verhalten (funktional)
  2. Gewünschtes Verhalten in Code implementieren (technisch)
  3. Gib es eine Priorität . Ist es wichtiger oder weniger wichtig als anderes gewünschtes Verhalten?
  4. Optimieren Sie den geometrischen Mittelwert, indem Sie die Parameter der Lösungen optimieren .

Ich habe über 2000 Parametersätze mit denselben 50 Samen getestet. Die vielversprechendsten Sets wurden ausgewählt und mit 250 identischen Samen bewertet. Diejenigen mit dem höchsten Rang waren der Input für die nächste Testrunde. Also habe ich es geschafft , einen genetischen Algorithmus zu erstellen, um den optimalen genetischen Algorithmus für dieses Problem zu finden, wie vom Benutzer mbomb007 vorgeschlagen .

Das gewünschte Verhalten:

  1. Die Arten sollten lernen, welche Farben sicher und welche schlecht sind.
  2. Die Art sollte sich hauptsächlich auf die Entscheidung konzentrieren, wohin sie sich bewegen soll, basierend auf den 3 Zellen direkt vor ihr. Wenn jedoch keine guten Bewegungen verfügbar sind, sollten vertikale oder Rückwärtsbewegungen in Betracht gezogen werden
  3. Die Spezies sollte auch schauen, was sich hinter den 8 Zellen um sie herum befindet, und diese Informationen bei der Entscheidungsfindung verwenden
  4. Die Arten sollten lernen , Fallen zu identifizieren .
  5. Einige Arten nehmen fälschlicherweise an, dass Wände gut sind und versuchen, sich ständig zu ihnen zu bewegen, und bleiben deshalb vor Wänden stecken. Wenn sie zu diesem Zeitpunkt die Spezies mit der höchsten Punktzahl sind, wird ihre DNA mit der falschen Annahme über die Wand bei Neugeborenen vielfach dupliziert . Nach einiger Zeit sitzen alle Arten vor Mauern fest und keiner von ihnen erreicht das Ziel, Punkte zu sammeln. Wie kann man die Idioten aufhalten?

Datenspeichermethoden:

Wir möchten, dass die Spezies Dinge lernt, sich an ihre Umgebung anpasst und die Stärksten werden. Dies funktioniert zwangsläufig nur, wenn das Lernen irgendwie gespeichert werden kann. Das Lernen wird in den 100 DNA-Bits 'gespeichert'. Es ist eine seltsame Art zu speichern, weil wir den Wert unserer DNA nicht ändern können. Wir gehen also davon aus, dass die DNA bereits Informationen über schlechte und gute Züge speichert. Wenn für eine bestimmte Art die richtigen Informationen in ihrer DNA gespeichert sind, bewegt er sich schnell vorwärts und erzeugt mit ihrer DNA viele neue Arten.

Ich fand heraus, dass der geometrische Mittelwert von der Speicherung der Informationen abhängt. Nehmen wir an, wir lesen die ersten 4 Bits der 100 Bits der DNA und möchten diese in einer ganzzahligen Variablen speichern. Wir können dies auf verschiedene Arten tun:

  1. Dezimaldatenspeicherung: Durch Verwendung der dnarangeFunktion "Eingebaut 1011" Beispiel: Aus 4 Bit wird 1x2 ^ 3 + 0x2 ^ 2 + 1x2 ^ 1 + 1x2 ^ 0 = 15. Mögliche Werte (für 4 Bits): [0, 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  2. Streifen Datenspeicherung: Mit der dnaStreakRangeFunktion (unten definiert), Beispiel: 4Bits 1011 werden 1x1 + 0x1 + 1x1+ 1x2 = 4. Mögliche Werte (für 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. Bitumendatenspeicherung: Durch Verwendung der dnaCountRangeFunktion (unten definiert), Beispiel: 4 Bit 1011 werden 1x1 + 0x1 + 1x1 + 1x1 = 3. Mögliche Werte (für 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;
}

Unterschiede zwischen den Speichermethoden sind:

  • Die dezimale Speichermethode ist anfällig für eine einzelne Bitänderung der DNA. Wenn sich der Bitwert von 1011 auf 0011 ändert, ändert sich sein Wert von 3 auf 2, was eine geringfügige Änderung darstellt.
  • Die dezimale Speichermethode ist homogen . Für jeden der möglichen Werte muss dieselbe Änderung vorgenommen werden. Die Wahrscheinlichkeit, dass Sie den Wert 15 aus einem 4-Bit-Speicherblock lesen, beträgt 1/16 = 6%. Die Streifenspeichermethode ist nicht homogen . Die Wahrscheinlichkeit, dass ein Streifen-4-Bit-Wert kleiner oder gleich 6 ist, beträgt (15-3) / 16 = 81% (alle 16 Kombinationen außer 0111,1110,111). Unten ein Bild, das die Form der Verteilung zeigt. Wie Sie am blauen Pfeil sehen können, beträgt die Wahrscheinlichkeit, dass ein 4-Bit-Streifen kleiner oder gleich 6 ist, 81%: Visualisierung der Verteilung von Dezimal-, Streifen- und Bitumenspeicherarten für 4,5 und 6 Bit lange Binärzahlen

Priorisieren Sie Lösungen.

Wenn der ColorScorePlayer zwei Vorwärtsbewegungen mit identischen Ergebnissen identifiziert hat, wird eine willkürliche Auswahl getroffen. IMHO sollten Sie niemals die Zufallsfunktion v.rng.rint()verwenden . Stattdessen sollten Sie diese Gelegenheit der gleichen Punktzahl als Haken verwenden, um Lösungen für Effekte zweiter Ordnung zu bewerten.

Der Effekt erster Ordnung hat die höchste Priorität. Wenn gleiche Werte erreicht werden, hat die Lösung mit der Priorität 2 Vorrang und so weiter. Durch Ändern der Parameter einer Lösung können Sie die Wahrscheinlichkeit des Auftretens gleicher Ergebnisse beeinflussen und auf diese Weise die Gewichtung der Lösungen mit Priorität 1 und 2 ändern.

Umsetzung des gewünschten Verhaltens

Erfahren Sie, welche Farben sicher sind:

  • 33% der 16 Farben sind schlecht und wenn die Punktzahl eines Zuges unter 63/3 liegt, ist der Zug nicht erlaubt. Daher threshold = 63/3=21ist 63 die maximale Punktzahl für 6 Bits und 33% = 1/3 (kann in der obigen Grafik nachgeschlagen werden).

Wenn keine guten Züge verfügbar sind, gehen Sie vertikal oder rückwärts:

  • Wenn keine Vorwärtsbewegungen zulässig sind, werden vertikale Bewegungen auf dieselbe Weise miteinander verglichen. Wenn auch keine vertikalen Bewegungen erlaubt sind, werden die Rückwärtsbewegungen eingestuft. Dies wird über die weightMoveVariable erreicht.

Schauen Sie, was dahinter steckt:

  • Wenn 2 oder 3 Züge identische Punkte haben, bestimmt das 3x3-Kästchen um diese Züge (über x2und y2Schleifen), welche Option (über die mainSubScoreVariable) die beste ist . Die am weitesten rechts stehende Spalte in dieser 3x3-Box führt.
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};
}

Prüfungsergebnis: 123 (2K-Läufe)

Erste 50 Ergebnisse (18 Spiele haben nur 1 Punkt erzielt):

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

Fallen identifizieren:

Ich habe die DNA der Spezies mit der höchsten Punktzahl untersucht, als ein beliebiges Spiel unter Verwendung des Speichers a bitsum4 endete (also hat die Farbpunktzahl einen Bereich von [0,4]):

  • 0 Punkte: Nach hinten teleportieren, beide Wände, 1x sicher
  • erzielte 1: Falle rückwärts (so harmlos), Teleport rückwärts, 1x sicher
  • erzielte 2: Falle vorwärts (so gefährlich), 1x sicher
  • erzielte 3: Teleport nach vorne, 5x sicher
  • 4 Punkte: Vorwärts teleportieren, 1x sicher

Daraus kann geschlossen werden, dass Mauern und Teleports eine korrekte Punktzahl erhalten. Fallen werden nicht identifiziert, da sie von der Richtung und der Farbe des Ursprungs abhängen, während die Bewertung nach der Farbe des Ziels erfolgt. Es besteht daher ein Bedarf, auch Daten zur Ursprungsfarbe zu speichern, so v(0,0). In einer idealen Welt möchten wir Informationen für 16 Farben x 8 Richtungen x 3 Bits = 384 Bits speichern.

Leider sind nur 100 Bit verfügbar, und wir können nicht alle verwenden, da wir für die oben erläuterte Lösung auch etwas Speicher benötigen. Deshalb werden wir 4 Farbbehälter herstellen:

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

und 4 Bewegungsrichtungsfächer

  • 0: vertikal oder rückwärts bewegen,
  • 1: vorwärts gehen,
  • 2: vorwärts bewegen,
  • 3: Vorwärts runter

Wenn die Dezimalpunktzahl 4 oder höher ist (100, 101, 110, 111), wird davon ausgegangen, dass dieser Zelle eine Falle zugeordnet ist. Infolgedessen wird dieser Zug nicht ausgewählt, wenn gleiche Punktzahlen auftreten. Die Identifizierung von Fallen ist also ein Effekt zweiter Ordnung, und das "Sehen, was dahinter steckt" wird eine Lösung mit dritter Priorität sein.

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

Prüfungsergebnis: 580 (2K-Läufe)

Erste 50 Ergebnisse (13 Spiele haben nur 1 Punkt erzielt):

28.044.1.189 1 2.265.670 2.275.942 3 122.769 109.183 401.366 61.643 205.949 47.563 138.680 1 107.199 85.666 31 2 29 1 89.519 22 100.908 14.794 1 3.198.300 21.601 14 3.405.278 1 1 1 2 74.167 1 71.242 97.331 201.080 1 102.944 1 71.442

Falsche Vermutungen über die Mauer werden von Trotteln oft in Neugeborene dupliziert:

Einige Arten nehmen fälschlicherweise an, dass Wände gut sind und versuchen, sich ständig zu ihnen zu bewegen, und bleiben deshalb vor Wänden stecken. Sie können auch in Endlosschleifen von Teleportern stecken bleiben. Der Effekt ist in beiden Fällen der gleiche.

Das Hauptproblem ist, dass nach einigen hundert Iterationen einige Gene sehr dominant werden . Wenn dies die „richtigen“ Gene sind, können Sie sehr hohe Punktzahlen erzielen (> 1 Million Punkte). Wenn diese falsch sind, stecken Sie fest, da Sie die Vielfalt brauchen, um die "richtigen" Gene zu finden.

Idioten kämpfen: Lösung 1: Farbumkehr

Die erste Lösung, die ich ausprobiert habe, bestand darin, einen Teil des ungenutzten Speichers zu nutzen, der immer noch sehr vielfältig ist. Nehmen wir an, Sie haben Ihrem Farbspeicher und dem Trap Finding Memory 84 Bit zugewiesen. Die verbleibenden 16 Bits werden sehr unterschiedlich sein. Wir können 2 dezimale 8 Variablen mit Werten im Intervall [0,255] füllen und sie sind homogen, was bedeutet, dass jeder Wert eine Chance von 1/256 hat. Die Variablen werden genannt inInverseund inReverse.

Wenn inInverse255 ist (eine Chance von 1/256), kehren wir die Interpretation der Farbwerte um . Die Mauer, von der der Trottel annimmt, dass sie sicher ist, wird eine hohe Punktzahl, eine niedrige Punktzahl und wird daher zu einem schlechten Zug. Der Nachteil ist, dass dies auch die 'Rechte'-Gene beeinflusst, so dass wir weniger sehr hohe Punktzahlen haben werden. Darüber hinaus muss sich diese inInverseArt vermehren und ihre Kinder erhalten auch Teile der dominanten DNA. Das Wichtigste ist, dass es die Vielfalt zurückbringt.

Wenn inReverse255 entspricht (eine Chance von 1/256), kehren wir die Reihenfolge der Speicherpositionen der Farbwerte um . Also bevor die Farbe 0 in den Bits 0-3 gespeichert wurde. Jetzt wird Farbe 15 in dieser Position gespeichert. Der Unterschied zum inInverseAnsatz besteht darin, dass der inReverseWille die bisher geleistete Arbeit rückgängig macht. Wir sind wieder am ersten Platz. Wir haben eine Spezies geschaffen, die ähnliche Gene wie zu Beginn des Spiels hat (außer, dass die Falle das Gedächtnis findet).

Durch die Optimierung wird geprüft, ob es sinnvoll ist, die inInverseund inReversegleichzeitig zu verwenden. Nach der Optimierung wurde festgestellt, dass der Score nicht erhöht wurde. Das Problem ist, dass wir eine vielfältigere Genpopulation haben, dies wirkt sich jedoch auch auf die „richtige DNA“ aus. Wir brauchen eine andere Lösung.

Morons Fighting: Lösung 2: Hash-Code

Die Art hat 15 mögliche Startpositionen und derzeit besteht eine zu große Chance, dass er genau den gleichen Weg einschlägt, wenn er an derselben Startposition startet. Wenn er ein Idiot ist, der Wände liebt, wird er immer wieder an derselben Wand hängen bleiben. Wenn er es glücklicherweise geschafft hat, eine weit vor ihm liegende Mauer zu erreichen, wird er mit seinen falschen Annahmen beginnen, den DNA-Pool zu dominieren. Was wir brauchen, ist, dass sein Nachwuchs einen etwas anderen Weg einschlägt (für ihn ist es sowieso zu spät) und nicht an der Wand weit vorne hängen bleibt, sondern an einer Wand in der Nähe . Dies kann durch die Einführung eines Hashcodes erreicht werden .

Ein Hashcode sollte den Zweck haben, die aktuelle Position auf dem Board eindeutig zu identifizieren und zu kennzeichnen . Der Zweck ist nicht herauszufinden, wie die (x, y) Position ist, sondern die Fragen zu beantworten, die meine Vorfahren zuvor an diesem Ort gestellt haben.

Nehmen wir an, Sie hätten das komplette Board vor sich und würden ein JPG von jeder 5 x 5-Zelle möglich machen. Am Ende erhalten Sie (53-5) x (15-5) = 380 Bilder. Geben wir diesen Bildern Nummern von 1 bis 380. Unser Hashcode sollte als solche ID angesehen werden, mit dem Unterschied, dass er nicht von 1 bis 330 läuft, sondern fehlende IDS hat, z. B. 563, 3424, 9424, 21245 usw.

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

Die Primzahlen 17und 31sind dort, um zu verhindern, dass die am Anfang der Schleife hinzugefügten Informationen verschwinden. Später erfahren Sie mehr darüber, wie Sie unseren Hashcode in den Rest des Programms integrieren können.

Ersetzen wir den Subscoring-Mechanismus "Look What's Beyond" durch einen anderen Subscoring-Mechanismus. Wenn zwei oder drei Zellen die gleichen Hauptpunkte haben, besteht eine 50% ige Chance, dass die oberste Zelle ausgewählt wird, eine 50% ige Chance, dass die unterste Zelle ausgewählt wird und eine 0% ige Chance, dass die mittlere Zelle ausgewählt wird. Die Chance wird nicht durch den Zufallsgenerator bestimmt, sondern durch Bits aus dem Speicher , da auf diese Weise sichergestellt wird, dass in der gleichen Situation die gleiche Auswahl getroffen wird.

In einer idealen Welt (in der wir unendlich viel Speicher haben) würden wir einen eindeutigen Hashcode für unsere aktuelle Situation berechnen , z. B. 25881, und zum Speicherort 25881 gehen und dort lesen, ob wir die obere oder untere Zelle auswählen sollten (wenn vorhanden) ist eine gleiche Punktzahl). Auf diese Weise wären wir in genau der gleichen Situation (wenn wir z. B. zum zweiten Mal über das Brett fahren und an der gleichen Position beginnen) und treffen die gleichen Entscheidungen. Da wir keinen unendlichen Speicher haben, wenden wir ein Modulo der Größe des verfügbaren Speichers auf den Hashcode an . Der aktuelle Hashcode ist in dem Sinne gut, dass die Verteilung nach der Modulo-Operation homogen ist.

Wenn der Nachwuchs das gleiche Board mit leicht veränderter DNA reist, trifft er in den meisten Fällen (> 99%) genau die gleiche Entscheidung. Aber je weiter er kommt, desto größer wird die Chance, dass sein Weg sich von seinen Vorfahren unterscheidet. Die Chance, dass er an dieser Wand hängen bleibt, ist also gering. Während er mit seinem Vorfahren an der gleichen Wand hängen bleibt, ist er relativ groß, aber das ist nicht so schlimm, da er nicht viel Nachwuchs zeugt. Ohne den Hashcode-Ansatz ist die Wahrscheinlichkeit, an der nahen und der entfernten Wand hängen zu bleiben, nahezu gleich

Optimierung

Nach der Optimierung wurde festgestellt, dass die Trap-Identifikationstabelle nicht benötigt wird und 2 Bit pro Farbe ausreichen. Der Rest des Speichers 100-2x16 = 68 Bits wird zum Speichern des Hash-Codes verwendet. Es scheint, dass der Hash-Code-Mechanismus in der Lage ist, Fallen zu vermeiden.

Ich habe für 15 Parameter optimiert. Dieser Code enthielt den besten Satz optimierter Parameter (bis jetzt):

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

Prüfungsergebnis: 922 (2K-Läufe)

Erste 50 Ergebnisse (9 Spiele haben nur 1 Punkt erzielt):

112.747 3 1 1.876.965 8 57 214.921 218.707 2.512.937 114.389 336.941 1 6.915 2 219.471 74.289 31 116 133.162 1 5 633.066 166.473 515.204 1 86.744 17.60 2 190.697 1 6 122 126.399 399.045 1 190.697 1 6 169.119 4.990 77.432 23.172.168 1 169.168

Dies ist mein erstes C ++ - Programm. Ich habe, wie die meisten von euch, Hintergrundwissen in der Gnomenanalyse. Ich möchte mich bei den Organisatoren bedanken, da es mir wirklich Spaß gemacht hat, daran zu arbeiten.

Wenn Sie Feedback haben, hinterlassen Sie bitte unten einen Kommentar. Entschuldigung für die langen Texte.


Ich finde deine Fallenanalyse ziemlich interessant.

Haben Sie eine andere Hash-Funktion ausprobiert, zum Beispiel die 25 Farbwerte, die als 12,5 16-Bit-Wörter angesehen werden, zu xoren und ein Modulo zu nehmen? Ich bin nicht überzeugt, dass die Primzahlkongruenz eine bessere Homogenität ergibt, aber ich bin kein großer Mathematiker.

Haben Sie auch darüber nachgedacht, einen Pfad-Algorithmus hinzuzufügen? Es scheint ein enormer Verbesserungsfaktor zu sein, unabhängig vom Genom, da es die Bewegungen auf diejenigen beschränkt, die die Fähigkeit des Genoms nur auf Wegen testen, die viel wahrscheinlicher zu einer Gewinnposition führen.

kuroi, danke für dein feedback. Ich habe das xoring nicht ausprobiert, da ich mit binären Operationen in c ++ nicht so vertraut bin. Ich nehme an, Sie meinen 12,5 8-Bit-Wörter? Verwenden Sie Xoring?
Ruut

Sie können sich meinen Code für "hartgläubige" ansehen, um zu sehen, welche Art von Hash-Funktion ich verwende. Grundsätzlich überspringe ich die Off-Track-Zellen und betrachte die On-Track-Farben als Teile eines 16-Bit-Wortes hoher und niedriger Ordnung. Alle diese Wörter werden mit XORs in einem Register akkumuliert, das dann durch die Hash-Tabellengröße dividiert wird. Solange der Hash-Max-Wert (65535) viel höher als die Tabellengröße (<100) ist, hat das Modulo eine gute Ausbreitungskraft. Ich habe es auf einer großen Anzahl zufällig generierter Gitter getestet und es scheint eine gute Homogenität zu haben.

6

Pathfinder, C ++, vorläufiges Ergebnis 35.8504 (50 Runden)

Eine komplette Überholung! Ich habe meinen Algorithmus auf C ++ portiert und ein wenig optimiert, aber die Punktzahl ist immer noch nicht sehr hoch, wahrscheinlich, weil die Ratten ihre Köpfe immer wieder gegen Wände schlagen. Ich bin es leid zu versuchen, dies zu verbessern, also lasse ich es einfach für den Moment sein.


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

Erläuterung

Die allgemeine Idee ist, jede Farbe als eine Falle oder nicht zu klassifizieren, dann Richtungen zu Fallen und Gewichte zu Nicht-Fallen zuzuweisen und zu versuchen, dem Pfad mit minimalem Gewicht zum rechten Rand des Sichtgitters zu folgen.

In den ersten 80 Bits des Genoms wird jede Farbe mit 5 Bits klassifiziert abcde. Wenn ab = 01, ist die Farbe eine Falle und cdecodiert ihre Richtung (acht Möglichkeiten). Wenn ab ≠ 01, die Farbe ist nicht eine Falle, und sein Gewicht ist a + b + 2*(c + d + e).

Als nächstes initialisieren wir ein 3x7-Gitter, das das Sichtfeld der Ratte auf der rechten Seite darstellt und mit "unbekannten" Farben aufgefüllt ist. Die Bits 80-84 codieren das Gewicht der unbekannten Zellen ähnlich wie die Nicht-Trap-Farben, und die Bits 85-89 codieren ein gemeinsames Gewicht für die Traps. Wir füllen das Gitter mit den Gewichten, berechnen die kürzesten Pfade und fügen den Zellen direkt über und unter der Ratte ein zusätzliches Gewicht (in den Bits 90-95 codiert) hinzu, um ein Ausweichen zu verhindern. Die Bits 95-99 codieren ein Zielgewicht. Wenn das Mindestgewicht eines Pfades darunter liegt, steckt die Ratte wahrscheinlich irgendwo fest und bewegt sich nach dem Zufallsprinzip (aber nie rückwärts). Andernfalls folgt er dem Pfad mit dem Mindestgewicht. Mit einer geringen Wahrscheinlichkeit, die von dem das Ausweichen verhindernden Gewicht abhängt, wählt die Ratte stattdessen den Weg des zweiten bis minimalen Gewichts. Dies soll verhindern, dass Sie an Wänden hängen bleiben (aber es scheint momentan nicht sehr gut zu funktionieren).


Lief deine Implementierung auf meinem Computer. Hat ein paar Stunden gedauert. Es wird eine durchschnittliche Punktzahl von 7.848433940863856 Punkten. pastebin.com/d50GcwnK
Jakube

@ Jakube Vielen Dank! Das ist viel schlimmer als erwartet, aber jetzt, wo ich mir den Code noch einmal ansehe, sehe ich mehrere Bugs und andere Kuriositäten. Ich werde versuchen, dies später nach C ++ zu portieren, damit ich es selbst analysieren kann.
Zgarb

5

LookAheadPlayer C ++ .90 89.904

Mein ursprünglicher Gedanke war, nach 4 Bits zu suchen, die der gesuchten Farbe entsprechen, und die folgenden wenigen Bits als Punktzahl zu verwenden. Dies stellte sich als schreckliche Idee heraus, wahrscheinlich aufgrund von Mutationen.

Ich dachte über Möglichkeiten zum Schutz vor Mutationen und Überkreuzungen nach und erinnerte mich an die Arbeit, die ich an der Entschlüsselung von QR-Codes geleistet habe. In QR-Codes werden die Daten in Blöcke aufgeteilt und gestreift, um zu vermeiden, dass Fehler zu viel von einem bestimmten Teil der Daten zerstören.

Daher schneide ich wie der ColorScorePlayer die DNA in 16 Stücke und verwende diese als gegebene Punktzahl. Die Punkte sind jedoch gestreift, so dass die einzelnen Bits jedes Punktes nicht benachbart sind. Ich summiere dann die Punktzahl der aktuell möglichen Züge und der nächsten möglichen Züge und wähle den besten Zug aus.

Hinweis: Dies wurde auf MinGW codiert / getestet. Es würde nicht mit Optimierungen oder mit Multithreading kompilieren. Ich habe weder eine aktuelle Linux-Installation noch Visual Studio zur Hand, um einen Compiler zu verwenden, auf dem diese funktionieren. Ich werde es morgen früh schnell testen, aber lass es mich wissen, wenn du auf irgendwelche Probleme stößt.

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

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

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

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

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

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

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

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

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

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

    return direction_lut[best];
}

5

SlowAndSteady C ++ (9,7 Punkte)

Wir können uns nicht darauf verlassen, Teile des Genoms als Zahlen zu interpretieren, da ein einzelner Bit-Flip abhängig von seiner Position radikal unterschiedliche Auswirkungen haben kann. Deshalb benutze ich einfach 16 6-Bit-Segmente und bewerte sie nach der Anzahl von 1s. Anfänglich 111111war es gut und 000000war es schlecht, und während es auf lange Sicht (sobald das Genom vollständig entwickelt ist) in der Anfangskonfiguration der DNA keine Rolle spielt, haben die meisten Segmente 2-4, also wechselte ich zum 9 - (#1 - 3)^2Scoring ermöglicht viel mehr Bewegungsfreiheit in den ersten Runden und eine schnellere Evolution.

Im Moment schaue ich nur die 7 nächsten Nachbarn an, füge der Farbbewertung eine Richtungskorrektur hinzu und bewege mich nach dem Zufallsprinzip in eine der höchsten Richtungen.

Obwohl die Punktzahl selbst nicht sehr hoch ist, erreichen meine Tiere die Ziellinie und erreichen in 3/4 der Fälle eine Punktzahl von> 1.

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

Und eine Probe auf 100 Brettern

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

Geometrischer Mittelwert: 9.76557


Wird die von Ihnen angegebene Punktzahl für ein Board mit der Standardmutationsrate oder Ihrem angepassten Wert angegeben?
Trichoplax

"Meine Viecher erreichen die Ziellinie und erzielen in 3/4 der Fälle> 1" Ich wünschte, die Scoring-Metrik belohnt dies
Sparr

5

WeightChooser | C # | Scores: 220.8262 in 1520 Spielen

Berechnet das Gewicht für den möglichen nächsten Zug (blau) basierend auf dem Durchschnittsgewicht der möglichen verfolgten Züge (gelb)

using ppcggacscontroller;
using System.Linq;
using System;

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

    const int dnaBits = 4;

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

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

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

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

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

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

        return w / i;
    }
}

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

5

RATS IN ACTION (keine Antwort, sondern ein grafisches Tool für C ++ - Bots)

Seit Beginn dieser Herausforderung hatte ich Schwierigkeiten herauszufinden, was die Ratten wirklich auf der Strecke zu suchen hatten.
Am Ende habe ich den Controller gehackt und ein Side-Tool geschrieben, um eine grafische Darstellung einer Spur zu erhalten.
Schließlich habe ich noch ein bisschen gehackt und eine Visualisierung der möglichen Pfade der DNA einer bestimmten Ratte hinzugefügt.

Die Karte ist sehr unübersichtlich und gewöhnungsbedürftig, aber ich fand es sehr hilfreich zu verstehen, wie meine Bots funktionierten.

Hier ist ein Beispiel:

Beispielspur

Sie müssen wahrscheinlich zoomen, um etwas zu sehen. Hier ist nur die erste Hälfte:

halber Track (kein Wortspiel vorgesehen)

Schauen wir uns zunächst die Wege der Ratte an. Für jeden möglichen Startort gibt es einen Pfad (normalerweise 15, manchmal etwas weniger). Normalerweise verschmelzen sie und führen im Idealfall zu einem einzigen Siegesort.

Die Pfade werden durch große gerade Pfeile dargestellt. Die Farbe beschreibt das Ergebnis:

  • grün: gewinnen
  • gelb: Endlosschleife
  • braun: Wand knallt
  • rot: unglücklicher Unfall

In diesem Beispiel haben wir 12 gewinnende Startpositionen, eine führt zu einer Endlosschleife und zwei zu einem grausamen Tod (wie es scheint wird in eine Falle teleportiert).

Die Pfaddiskontinuitäten sind auf Teleportationen zurückzuführen, die Sie mit den entsprechenden gekrümmten Pfeilen verfolgen können.

Nun zu den farbigen Symbolen. Sie repräsentieren die Bedeutung der 16 Farben (die grauen repräsentieren, was eine Ratte sieht).

  • Wand: quadratisch
  • Teleporter: 4 verzweigter Stern
  • Fallenmelder: kleines Oktogon

leere Farben sind ... na ja ... leer.

Teleporter haben ausgehende Pfeile, die auf ihr Ziel zeigen.

Fallendetektoren haben auch Pfeile, die auf die Falle hinweisen, die als roter Kreis dargestellt ist.
In einem von 9 Fällen befindet sich die Falle in derselben Zelle wie ihr Detektor. In diesem Fall sehen Sie das kleine Oktogon über dem roten Kreis.

Dies ist in diesem Beispiel für die hellgelbe Falle der Fall.
Sie können auch die lila Fallenmelder sehen, die auf die angegebene Falle zeigen.

Beachten Sie, dass der rote Kreis einer Falle manchmal unter einer Wand versteckt ist. Beide sind tödlich, daher ist das Ergebnis bei der Teleportation dasselbe.
Beachten Sie auch, dass sich möglicherweise eine Falle auf einem Teleporter befindet. In diesem Fall hat der Teleporter Vorrang (dh die Ratte wird teleportiert, bevor sie in die Falle fällt, wodurch die Falle neutralisiert wird).

Schließlich stellen die grauen Symbole dar, was meine Ratten sehen (dh die Bedeutung ihrer Genomattribute für die Farben).

  • Wand: quadratisch
  • Fallenmelder: Oktogon
  • Falle: X

Grundsätzlich werden alle auf einem grauen Quadrat sitzenden Zellen von der Ratte als Wände betrachtet.
Große X stellen Zellen dar, die als Fallen betrachtet werden, wobei die entsprechenden Oktogone den Detektor angeben, der sie gemeldet hat.

In diesem Beispiel sind beide Wände als solche gekennzeichnet, ebenso wie die blassgelbe Falle (was auf eine tödliche Zelle hinweist, sodass es richtig ist, sie als Wand darzustellen).
Der Mauve-Trap-Detektor wurde als solcher identifiziert (er befindet sich auf einem grauen Oktogon), aber die Trap-Position ist falsch (Sie können sehen, dass einige rote Kreise keine Kreuze darunter haben).

Von 4 Teleportern gelten 2 als Wände (türkis und braun) und 2 als leere Zellen (rötlich und gelblich).

Einige leere Zellen werden als Fallendetektoren oder Wände betrachtet. Wenn man genau hinschaut, kann man sehen, dass diese "fehlerhaften Detektoren" tatsächlich den Zutritt zu Zellen verbieten, die die Ratte in Schwierigkeiten bringen würden, und obwohl sie nicht den tatsächlichen Farben entsprechen, haben sie einen bestimmten Zweck.

Der Code

Nun, es ist ein Durcheinander, aber es funktioniert ziemlich gut.

Vom Code des Spielers aus habe ich nur eine Schnittstelle hinzugefügt: eine Trace-Funktion, mit der die Bedeutung einer bestimmten DNA gemeldet wird. In meinem Fall habe ich 3 Typen verwendet (Wand-, Fallen- und Leermelder), aber Sie können grundsätzlich alles ausgeben, was mit Farbe zu tun hat (oder gar nichts, wenn Sie keine genombezogenen Grafiken wünschen).

Ich habe den Controller gehackt, um eine riesige Zeichenfolge zu generieren, in der die Beschreibung von Spur und Farben mit einem "Probelauf" der Ratten-DNA von allen möglichen Stellen aus verglichen wird.

Das bedeutet, dass die Ergebnisse nur dann wirklich aussagekräftig sind, wenn der Bot keine Zufallswerte verwendet. Andernfalls stellen die angezeigten Pfade nur ein mögliches Ergebnis dar.

Zuletzt werden alle diese Spuren in eine große Textdatei geschrieben, die später von einem PHP-Dienstprogramm gelesen wird, das die grafische Ausgabe erzeugt.

In der aktuellen Version mache ich jedes Mal einen Schnappschuss, wenn eine Ratte stirbt, nachdem sie eine neue maximale Fitness erreicht hat (die die fortschreitende Verfeinerung des Genoms ziemlich gut zeigt, ohne dass zu viele Schnappschüsse erforderlich sind), und einen letzten Schnappschuss am Ende des Spiels (das zeigt sich) die erfolgreichste DNA).

Bei Interesse kann ich den Code veröffentlichen.

Dies funktioniert natürlich nur für C ++ - Bots, und Sie müssen eine Trace-Funktion schreiben und möglicherweise den PHP-Code ändern, wenn Sie einige genomspezifische Daten anzeigen möchten (in meinem Fall die grauen Zahlen).
Auch ohne DNA-spezifische Informationen können Sie die von Ihrer DNA verfolgten Pfade auf einer bestimmten Karte mit sehr geringem Aufwand anzeigen.

Warum eine Zwischenausgabe?

Erstens hat C ++ keine anständige tragbare Grafikbibliothek, von der man sprechen könnte, insbesondere wenn MSVC verwendet wird. Selbst wenn Win32-Builds normalerweise verfügbar sind, entstehen sie oft aus einem nachträglichen Grund, und die Anzahl der benötigten externen Bibliotheken, Pakete und anderen unixartigen Feinheiten macht das Schreiben einer schnellen und einfachen grafischen Anwendung zu einem fürchterlichen Schmerz in einem Körperteil, der durch Anstand verhindert wird mich von der Benennung.

Ich dachte darüber nach, Qt zu verwenden (die einzige Umgebung, die die Entwicklung von portablen GUIs / Grafiken in C ++ zu einer einfachen und sogar angenehmen Aufgabe macht, IMHO - wahrscheinlich, weil sie ein Messaging-System à la Objective C hinzufügt , das C ++ schmerzlich fehlt und einen unglaublichen Job zur Begrenzung des Arbeitsspeichers leistet Management auf das Nötigste), aber das sah nach einem Overkill für die anstehende Aufgabe aus (und jeder, der den Code verwenden möchte, müsste das großartige SDK installieren - der Aufwand lohnt sich wohl kaum).

Selbst wenn man eine tragbare Bibliothek voraussetzt, muss man nicht über die erforderliche Geschwindigkeit sprechen (eine Sekunde oder so, um ein Bild zu erstellen, ist weitgehend ausreichend), und C ++ ist aufgrund seiner sprichwörtlichen Steifheit und inhärenten Unordnung sicherlich nicht das beste Werkzeug für diese Aufgabe.

Darüber hinaus bietet die Ausgabe von Zwischentexten viel Flexibilität. Sobald die Daten vorhanden sind, können Sie sie für andere Zwecke verwenden (zum Beispiel zum Analysieren der Leistung der Bots).

Warum PHP?

Ich finde die Sprache sehr einfach und anpassungsfähig, sehr praktisch für das Prototyping. Ich habe es zu meiner Lieblingssprache für Code-Herausforderungen gemacht, die keine extremen Leistungen erfordern.
Es ist eine schreckliche Sprache zum Golfen, aber Golf war sowieso nie meine Sache.

Ich nehme an, Python oder Ruby wären für den gleichen Zweck genauso angenehm, aber ich hatte nie die Gelegenheit, ernsthafte Arbeit mit ihnen zu leisten, und ich habe in letzter Zeit an Websites gearbeitet, also PHP.

Auch wenn Sie die Sprache nicht kennen, sollte es nicht allzu schwierig sein, den Code an Ihre Bedürfnisse anzupassen. Vergiss nur nicht die $s vor den Variablen, genau wie die guten alten Basic-Tage :).


1
Würden Sie bitte Ihr Werkzeug teilen? Ich sehe weder Code noch einen Link in Ihrer Antwort.
Franky

5

SkyWalker - Python - erzielt in 50 Spielen weniger als 231 Punkte

Also erst Code und dann ein paar Erklärungen. Ich hoffe, beim Kopieren ist nichts kaputt gegangen.

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

Einige Erklärung

Meiner Meinung nach besteht der Hauptunterschied darin, dass ich nicht jede Farbe codiere. Stattdessen versuche ich, die Anzahl der Farben zu speichern, die wichtig sind. Meiner Meinung nach sind diese Farben die Fallen, Wände und Teleporter. Die Probe muss nicht die Farbe einer guten Zelle kennen. Daher ist mein Genom folgendermaßen strukturiert.

  • 2 x 8 Bits für Traps, die ersten 4 Bits sind die Farbnummer, die anderen 4 sind der Offset
  • 2 x 4 Bits für Wände, nur die Farbe
  • 4 x 7 Bits für Teleporter, wieder 4 Bits für die Farbe, 3 um gut oder schlecht zu entscheiden

Dadurch werden insgesamt 52 Bits verwendet. Ich benutze jedoch nur das erste Bit der 3 Teleporterentscheider (ich überprüfe, ob die Zahl 3 größer ist). Daher könnten die anderen 2 gelöscht werden, was mich bei 44 Bit belässt.

Bei jedem Zug überprüfe ich jedes Feld meiner Sicht, ob es eine der schlechten Farben ist (+ das Feld außerhalb des Bretts -1), und füge es einer Liste von Feldern hinzu, in die sich die Probe nicht bewegen möchte. Im Falle einer Überfüllung füge ich das Feld hinzu, das sich auf dem gespeicherten Versatz für diese Überfüllungsfarbe befindet.

Basierend auf der Liste dieser fehlerhaften Felder wird der nächste Zug berechnet. Die Reihenfolge der bevorzugten Felder ist:

  1. nach vorne
  2. hoch oder runter
  3. rückwärts rauf oder runter
  4. rückwärts

Wenn zwei Felder einer Kategorie zutreffen, wird eines zufällig ausgewählt.

Ergebnisse

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

Gedanken

  • Ich habe keine Ahnung, ob ich mit den 50 Läufen Glück hatte oder ob meine Strategie wirklich etwas Weisheit enthält.

  • Meine Läufe scheinen sich nie zu verbessern und erzielen Super-Highscores, aber sie finden zumindest ein paar Mal das Ziel

  • Eine kleine Zufälligkeit ist gut, um nicht in einer Falle hängen zu bleiben, manche kurz vor dem Ende des Rennens

  • Ich denke, dass nicht spezielle Farben niemals schlecht sind. Instanzen von ihnen können jedoch schlecht sein, wenn sie sich auf dem Offset einer Falle befinden. Daher macht es keinen Sinn, eine Farbe als schlecht zu bezeichnen, wenn sie keine Falle, Wand oder schlechten Teleporter enthält.

  • Mauern sind die größten Feinde

Verbesserungen

Erstens ist ein C ++ - Port erforderlich, um mehr Tests durchzuführen und ein aussagekräftigeres Ergebnis zu erzielen, obwohl ich vermissen werde, dass die schwarzen Quadrate immer näher an das Ziel heranrücken.

Eines der Hauptprobleme besteht darin, dass sich schlechte Zellen (oder solche, die die Probe für schlecht hält) vor der Ratte leicht im Kreis auf und ab bewegen. Dies könnte gestoppt oder reduziert werden, indem in diesen Fällen 2 Züge nach vorne geschaut werden und verhindert wird, dass es sich auf ein Feld bewegt, auf dem es sich einfach wieder zurück bewegt.

Oft dauert es einige Zeit, bis eine Ratte mit guten Genen das Ziel erreicht und beginnt, diese Gene zu verbreiten. Vielleicht brauche ich eine Strategie, um die Vielfalt in diesen Fällen zu erhöhen.

Da Teleporter schwer zu kalkulieren sind, sollte ich vielleicht die Bevölkerung in diejenigen aufteilen, die riskant sind und immer gute Teleporter nehmen, und diejenigen, die besorgter sind und sie nur nehmen, wenn es keine andere Wahl gibt.

Ich sollte die zweite Hälfte meines Genoms irgendwie benutzen.


Ich versuche auch, Farben zu speichern, aber am Ende ist klar, dass es nicht funktioniert, weil Sie doppelte Farben erhalten. Wenn beispielsweise self.bit_chunk(16, 4)und self.bit_chunk(20, 4)haben beide den Wert 0010Sie effektiv nur dann gespeichert haben Informationen über eine der beiden Fallen.
Ruut

Ich musste einer Zeile Einrückungen hinzufügen, damit diese ausgeführt werden konnten - ich vermute, dass diese beim Kopieren und Einfügen verloren gingen. Ich habe es jetzt auch hier zu Ihrem Code hinzugefügt.
Trichoplax

Für alle anderen wollen dies auszuführen: Es läuft in Python 2 und kann in Python 3 durch Ändern der einzelnen Vorkommen ausgeführt werden itervalueszu values.
Trichoplax

Ich habe die folgenden Ergebnisse erhalten: [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] Geometrisches Mittel 122.9220309940335
Trichoplax

Es sieht so aus, als müssten wir mehr als 50 Spiele absolvieren, um ein zuverlässiges Ergebnis zu erzielen.
Trichoplax

3

Python, NeighborsOfNeighbors, Score = 259.84395 über 100 Spiele

Dies ist eine Variation von ColorScorePlayer. Alle 6 Bits speichert einen Qualitätsfaktor für ein Quadrat. Wenn der Bot einen Zug macht, zählt er jedes der drei vorderen Felder - diagonal nach oben, vorwärts und diagonal nach unten. Die Punktzahl ist die Qualität des Quadrats plus die Hälfte der durchschnittlichen Qualität der nächsten 3 Quadrate. Dies gibt dem Bot einen gewissen Blick nach vorne, ohne die Qualität des ersten Quadrats zu beeinträchtigen. Der Algorithmus ähnelt LookAheadPlayer, den ich vor dem Schreiben dieser Lösung nicht gesehen habe.

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)

In einer Zeile fehlte ein Einzug. Ich denke, es ist beim Einfügen verloren gegangen. Ich habe es hinzugefügt.
Trichoplax

Unter Python 3 wurde der Vergleich von None bei der Berechnung von max (Scores) beanstandet. Also habe ich else Nonein else 0der vorherigen Zeile umgestellt , um Ihre Punktzahl zu berechnen. Hoffentlich bleibt Ihre Logik unverändert (ich habe hier auf SE keine Änderungen an Ihrem Code vorgenommen, abgesehen vom Hinzufügen des verlorenen Einzugs).
Trichoplax

Ich habe in Python 3 die folgenden Ergebnisse für diese Antwort erhalten: [1, 13085, 360102, 1, 73713, 1, 189, 1, 1, 193613, 34, 195718, 199, 8, 1, 1, 60006, 66453, 2, 2, 53, 425206, 1, 4, 1, 1, 16, 153556, 1, 18134, 35655, 1, 4211684, 2, 1, 26451, 8, 1, 724635, 69242, 38469, 796553, 111340, 1, 25, 40017, 76064, 66478, 209365, 3925393]
Trichoplax

Ein geometrischer Mittelwert von 428,3750848244933
Trichoplax

2

ROUS (Nagetier ungewöhnlicher Größe), Java, Score = 0

Dadurch wird die Umgebung durchwühlt, um zu entscheiden, wohin sie gehen soll. Da der Java-Controller nicht funktioniert, habe ich keine Punkte dafür. Dies wird nur sehr weit kommen, wenn es ein paar Teleporter findet, die ihm helfen.Dies kann dazu führen, dass der Controller von Zeit zu Zeit abstürzt. Dies ist wahrscheinlich auf die Tatsache zurückzuführen, dass es sich bei der natürlichen Umgebung um den Feuersumpf handelt.

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

public class ROUS extends Player{

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

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

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

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

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

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

1
Der Java-Controller arbeitet jetzt.
Martin Ender

3
Zuerst dachte ich, Sie würdigen das alte Russland, aber wie es scheint, war es Rob Reiner.

Die Mindestpunktzahl ist 1
Trichoplax

@trichoplax ... den Controller zum Absturz bringen ...
TheNumberOne

Oh ich verstehe - also passiert das oft genug, dass du das Ende eines Laufs nicht erreichen kannst?
Trichoplax

2

Grauer Lookahead (C ++, ~ 1,35)

Diesem geht es im Durchschnitt nicht sehr gut, aber in seltenen Fällen funktioniert es hervorragend. Leider werden wir im geometrischen Durchschnitt (1,35) und nicht im Maximalwert (20077) bewertet.

Dieser Algorithmus verwendet lediglich 4-Bit-Graucodes, um die Punktzahl jeder Farbe irgendwo zwischen -2 und 2 (mit einer Neigung in Richtung des Bereichs [-1..1]) abzubilden, und berechnet die Punktzahl der Kacheln und der nächsten Züge jeder Bewegung . Es wird auch ein 2-Bit-Gray-Code verwendet, um den Multiplikator für die Kachel selbst sowie den Vorspannungsfaktor für die Bewegung nach rechts zu bestimmen. (Gray-Codes sind aufgrund von Mutationen viel weniger anfällig für große Sprünge, obwohl sie für die Überkreuzung zwischen zwei Codepunkten eigentlich keinen Gefallen tun ...)

Es macht auch absolut nichts, zu versuchen, mit Fallen speziell umzugehen, und ich vermute, dass dies der Untergang sein könnte (obwohl ich dem Controller keine Instrumente hinzugefügt habe, um diese Theorie zu testen).

Für jeden möglichen Zug wird eine Punktzahl ermittelt, und unter allen Zügen mit der höchsten Punktzahl wird nach dem Zufallsprinzip ausgewählt.

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

Bei meinem letzten Lauf habe ich Punkte bekommen: 1 1 1 1 1 1 1 46 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 20077 1 1 1 2 1 1 1 1 1

Ich wünschte, ich könnte mehr von den 20077s und weniger von den 1s bekommen. :)


1
Die Verwendung von Gray-Code ist eine Grayt-Idee! ;)
matovitch

1
+1 für Gray-Codes. Ein vollständig mutationsresistentes Genom kann die Vielfalt jedoch erheblich beeinträchtigen. Und übrigens ist eine Punktzahl von 20.000 nicht einmal annähernd das Maximum, das Sie erreichen können. Wenn eine Ratte die Fähigkeit entwickelt, die Strecke von einem beliebigen Startpunkt aus zu rennen, wird sie praktisch unsterblich und erzielt eine enorme Fitness-Punktzahl. Sein Genom dominiert schnell und führt zu einer Population von bis zu 50.000 Ratten und einigen Millionen.

2

C ++, TripleScore, Punktzahl: 100 ~ 400

Erstens variiert meine Punktzahl über mehrere Läufe stark (hauptsächlich aufgrund der Anzahl der Einsen).

Der Kern berechnet die Punktzahl aus 5 Richtungen: hoch, runter, vorwärts hoch, vorwärts und vorwärts runter. Zuerst wird die Punktzahl von Auf und Ab berechnet, dann werden die Ergebnisse mit dem Wert des Verbleibens an Ort und Stelle verglichen. Wenn es besser ist, an Ort und Stelle zu bleiben, als sich nach oben oder unten zu bewegen, werden diese Richtungen nicht gewählt (also muss es vorwärts gehen). Dies dient dazu, ein Springen (hoch, runter, hoch, runter, ...) zwischen 2 Punkten zu verhindern.

Jetzt werden die 3 anderen Richtungen gewertet: Vorwärts nach oben, geradeaus und vorwärts nach unten. Aus allen untersuchten Richtungen werden die mit der höchsten Punktzahl festgehalten und 1 davon wird zufällig ausgewählt.

Eine Richtung bewerten: TripleScore berechnet den Punktestand einer Bewegung anhand von 3 Unterpunkten:

  • Die Punktzahl der Farbe des Ziels (hängt von der DNA ab, wie im colorScorePlayer)
  • Die Punktzahl für die Zukunft (hängt von der DNA ab)
  • Die maximale Punktzahl für einen Vorwärtszug vom Ziel aus (multipliziert mit einem Faktor, der in der DNA gespeichert ist)

Wie bei anderen Antworten hängt die Punktzahl stark von der Anzahl der zurückgegebenen 1-Punkte ab.

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

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

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

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

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

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

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

2

Ruby - ProbabilisticScorePlayer

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

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

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

Diese hochgradig nicht deterministische Ratte berechnet die Wahrscheinlichkeit, durch ihre Nachbarschaft auf ein Feld zu gelangen. Die ersten 16 Slots im Genom repräsentieren die 16 Farben. 1 in einem Slot bedeutet, dass die Farbe gut ist, 0 bedeutet schlecht. Die nächsten 16 gelten gleichermaßen für das Feld vor Ihrem Ziel und so weiter.

Der Hauptvorteil des probabilistischen Ansatzes besteht darin, dass es fast unmöglich ist, lange hinter einer Mauer zu stecken. Der Nachteil ist, dass Sie so gut wie nie eine perfekte Ratte bekommen.


+1 für Originalität. Was für eine Punktzahl hast du bekommen?

Noch nie getestet ...
MegaTom

Haben Sie vergessen, ceinen Anfangswert anzugeben? Es scheint nicht definiert zu sein, wenn Sie es in der ersten verwenden if.
Martin Ender

@ Martinbüttner ja das habe ich vergessen. Ich werde es jetzt reparieren.
MegaTom

Ich kenne Ruby nicht so gut, aber Ihr Code läuft nicht unter Ruby2.1.5. coordsist keine Liste, die Sie &&anstelle einer andvergessenen Klammer verwenden, und selbst nachdem Sie all dies behoben haben, beschränken Sie die RNG-Werte nicht, sodass Sie eine leere Richtung erhalten. Ist dieser Pseudocode oder etwas, das mit einer Art Ruby-Dialekt ausgeführt werden soll?

2

Java, RunningStar, Score = 1817.050970291959 über 1000 Spiele

Dieser Bot verwendet die Farbcodierung von Run-Bonus mit StarPlayer der Technik von .

Update: Java Controller behoben.

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

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

public class RunningStar extends Player{

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

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

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

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

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

    private class Path implements Comparable<Path>{

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

        private Path(){}

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

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

        public Point head() {
            return head;
        }

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

            Path path = (Path) o;

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

            return true;
        }

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

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

        }

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

2

LeapForward, Python 2

Nicht besonders bahnbrechend, aber es ist mein einziger Versuch, der gut funktioniert hat.

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)

Grundsätzlich codiert es vier Farben (jeweils 4 Bits), um dies im Genom zu vermeiden. Es wird dann zu einer Farbe weitergeleitet, die nicht in dieser Liste enthalten ist. Wenn alle Farben schlecht sind, springt es immer noch ins Unbekannte.


Hätte es wahrscheinlich "RedQueen" nennen sollen :)
plannapus

1

Java - IAmARobotPlayer - Punktzahl 3.7

Ich habe gerade diese Roboterratte zum Vergleich mit einem anderen (bisher nicht sehr interessanten) Programm erstellt. Es schneidet insgesamt nicht gut ab, aber wenn es irgendwo abschneidet, werden viele Ratten auftauchen. Die Idee ist, dass nur die drei Zellen davor betrachtet werden, jede Zelle ist gut oder schlecht. Dies ergibt eine Binärzahl. Dann wird es diese Nummer in seinem Genom nachschlagen, die drei aufeinanderfolgenden Bits nehmen, sie auch zu einer Nummer konvertieren und die Aktion ausführen, die unter dieser Nummer gespeichert ist. Es verhält sich also immer gleich, wenn es auf die gleiche Situation stößt.

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

    }
}

Ergebnis:

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

1

Vorsichtige Exemplare - C ++ - erzielt ungefähr 2030 über 200 Läufe

Dabei wird der Farbanteil (16 x 4 Bit) der von Blind Faith codierten DNA verwendet, der Rest (36 Bit) der DNA bleibt jedoch vollständig ungenutzt.

Die Kodierung für eine Farbe ist entweder:

  • 10XX - für sichere Plätze;
  • 11XX - für tödliche Quadrate; und
  • 0000 bis 0111 - für die 8 Typen von Überfüllungsquadraten.

Wobei X nicht verwendete Bits angibt. Vorausgesetzt, dass nur 2 von 16 Farben Überfüllungen sind, die alle 4 ihrer Bits verwenden (und nur wenn die Überfüllung versetzt ist, was 8-von-9-mal der Fall ist), gibt es normalerweise 64 nicht verwendete Bits - Die Theorie besagt, dass Mutationen, die sich auf eines dieser nicht verwendeten Bits auswirken, das Genom nicht zerstören. Die Stabilität ist besser als bei anderen ausgefallenen Lösungen, die diese verbleibenden Bits verwenden können.

Die Proben verwenden dies dann, um eine sichere Route in einem 7x7-Raster zu planen, das auf sich selbst zentriert ist (die 5x5-Linien ermöglichen eine Sicht plus 1 Quadrat auf jeder Seite, um versetzte Fallen zu ermöglichen).

Ich habe anfänglich damit begonnen, einige Überprüfungen durchzuführen, um sicherzustellen, dass die Tatsache, dass die Farbe, auf der die Probe momentan steht, nicht tödlich ist, mit dem Genom übereinstimmt, und fehlerhafte Farben als Quadrate der UNSURE-Sicherheit (und ihrer benachbarten Quadrate) gekennzeichnet - dies trug jedoch erheblich dazu bei Komplikation für wenig bis gar keinen Gewinn im Vergleich zum Markieren dieser Quadrate als SICHER und Töten einiger zusätzlicher Exemplare. Ich werde darauf zurückkommen, wenn ich Zeit habe.

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

Sample Scores:

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

Maximale Punktzahl während des Tests: 8.150.817 gespeicherte Proben.


Jetzt hast du es geschafft ... Ich wollte das Pfading für später speichern, aber ich konnte deine vorsichtigen Nagetiere nicht unangefochten lassen :) Wie es scheint, funktioniert das Pfading mit einer effizienteren Codierung noch besser. Auch Ihre Idee, den Pfadbereich auf 7x7 zu erweitern, scheint vielversprechend. Ich werde sehen, ob ich das nutzen kann.

Ich mache derzeit 2000 Läufe dafür ... nach den ersten 900 scheint sich der Mittelwert bei 600 zu bewegen, was ziemlich weit von 2000 entfernt ist nur ein Zufall?
Martin Ender
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.