Formic Functions - Ameisenkönigin des Hügelwettbewerbs


104

Live schauen | Aktive Antworten | Neue Antwort hinzufügen | Chatraum | Quellcode | Bestenliste

Neue Turniere, wann immer nötig. Neue Spieler und neue Updates sind herzlich willkommen.

Königinameise auf einer Tanzfläche mit ändernden farbigen Fliesen

Kein eigentliches Filmmaterial.

Jeder Spieler beginnt mit einer Ameise - einer Königin, die Nahrung sammelt. Jedes Lebensmittelstück kann aufbewahrt oder zur Herstellung eines Arbeiters verwendet werden. Die Arbeiter sammeln auch Lebensmittel, um sie der Königin zurückzubringen.

16 Spieler treten in einer Arena an. Der Gewinner ist die Königin, die nach 30.000 Runden das meiste Essen in der Hand hat. Der Haken ist, dass die Ameisen nur kommunizieren können, indem sie die Farben der Arenaquadrate ändern, die auch von konkurrierenden Ameisen geändert werden können ...

Das Spiel anschauen

Dies ist ein JavaScript-Wettbewerb, bei dem Sie das Spiel live in Ihrem Browser verfolgen können, indem Sie auf den folgenden Link klicken.

Klicken Sie hier, um das Spiel live zu verfolgen

Vielen Dank an Helka Homba für die ursprünglichen Stack Snippet King of the Hill-Wettbewerbe, Red vs. Blue - Pixel Team Battlebots und Block Building Bot Flocks , die die Idee eines Webbrowsers mit KotH hatten und den Code für diesen Wettbewerb umfassend informierten.

Vielen Dank auch für all die Rückmeldungen und Tests von den wunderbaren Leuten in der Sandbox und im Chat.

Bestenliste

Bild der Top-Plätze der Bestenliste

(Klicken Sie auf das Bild, um die vollständige Bestenliste und die Erklärung zu den gemeinsamen Plätzen anzuzeigen. Aus Platzgründen werden hier nur einige wenige Spieler angezeigt.)

Diese Rangliste wird auf dem Spieler basiert , wie sie am Sonntag 2 waren nd September 2018.

Screenshots

Einige Bilder, wie die Arena gegen Ende eines Spiels aussieht. Klicken Sie auf die Bilder, um sie in voller Größe anzuzeigen.

Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena Bild der Arena

Um eine Vorstellung davon zu bekommen, was in der Arena passiert und wie sich all diese Muster bilden, können Sie das Spiel starten und mit der Maus über die Arena fahren, um die Ameisen bei der Arbeit zu sehen. Siehe auch die faszinierenden Erklärungen in den Antworten.

Die Arena

Die Arena ist ein ringförmiges Gitter aus quadratischen Zellen. Es hat eine Breite von 2500 und eine Höhe von 1000. Alle Zellen beginnen mit Farbe 1.

Anfangs enthalten genau 0,1% der Zellen Nahrung. Die 2500 Lebensmittelstücke werden gleichmäßig zufällig verteilt. Während des Spiels werden keine neuen Lebensmittel eingeführt.

Die Königinnen werden nach dem Zufallsprinzip auf leere Zellen gelegt, ohne die Garantie, dass sie nicht nebeneinander liegen (obwohl dies sehr unwahrscheinlich ist).

Ameisen Fähigkeiten

  • Sehvermögen: Jede Ameise sieht die 9 Zellen in ihrer Nachbarschaft 3 mal 3. Es hat keine Kenntnis von anderen Ameisen außerhalb dieser Nachbarschaft. Es sieht den Inhalt jeder der 9 Zellen (andere Ameisen und Nahrung) und auch die Farbe jeder Zelle .
  • Keine Erinnerung: Jede Ameise trifft ihre Entscheidungen basierend auf dem, was sie sieht - sie erinnert sich nicht an das, was sie in der vorherigen Runde getan hat, und hat keine Möglichkeit, einen anderen Zustand als die Farben der Arenazellen zu speichern.
  • Keine Orientierung: Eine Ameise weiß nicht, wo sie ist oder in welche Richtung sie schaut - sie hat kein Konzept von Norden. Die Nachbarschaft 3 x 3 wird in einer zufällig gedrehten Ausrichtung angezeigt, die sich bei jeder Umdrehung ändert, sodass sie nicht einmal in einer geraden Linie laufen kann, es sei denn, sie verfügt über Farben, die sie leiten. (Wenn Sie in jeder Runde den gleichen Zug machen, erhalten Sie eher einen zufälligen Schritt als eine gerade Linie.)
  • Verschieben, Markieren und Produzieren von Arbeitskräften: Siehe Ausgabe unten.
  • Unsterblichkeit: Dies sind Hochlandameisen, die nicht sterben können. Sie können konkurrierende Ameisen verwirren, indem Sie die Farben um sie herum ändern oder sie daran hindern, sich zu bewegen, indem Sie sie mit 8 eigenen Ameisen umgeben. Ansonsten können sie jedoch nicht beschädigt werden.
  • Tragen von Lebensmitteln: Ein Arbeiter kann bis zu 1 Lebensmittelstück tragen. Eine Königin kann eine beliebige Menge an Essen tragen.
  • Übergabe von Essen: Wenn ein Arbeiter an eine Königin angrenzt (in eine der 8 Richtungen), wird das Essen automatisch auf eine der folgenden Arten übergeben:
    • Ein beladener Arbeiter neben seiner eigenen Königin übergibt seine Nahrung an seine Königin.
    • Ein unbeladener Arbeiter neben einer feindlichen Königin stiehlt 1 Stück Lebensmittel, falls vorhanden.

Ein Arbeiter kann nicht von einem Arbeiter stehlen, und eine Königin kann nicht von einer Königin stehlen. Auch kann ein Arbeiter keine Nahrung von seiner eigenen Königin nehmen, und eine Königin kann nicht von einem feindlichen Arbeiter stehlen.

Beachten Sie, dass Ameisen nacheinander wechseln und die Nahrungsübertragung am Ende der einzelnen Runde jeder Ameise erfolgt und keine Runde einnimmt. Es passiert unabhängig davon, ob sich ein Arbeiter neben eine Königin oder eine Königin neben einen Arbeiter bewegt, und es passiert immer noch, wenn beide beteiligten Ameisen für ihren Zug stillstehen.

Codierung

Geben Sie einen Funktionskörper an

Jede Ameise wird von einer Ameisenfunktion gesteuert. In jeder Runde wird die Ameisenfunktion des Spielers für jede Ameise separat aufgerufen (nicht nur einmal pro Spieler, sondern einmal für die Dame und einmal für jeden Arbeiter, den der Spieler kontrolliert). In jeder Runde erhält die Ameisenfunktion ihre Eingabe und gibt einen Zug für diese bestimmte Ameise zurück.

Wenn Sie eine Antwort mit einem Codeblock veröffentlichen, der den Hauptteil einer JavaScript-Funktion enthält, wird dieser automatisch in den Controller aufgenommen (aktualisieren Sie einfach die Controllerseite). Der Name des Spielers bildet den Titel der Antwort in der Form # PlayerName(die in den Controllertabellen auf maximal 40 Zeichen gekürzt wird).

Kein Staat, keine Zeit, kein Zufall

Eine Funktion darf nicht auf globale Variablen zugreifen und keinen Status zwischen den Runden speichern. Es können integrierte Funktionen verwendet werden, bei denen kein Status gespeichert wird. Zum Beispiel ist die Verwendung von Math.abs()in Ordnung, Date.getTime()darf aber nicht verwendet werden.

Eine Ant-Funktion kann nur einen Pseudozufallszahlengenerator verwenden, den sie selbst bereitstellt und der keinen Zustand speichert. Zum Beispiel kann es die Farben / Lebensmittel / Ameisen verwenden, die in jeder Runde als Samen sichtbar sind. Math.random()ist ausdrücklich untersagt, da wie bei fast allen Pseudozufallszahlengeneratoren der Zustand gespeichert wird, um zur nächsten Zahl in der Folge überzugehen.

Eine einfache Zufallsstrategie ist aufgrund der zufälligen Ausrichtung der Eingabe immer noch möglich. Eine Ameise, die immer die gleiche Richtung wählt, führt einen zufälligen Lauf durch und nicht einen geraden Pfad. In den Beispielantworten finden Sie einfache Methoden zur Verwendung dieser Zufälligkeit und zur Vermeidung dieser Zufälligkeit .

Eine Ameisenfunktion darf weitere Funktionen in ihrem Körper enthalten. In den vorhandenen Antworten finden Sie Beispiele, wie dies nützlich sein kann.

Console.log

Sie können sich beim Testen eines neuen Herausforderers an der Konsole anmelden, aber sobald Sie hier eine Antwort gepostet haben, hat der Spieler keinen Zugriff mehr darauf console.log. Der Versuch, ihn zu verwenden, führt zu einem Fehler und einer Disqualifikation, bis er bearbeitet wird. Dies sollte helfen, Leaderboard-Turniere schnell zu halten und gleichzeitig das Einfügen von Debugging-Code in den neuen Challenger-Textbereich zu ermöglichen.

Ein- und Ausgabe

Eingang

Die Ausrichtung der Eingabe wird für jede Ameise und für jede Runde zufällig gewählt. Die Eingabe wird um 0, 90, 180 oder 270 Grad gedreht, jedoch niemals reflektiert.

Die Zellen sind in englischer Lesereihenfolge nummeriert:

0 1 2
3 4 5
6 7 8

Die Ant-Funktion erhält ein Array mit dem Namen view, das ein Objekt für jede der 9 sichtbaren Zellen enthält. Jedes Objekt hat Folgendes:

color: a number from 1 to 8
food: 0 or 1
ant: null if there is no ant on that cell, or otherwise an ant object

Wenn eine Zelle eine Ameise enthält, hat das Ameisenobjekt Folgendes:

food: 0 or more (maximum 1 for a worker)
type: 1 to 4 for a worker, or 5 for a queen
friend: true or false

Die Ameise kann ihre eigenen Details bestimmen, indem sie sich die Ameise in der zentralen Zelle ansieht view[4].ant. Zum Beispiel view[4].ant.typeist 5 für eine Königin oder eine Zahl von 1 bis 4 für einen Arbeiter (der seinen Typ angibt).

Ausgabe

Die Ausgabe wird als Objekt zurückgegeben, das die auszuführende Aktion darstellt. Dies kann Folgendes haben:

cell: a number from 0 to 8 (mandatory)
color: a number from 1 to 8 (optional)
type: a number from 1 to 4 (optional)

Wenn colorund typeweggelassen werden oder Null, cellwird die Zelle angegeben, in die verschoben werden soll.

Wenn colornicht Null ist, wird die angegebene Zelle auf diese Farbe gesetzt.

Wenn typeungleich Null ist, wird in der angegebenen Zelle eine Arbeiterameise dieses Typs erstellt. Nur eine Königin kann einen neuen Arbeiter erschaffen, und nur, wenn sie Essen hat, da dies ein Stück Essen pro Arbeiter kostet.

Beispielausgaben:

{cell:0}: move to cell 0
{cell:4}: move to cell 4 (that is, do nothing, as 4 is the central cell)
{cell:4, color:8}: set own cell to color 8
{cell:6, type:1}: create a type 1 worker on cell 6
{cell:6, color:1}: set cell 6 to color 1
{cell:6, color:0}: equivalent to just `{cell:6}` - move rather than set color
{cell:6, type:0}: equivalent to just `{cell:6}` - move rather than create worker
{cell:6, color:0, type:0}: move to cell 6 - color 0 and type 0 are ignored

Ungültige Ausgaben:

{cell:9}: cell must be from 0 to 8
{cell:0, color:9}: color must be from 1 to 8
{cell:0, type:5}: type must be from 1 to 4 (cannot create a new queen)
{cell:4, type:1}: cannot create a worker on a non-empty cell
{cell:0, color:1, type:1}: cannot set color and create worker in the same turn

Eine Ameise, die sich auf eine Zelle mit Lebensmitteln bewegt, nimmt das Lebensmittel automatisch auf.

Arbeitnehmertyp

Jeder Arbeiter hat einen Typ , eine Zahl von 1 bis 4. Dies hat für den Kontrolleur keine Bedeutung und ist Sache des Spielers. Eine Königin könnte alle ihre Arbeiter als Typ 1 produzieren und ihnen das gleiche Verhalten geben, oder sie könnte mehrere Arbeitertypen mit unterschiedlichem Verhalten produzieren, vielleicht Typ 1 als Häcksler und Typ 2 als Wächter.

Die Worker-Typennummer wird von Ihnen beim Erstellen eines Workers vergeben und kann danach nicht mehr geändert werden. Verwenden Sie es nach Belieben.

Reihenfolge ändern

Ameisen wechseln sich in einer festgelegten Reihenfolge ab. Zu Beginn eines Spiels wird den Königinnen eine zufällige Reihenfolge zugewiesen, die sich für den Rest des Spiels nicht ändert. Wenn eine Dame einen Arbeiter erstellt, wird dieser Arbeiter in die Reihenfolge vor seiner Dame eingefügt. Dies bedeutet, dass sich alle anderen Ameisen aller Spieler genau einmal bewegen, bevor der neue Arbeiter seinen ersten Zug macht.

Begrenzung der Anzahl der Spieler

Offensichtlich kann eine unbegrenzte Anzahl von Spielern nicht in die Arena passen. Da es mittlerweile mehr als 16 Antworten gibt, werden in jedem Spiel 16 zufällig ausgewählte Antworten angezeigt. Die durchschnittliche Leistung über viele Spiele ergibt eine Rangliste mit allen Spielern, ohne jemals mehr als 16 in einem einzigen Spiel zu haben.

Zeitlimit pro Runde

Jedes Mal, wenn die Funktion ant aufgerufen wird, sollte sie innerhalb von 15 Millisekunden zurückkehren. Da das Zeitlimit aufgrund von Schwankungen außerhalb der Kontrolle der Ameisenfunktion überschritten werden kann, wird ein Durchschnitt berechnet. Wenn der Durchschnitt zu irgendeinem Zeitpunkt über 15 Millisekunden liegt und die Gesamtzeit, die diese bestimmte Ameisenfunktion für alle bisherigen Anrufe benötigt, mehr als 10 Sekunden beträgt, wird der betreffende Spieler disqualifiziert.

Disqualifikation

Dies bedeutet, dass der Spieler nicht zum Gewinnen berechtigt ist und seine Ameisenfunktion während dieses Spiels nicht erneut aufgerufen wird. Sie werden auch nicht in weiteren Spielen enthalten sein. Wenn ein Spieler während eines Ranglistenspiels auf dem Turniercomputer disqualifiziert wird, wird er von allen zukünftigen Ranglistenspielen ausgeschlossen, bis er bearbeitet wird.

Ein Spieler wird für eine der folgenden Bedingungen für eine seiner Ameisen (Königin oder Arbeiterin) disqualifiziert:

  • Zeitüberschreitung wie beschrieben (gemittelt über 10 Sekunden).
  • Rückgabe eines ungültigen Zuges wie unter Ausgabe beschrieben.
  • Die Zelle, in die verschoben werden soll, enthält eine Ameise.
  • Die Zelle, in die sich das Essen bewegen soll, und die Ameise sind bereits beladene Arbeiter.
  • Die Zelle, in der ein Arbeiter produziert werden soll, ist nicht leer (enthält Nahrung oder eine Ameise).
  • Ein Arbeiter versucht, einen Arbeiter zu produzieren.

Es mag hart erscheinen, sich für ungültige Züge zu disqualifizieren, anstatt dies einfach als keinen Zug zu interpretieren. Ich glaube jedoch, dass die Durchsetzung korrekter Implementierungen mit der Zeit zu interessanteren Strategien führen wird. Dies ist keine zusätzliche Herausforderung, daher wird ein eindeutiger Grund angezeigt, wenn ein Spieler disqualifiziert wird, mit den spezifischen Eingaben und Ausgaben, um die Festlegung des Codes zu erleichtern.

Mehrfachnennungen und Bearbeitung

Sie können mehrere Antworten geben, sofern sie sich nicht mit den anderen messen. Vorausgesetzt, jede Antwort zielt ausschließlich auf ihren eigenen Sieg ab, ist es Ihnen gestattet, Ihre Strategie so anzupassen, dass Sie Schwachstellen in bestimmten anderen Strategien ausnutzen, einschließlich der Änderung der Farbe der Zellen, um sie zu verwirren oder zu manipulieren. Bedenken Sie, dass die Wahrscheinlichkeit, einen bestimmten Spieler in einem bestimmten Spiel zu treffen, abnimmt, je mehr Antworten eingehen.

Sie können Ihre Antworten auch jederzeit bearbeiten. Es liegt an Ihnen, ob Sie eine neue Antwort veröffentlichen oder eine vorhandene bearbeiten. Vorausgesetzt, das Spiel ist nicht mit vielen nahezu identischen Variationen überflutet, sollte es kein Problem geben.

Wenn Sie die Antwort einer anderen Person ändern, denken Sie bitte daran, die Antwort einer anderen Person mit einem Link zu Ihrer Antwort zu versehen.

Wertung

Am Ende eines jeden Spiels ist die Punktzahl eines Spielers die Anzahl der anderen Spieler, die von ihrer Königin weniger Lebensmittel bei sich tragen lassen. Von Arbeitern mitgeführte Lebensmittel werden nicht gezählt. Diese Punktzahl wird der Rangliste hinzugefügt, die in der Reihenfolge der durchschnittlichen Punktzahl pro Spiel angezeigt wird.

Gemeinsame Plätze zeigen an, dass die Reihenfolge der Spieler zwischen 6 Teilmengen der bisher gespielten Spiele noch nicht konsistent ist. Die Liste der Spiele ist in 6 Untergruppen unterteilt, da dies die Mindestanzahl ist, die eine Wahrscheinlichkeit von weniger als 5% ergibt, dass einem bestimmten Spielerpaar unterschiedliche Plätze in der falschen Reihenfolge zugewiesen werden.

Plaudern

Um den Kommentarbereich hier übersichtlich zu halten, nutzen Sie bitte den speziellen Chat-Raum für Fragen und Diskussionen. Kommentare zu diesem Beitrag werden wahrscheinlich nach einer Weile gelöscht, wohingegen Nachrichten im Chatraum dauerhaft gespeichert werden.

Um Sie wissen zu lassen, bin ich eher geneigt, Antworten zu unterstützen, die eine klare und interessante Erklärung der Funktionsweise des Codes enthalten.


2
@DestructibleLemon Um diese Kommentare durchzulesen, habe ich im Chatroom geantwortet
Trichoplax


7
Hey, ich hab was gemacht ! Sie werden es vielleicht interessant finden, da es von dieser Herausforderung inspiriert ist und eine Implementierung des Formic Functions-Tests enthält .
Dave

2
@ Dave Ihr Controller ist unglaublich schnell :) - aber lassen Sie mich erwähnen, dass sich seine Wertung von der des Originals zu unterscheiden scheint, wenn Königinnen am Ende eines Spiels für Essen gebunden sind. Die Punktzahl sollte die Anzahl der anderen Teilnehmer sein, deren Königinnen (streng) weniger Essen halten. Wenn zum Beispiel drei Spieler am Ende 0 Essen haben, sollten sie alle für dieses Spiel null Punkte erzielen, nicht drei.
GNiklasch

2
@GNiklasch danke; Fest. Ich sehe auch, dass deine Ameise jetzt das Spiel dominiert. Beeindruckend!
Dave

Antworten:


20

Forensische Ameisen

Alle meine Antworten haben die gleichen einfachen Hilfsfunktionen. Suchen Sie nach "Hier beginnt die übergeordnete Logik", um den für diese Antwort spezifischen Code anzuzeigen.

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var PARTNER = 1;
var SENTINEL = 2;

var COL_DANCING1 = 8;
var COL_DANCING2 = 7;
var SAFE_COLOURS = random_colour_band(colours_excluding([WHITE, COL_DANCING1]));

function pass_time() {
  // Wait patiently for the blockage to go away by setting
  // random cell colours (unless we're near the sentinel)
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && friend_at(i) === SENTINEL) {
      return null;
    }
  }
  return {cell: 0, color: SAFE_COLOURS.next()};
}

function move_sentinel(me, buddies) {
  // Our job is to be a sentinel showing when the queen has wrapped around.
  // We are created first, so will move first.
  // We won't find any food.

  if(!buddies[QUEEN] && !buddies[PARTNER]) {
    // No ongoing dance; make sure our state is good for when they arrive
    return try_all_angles([
      {cell: CENTRE, color: WHITE},
      (rot) => ({cell: rot[1], color: COL_DANCING2}),
      (rot) => ((view[rot[0]].color === COL_DANCING1)
        ? {cell: rot[0], color: SAFE_COLOURS.next()}
        : null)
    ]);
  }

  // Dance when queen passes
  var danceStage = view[CENTRE].color;

  if(danceStage === WHITE) {
    // Dance has not begun yet, but queen & partner are nearby
    return try_all_angles((rot) => {
      if(friend_at(rot[5]) === QUEEN && friend_at(rot[8]) === PARTNER) {
        return {cell: CENTRE, color: COL_DANCING1};
      }
    });
  }

  if(danceStage === COL_DANCING1) {
    if(buddies[PARTNER]) {
      return null; // Wait for partner to see us
    }
    // Partner saw us @8 and moved down, queen followed.
    // We must also move down (will end up on a COL_DANCING2)
    return try_all_angles((rot) =>
      ((friend_at(rot[8]) === QUEEN) ? {cell: rot[7]} : null));
  }

  // Move towards queen counter-clockwise when she's diagonally connected
  return try_all_angles((rot) =>
    ((friend_at(rot[2]) === QUEEN) ? {cell: rot[1]} : null));
}

function move_partner(me, buddies) {
  // Our job is to travel with the queen and keep her oriented.
  // We are created second, so move after the sentinel.
  // Any food we find will immediately go to the queen, since
  // we are adjacent at all times.

  // Queen will be N of us; orient ourselves
  var rot = identify_rotation((rot) => friend_at(rot[1]) === QUEEN);

  if(!rot) {
    // Queen is lagging or lost;
    return null;
  }

  var danceStage = view[rot[0]].color;
  if(
    friend_at(rot[0]) === SENTINEL &&
    (danceStage === COL_DANCING1 || danceStage === COL_DANCING2)
  ) {
    // Dance down (queen will follow)
    return {cell: rot[7]};
  }

  if(view[rot[0]].ant) {
    // Queen is blocked
    return null;
  }

  // Lead queen if both can move
  return {cell: rot[3]};
}

function move_queen(me, buddies) {
  // Our job is to travel over the entire level collecting food.
  // We move last.

  if(buddies[PARTNER]) {
    // Partner will be S or SW of us; follow if they are ahead
    return try_all_angles((rot) =>
      (friend_at(rot[6]) === PARTNER) ? {cell: rot[3]} : null);
  }

  var rot = identify_rotation((rot) => friend_at(rot[3]) === SENTINEL);
  if(rot && view[rot[0]].color >= 7) {
    // Dance down (follow partner)
    return {cell: rot[7]};
  }

  // We're on our own, or the buddy strategy failed. Start again.

  rot = identify_rotation((rot) => friend_at(rot[5]) === SENTINEL);
  if(rot && me.food >= 1) {
    // Already have a sentinel; just need a partner
    return best_of([
      {cell: rot[7], type: PARTNER},
      {cell: rot[6], type: PARTNER},
    ]);
  } else if(me.food >= 2) {
    // Create sentinel first so that we'll know to create the partner next.
    // (ensure the sentinel is created on a white cell so that it won't
    // think it's dancing)
    return try_all_angles(
      (rot) => ((view[rot[5]].color === WHITE)
        ? {cell: rot[5], type: SENTINEL} : null),
      (rot) => ({cell: rot[5], color: WHITE})
    );
  }

  // Not able to start yet; fall back to lone behaviour:
  // Random-walk until we find or make a buddy
  return best_of([
    grab_nearby_food,
    fast_diagonal.bind(null, SAFE_COLOURS),
    go_anywhere
  ]);
}

return play_safe([move_agent([
  PARTNER, move_partner,
  SENTINEL, move_sentinel,
  QUEEN, move_queen,
]), pass_time]);

Die forensischen Ameisen verfolgen einen wissenschaftlichen Ansatz, um das Raster zu fegen. Nach einem anfänglichen Rätsel um Nahrung werden 2 Arbeiterameisen erschaffen. Die Rollen sind:

Königin

Die Königin wird sich mit dem Partner zusammenschließen, um mit Lichtgeschwindigkeit in einer geraden Linie zu reisen. Keiner von beiden wird auseinander gehen, um Essen zu holen. Sie greifen nur nach dem, worüber sie stolpern.

Partner

Der Partner bewegt sich mit der Königin und hält ihren Blick in die gleiche Richtung. Da sich beide Ameisen in jeder Runde um 1 Feld bewegen können, können sie in einer geraden Linie bleiben, ohne Zeit mit dem Bemalen des Bodens zu verschwenden.

Wenn der Partner jemals etwas zu essen findet, geht er sofort zur Königin, da sie jederzeit in der Nähe ist.

Wächter

Die wichtigste Ameise. Dieser bleibt gesetzt, bis die Königin und der Partner ihn erreichen, fordert sie dann auf, 2 Pixel weiterzuschieben, und bewegt sich selbst um 2 Pixel. Dies führt dazu, dass die Dame und der Partner das gesamte Board nach und nach überstreichen (also ungefähr 30 Pixel davon). Es bewegt sich nur, wenn die Königin in der Nähe ist, sodass alle gefundenen Lebensmittel sofort ausgehändigt werden.

In seiner Freizeit gehört es zu den Hobbys des Wächters, den Boden zufällig zu streichen, um hoffentlich alle Konkurrenten durcheinander zu bringen.


Diese arbeiten sehr konstant; zwischen ihnen können sie 2 Zellen pro Frame fegen, was über 30000 Frames 60000 Zellen bedeutet, und mit 0,1%, die Nahrung enthalten, bedeutet dies eine endgültige Punktzahl von 60, die sie ziemlich konsequent erreichen.


(und hier ist der andere, den ich während der Beta-Phase vorbereitet habe! - das bin ich jetzt; ich bin sicher, dass diese ziemlich schnell geschlagen werden!)
Dave

Die Punktzahl ist bemerkenswert konsistent. Es wird interessant sein zu sehen, wie sich das auswirkt, wenn sich die Arena mit mehr Konkurrenten füllt ...
trichoplax

Ich frage mich, ob das Hinzufügen eines weiteren Partners auf der anderen Seite der Königin helfen würde.
K Zhang

1
@ KZhang Ich denke, es würde (theoretisch würde es die Punktzahl auf ~ 90 erhöhen), aber es ist schwer genug, die beiden zusammen mit dem Sentinel synchron zu halten! Der Tanz "Move Everybody Up 2 Pixel" brauchte eine Weile, um es herauszufinden. Wenn ich auf 3 Pixel gehe, wird einer der Tricks blockiert, auf die ich mich verlassen habe (der Sentinel bereitet die Leerzeichen um sich herum im Voraus vor).
Dave

Spitze der ersten Rangliste ...
Trichoplax

18

Minenräumer 6.4

const DEBUG = false;
const ADD = (a,b) => a + b;
var toReturn;
var me = view[4].ant;
me.me = true; // for basedOn to know
var food = me.food;
var type = me.type;
var isQueen = type == 5;

// raw directions
const UL = 0; const U  = 1; const UR = 2;
const L  = 3; const C  = 4; const R  = 5;
const DL = 6; const D  = 7; const DR = 8;

// directions from the reference point
const ul = 16; const u  = 17; const ur = 18;
const l  = 19; const c  = 20; const r  = 21;
const dl = 22; const d  = 23; const dr = 24;
const rp = 16;

function allRots (arr) {
  return [arr,
  [arr[2], arr[5], arr[8],
   arr[1], arr[4], arr[7],
   arr[0], arr[3], arr[6]],

  [arr[8], arr[7], arr[6],
   arr[5], arr[4], arr[3],
   arr[2], arr[1], arr[0]],

  [arr[6], arr[3], arr[0],
   arr[7], arr[4], arr[1],
   arr[8], arr[5], arr[2]]];
}
var allVRots = allRots(view);

function rotateCW3([[a,b,c],[d,e,f],[g,h,i]]) {
  return [[g,d,a],[h,e,b],[i,f,c]]
}

function on (where, what) {
  if (Array.isArray(where)) return where.some(c=>on(c, what));
  if (Array.isArray(what)) return what.some(c=>on(where, c));
  return basedOn(get(where), what);
}
function find (what) {
  return view.findIndex(c=>basedOn(c, what));
}
function findAll (what) {
  return view.map((c,i)=>[c,i]).filter(c=>basedOn(c[0], what)).map(c=>c[1]);
}
function count (what) {
  return findAll(what).length;
}
function findRel (what) {
  return ref(find(what));
}
function findAllRel (what) {
  return findAll(what).map(c=>ref(c));
}
function found (what) {
  return find(what) != -1;
}
function get (dir) {
  if (Array.isArray(dir)) return dir.map(c=>get(c));
  return view[raw(dir)];
}
function deq (a, b) {
  return a==b || raw(a)==raw(b);
}

// returns a random number from 0 to 4, based on the rotation. Will always have a possibility of being 0
function random4 () {
  var scores = allRots(view.map(c=>c.color)).map((c) => {
    let cscore = 0;
    c.forEach((c) => {
      cscore*= 8;
      cscore+= c-1;
    });
    return cscore;
  });
  var bestscore = -1, bestindex = 1;
  scores.forEach((score, index) => {
    if (score > bestscore) {
      bestscore = score;
      bestindex = index;
    }
  })
  return bestindex;
}

function rotate (what, times) {
  for (var i = 0; i < times; i++) what = [2,5,8,1,4,7,0,3,6][what];
  return what;
}

function raw(dir) {
  if (dir&rp) return rotate(dir&~rp, selectedRot);
  return dir;
}

function ref(dir) {
  if (dir == -1) return -1;
  if (dir&rp) return dir;
  return rotate(dir, 4-selectedRot)|rp;
}

function move(dir, force) {
  if (Array.isArray(dir)) return dir.some(c=>move(c, force));
  dir = raw(dir);
  return result({cell:dir}, force);
}

function color(dir, col) {
  if (Array.isArray(dir)) return dir.some(cdir => !color(cdir, col));
  dir = raw(dir);
  if (view[dir].color == col) return true;
  result({cell:dir, color:Math.abs(col)});
  return false;
}

function rcolOf(what) {
  return Number.isInteger(what)? what : what.color;
}

function colOf(what) {
  return Math.abs(Number.isInteger(what)? what : what.color);
}
function sees(c1,c2) {
  c1 = raw(c1);
  c2 = raw(c2);
  return Math.abs(c1%3-c2%3)<2 && Math.abs(Math.floor(c1/3)-Math.floor(c2/3))<2;
}

function spawn(dir, t) {
  if (Array.isArray(t)) return t.some(c=>spawn(dir, c));
  if (Array.isArray(dir)) return dir.some(c=>spawn(c, t));
  dir = raw(dir);
  return result({cell:dir, type:t});
}
// repairs a single cell
function correct(dir) {
  dir = raw(dir);
  let col = colOf(selectedPt[dir]);
  if (col && view[dir].color != col) {
    color(dir, col);
    return false;
  }
  return true;
}
// if pattern is repaired, returns true, otherwise fixes one cell
// firstdirs is lowercase (if you do want it to be from the patterns POV)
function repair(firstdirs, onlyThose) {
  //log("FD",firstdirs);
  var found = [];
  view.forEach((v, i) => {
    let col = colOf(selectedPt[i]);
    if (col && v.color != col) {
      found.push(i);
    }
  });
  if (found.length == 0) return true;
  if (firstdirs && (firstdirs = firstdirs.map(c=>raw(c))).some(c=>found.includes(c))) {
    let dir = firstdirs.find(c=>found.includes(c));
    let col = colOf(selectedPt[dir]);
    color(dir, col);
    return false;
  }
  if (!onlyThose) {
    let dir = found[random4() % found.length];
    let col = colOf(selectedPt[dir]);
    color(dir, col);
    return false;
  } else return true;
}

function flatten (arr) {
  return arr.reduce((a,b)=>a.concat(b));
}

var selectedHp, selectedVp, selectedPt, selectedRot;

class Pattern {
  constructor(pattern, inherit) {
    this.pt = pattern;
    if (inherit) {
      this.vp = inherit.vp;
      this.hp = inherit.hp;
      this.rot = inherit.rot;
    } else {
      this.vp = 0;
      this.hp = 0;
      this.rot = 0;
    }
  }

  rotateClockwise() {
    var arr = [];
    for (var i = 0; i < this.pt[0].length; i++) {
      var sarr = [];
      for (var j = this.pt.length-1; j >= 0; j--) {
        sarr.push(this.pt[j][i]);
      }
      arr.push(sarr);
    }
    //log(arr);
    var res = new Pattern(arr, this);
    res.rot = (this.rot+1) % 4;
    return res;
  }

  select(x, y, w, h) {
    var res = new Pattern(this.pt.slice(y, y+h).map(c=>c.slice(x, x+w)), this);
    res.hp+= x;
    res.vp+= y;
    return res;
  }

  rots(dir) {
    var pts = [];
    var pt = new Pattern(this.pt, this);
    for (let i = 0; i < this.lengthIn(dir); i++) {
      pts.push(pt);
      pt = pt.rotate(dir);
    }
    return pts;
  }

  map(fn) {
    return new Pattern(this.pt.map(ln=>ln.map(fn)), this);
  }

  lengthIn(dir) {
    if (dir == U || dir == D) return this.pt.length;
    else if (this.pt.length > 0) return this.pt[0].length;
    else return 0;
  }
  rotate(dir) { // moves the center to that direction, shifting the side
    if (dir == R) {
      var res = new Pattern(this.pt.map(c=>((h,...t)=>t.concat(h))(...c)), this);
      res.hp++;
      return res;
    }
    if (dir == L) {
      var res = new Pattern(this.pt.map(a=>a.slice(-1).concat(a.slice(0,-1))), this);
      res.hp++;
      return res;
    }
    if (dir == D) {
      var res = new Pattern(((h,...t)=>t.concat([h]))(...this.pt), this);
      res.vp++;
      return res;
    }
    throw "rotate unimplemented dir!";
  }

  center(dir) { // moves the center to that direction
    if (dir == R) {
      var res = new Pattern(this.pt.map(c=>((h,...t)=>t.concat(0))(...c)), this);
      res.hp++;
      return res;
    }
    if (dir == L) {
      var res = new Pattern(this.pt.map(a=>[0].concat(a.slice(0,-1))), this);
      res.hp++;
      return res;
    }
    if (dir == D) {
      var res = new Pattern(((h,...t)=>t.concat([new Array(h.length)]))(...this.pt), this);
      res.vp++;
      return res;
    }
    throw "center unimplemented dir!";
  }

  setSize(xs, ys) {
    var arr = [];
    for (let y = 0; y < ys; y++) {
      var ca = [];
      for (let x = 0; x < xs; x++) {
        ca.push(this.pt[y % this.pt.length][x % this.pt[0].length]);
      }
      arr.push(ca);
    }
    return new Pattern(arr, this);
  }

  static add(pattern, action, scorer, presetRot) {
    if (Array.isArray(pattern)) pattern = new Pattern(pattern);
    pattern = pattern.setSize(3,3);
    var cpt = pattern.setSize(3,3);
    var orig = cpt.pt;
    for (let i = 0; i < 4; i++) {
      cpt = cpt.rotateClockwise();
      if (!presetRot || presetRot == cpt.rot) {
        cpt.action = action;
        cpt.scorer = scorer;
        cpt.raw = orig;
        cpt.view = allVRots[cpt.rot];
        allPatterns.push(cpt);
      }
    }
  }


  static choose() {
    var maxScore = -1e307;
    var nextScore = -1e308;
    var maxPt;
    allPatterns.forEach((c) => {
      // null  = easy
      // 0     = bad queen
      // false = no match
      // >0    = score
      var falseN = 0;
      var corrects = c.raw.reduce((a,b)=>a.concat(b)).map((guess, index) => {
        var bo = basedOn(c.view[index], guess, true);
        var ant = guess.ant;
        if (ant && basedOn(c.view[index], {ant})) bo+= 1;
        if (bo === 0) return 0;
        if (bo === false) return false;
        if (bo && rcolOf(guess) > 0) return bo;
        var easy = rcolOf(guess)<=0;
        if (easy) {
          falseN++;
          return null;
        }
        return bo;
      });
      var corrstring = corrects.map((chr,i)=>chr>0? (colOf(c.raw[Math.floor(i/3)][i%3])==1? "W" : "#") : chr===null? "-" : " ").join("");
      function match(pt) {
        return new RegExp(pt.replace(/@/g, "[#-W]").replace(/C/g, "[#-]")).test(corrstring);
      }
      var score = corrects.reduce(ADD)*9/(9-falseN);
      if (match(".?(...)?##.##.*")) {
        if (match("(...)?@@@@@@.*|.?@@.@@.@@.?")) score+= foundEnemy? 5 : 3;
        else score+= foundEnemy? 3 : 1;
      } else if (!foundEnemy) score = Math.min(score/2, 5);
      if (c.scorer instanceof Function) score = c.scorer(score, c, corrects, falseN, match);
      if (DEBUG && score > -1) log(
        "scored", score,
        "corr", /*corrects.map(c =>
          (c===false?"F":c===null?"N":c===true?"T":c)
        )*/corrstring,
        "pt", c.raw.map(c=>c.ant? "A"+c.ant.type : c), c.hp, c.vp);
      if (score >= maxScore) {
        nextScore = maxScore;
        maxScore = score;
        c.corrstr = corrstring;
        maxPt = c;
      }
    });
    var flattened = maxPt.pt.reduce((a,b)=>a.concat(b));
    Pattern.hardcorr = flattened.map((guess, index) => rcolOf(guess)<2? 0 : basedOn(view[index], guess)).reduce(ADD);
    Pattern.corrstr = maxPt.corrstr;
    Pattern.corr = flattened.map((guess, index) => basedOn(view[index], guess)).reduce(ADD);
    Pattern.incorr = 9-Pattern.corr;
    Pattern.confidence = maxScore-nextScore;
    selectedRot = maxPt.rot;
    Pattern.action = maxPt.action;
    selectedPt = flattened;
    selectedHp = maxPt.hp;
    Pattern.raw = maxPt.raw;
    Pattern.view = maxPt.view;
    selectedVp = maxPt.vp;
    Pattern.score = maxScore;
    if (DEBUG) log("score", maxScore, "confidence", Pattern.confidence, "corr", Pattern.corr, "hardc", Pattern.hardcorr, "pt", maxPt.pt);//, "fn", maxPt.action+""
  }
}
var allPatterns = [];
function clear() {
  allPatterns = [];
}
function adds(raw, action, scorer, presetRot) { // must get a 3x3 arr
  var pt = raw;
  var hp = raw.hp;
  var vp = raw.vp;
  for (let rot = 0; rot < 4; rot++) {
    let view = allVRots[rot];
    allPatterns.push({pt, action, scorer, rot, hp, vp, view, raw});
    if (rot!=4) pt = rotateCW3(pt);
  }
}
function refPt(...args) {
  clear();
  if (Array.isArray(args[0])) {
    if (args[0].length != 3) args[0] = args[0].slice(0,3);
    if (args[0][0].length != 3) args[0] = args[0].map(c=>c.slice(0,3));
    adds(...args);
  }
  else Pattern.add(...args);
  Pattern.choose();
}

/*
is the 2nd param a subset of the 1st param.
guess can be a number (color), or an object ({color:..,ant:..,..})
guess.ant can be "worker", "queen", "enemy", "enemyworker", "enemyqueen" with obvious meanings. Note that "friend" ≠ me
guess.ant.type can be an array, ORing

true - correct!
false - not correct
0 - notqueen doesn't match (aka very bad)

negativesEqual makes this always return true for negative colors, otherwise it treats negatives as regular colors
*/
function basedOn(real, guess, negativesEqual) {
  if (Array.isArray(real)) return real.some(c=>basedOn(c, guess, negativesEqual));
  if (Number.isInteger(guess)) guess = {color:guess};
  if (guess.notqueen && real.ant && real.ant.friend && real.ant.type==5) return 0;
  if (guess.not) {
    var bo = basedOn(real, guess.not, negativesEqual);
    if (bo) return 0;
  } 
  if (guess.color && Math.abs(guess.color) != real.color && !(negativesEqual && guess.color<0)) return false; // 0 handles itself
  if (guess.obstacle !== undefined) {
    if (guess.obstacle && !real.ant && !(food && real.food && !isQueen)) return false;
    if (!guess.obstacle && (real.ant || (food && real.food && !isQueen))) return false;
  }
  if (guess.badobstacle !== undefined) {
    if (guess.badobstacle && !(real.ant && !real.ant.friend) && !(food && real.food && !isQueen)) return false;
    if (!guess.badobstacle && ((real.ant && !real.ant.friend) || (food && real.food && !isQueen))) return false;
  }
  if (guess.ant) {
    if (!real.ant) return false;
    if (guess.ant == "worker"      &&!( real.ant.friend && real.ant.type!=5)) return false;
    if (guess.ant == "queen"       &&!( real.ant.friend && real.ant.type==5)) return false;
    if (guess.ant == "enemyqueen"  &&!(!real.ant.friend && real.ant.type==5)) return false;
    if (guess.ant == "enemyworker" &&!(!real.ant.friend && real.ant.type!=5)) return false;
    if (guess.ant == "friend" && (!real.ant.friend || real.ant.me)) return false;
    if (guess.ant == "enemy"  &&  real.ant.friend) return false;
    if (Number.isInteger(guess.ant) && real.ant.type != guess.ant) return false;
    if (guess.ant.friend !== undefined && guess.ant.friend !== real.ant.friend) return false;
    if (guess.ant.type !== undefined && !(Array.isArray(guess.ant.type)? guess.ant.type.some(c=>c == real.ant.type) : guess.ant.type == real.ant.type)) return false;
    if (guess.ant.food !== undefined && guess.ant.food !== real.ant.food) return false;
  }
  if (guess.food !== undefined && guess.food !== real.food) return false;
  // log("matched");
  return true;
}

function result (action, force) {
  if (!force) if (toReturn !== undefined) return 0;
  var color = action.color;
  var type = action.type;
  var cell = action.cell;
  if (type < 1 || type > 4) return false;
  if (!(cell >= 0 && cell <= 8)) return false;
  if (color < 1 || color > 8) return false;
  if (!color && ((view[cell].ant && cell != 4) || (isQueen? (view[cell].food && type) : (food && view[cell].food)))) return false; // can't walk onto ant, can't spawn on food, can't move to food with food
  if (!isQueen && type) return false;
  if (!isQueen && !color && food && view[cell].food) return false;
  if (isQueen && !food && type) return false;
  if (type && cell==C) return false;
  if (color && type) return false;

  toReturn = action;
  return true;
}

const WH = 1; // white   
const C1 = 6; // green   HW
const C2 = 5; // red     
const C3 = 8; // black   
const C4 = 2; // yellow  HW
const C5 = 4; // cyan    HW
const C6 = 7; // blue    HW
const C7 = 3; // purple  HW
// C1=GR,C2=BL,C4=YL,C5=DK
const ENEMY = {ant:"enemy"};
const foundEnemy = found(ENEMY);
  //-----------------------------------------------------------------------------------------------------------------------------------------------------\\
 //----------------------------------------------------------------------- MAIN CODE ---------------------------------------------------------------------\\
//---------------------------------------------------------------------------------------------------------------------------------------------------------\\

function log(...args) {
  if (!DEBUG) return;
  toLogRaw.push(args);
  // for (let i of args) {
  //   if (i === undefined) i = "undefined";
  //   var res = "";
  //   if (typeof i === 'string') res = i;
  //   else res = JSON.stringify(i);
  //   toLog+= res + " ";
  // }
  // toLog+= "\n";
}
if (DEBUG) {
  var toLog = "";
  var logMyLogs = false;
  var toLogRaw = [];
  log(type, view.map(c=>c.ant? "A"+c.ant.type : c.color));
}

const Ut = 1;
const Dt = 2;
const Ht = 4;
const Uo = {ant:{type:Ut,friend:true}};
const Do = {ant:{type:Dt,friend:true}};
const Ho = {ant:{type:Ht,friend:true}};
const Mo = {ant:{type:[Ut,Dt],friend:true}};
const Fo = {food:1};
const Qo = {ant:{type:5,friend:true}};
const EQo = {ant:{type:5,friend:false}};
const FRIEND = {ant:"friend"};
const OBSTACLE = {obstacle:true};
const FREE = {obstacle:false};
const BADOBSTACLE = {badobstacle:true};
const STARTINGFOOD = 6;
const LESSENFOOD = 160;
const ENDINGFOOD = 160;
const isMiner = type==Ut || type==Dt;
var friendCount = count(FRIEND);
if (isMiner) {
  var Mu = type==Ut? u : d;
  var Md = type==Ut? d : u;
  var Mur = Mu+1;
  var Mul = Mu-1;
  var Mdr = Md+1;
  var Mdl = Md-1;
}

const foodExt = [C3, C7, WH];
const rawRail = [
  [WH,-1,C7,-1], // 43 03 13 23
  [C6,WH,WH,-1], // 42 02 12 22

  [C4,C1,C2,C2],
  [C2,C4,C5,C5],
  [C3,C5,C1,C3],


//[C3,C1,C4,C4],
//[C4,C4,C5,C2],
//[C5,C2,C1,C3],

//[C3,C1,C2,C5],
//[C3,C3,C1,C5],
//[C2,C4,C5,C2],


//[C2,C1,C3,C5], // 41 01 11 21
//[C1,C5,C5,C4], // 40 00 10 20
//[C5,C4,C2,C3], // 41 01 11 21
  [C6,WH,WH,-1], // 42 02 12 22
  [WH,-1,C7,-1]  // 43 03 13 23
]
.map((ln,row)=>(row<2||row>4)? ln.map(c=>({not:{ant:{friend:true, type:[Ht, 5]}},color:c})) : ln); // queen can't be in the top & bottom 2 rows

function section(ln, action, scorer) {
  if (ln > 0) section(-ln, action, scorer);
  var sct = rawRail.slice(ln+2, ln+5);
  var parts;
  if (Math.abs(ln) != 2) {
    parts = [];
    for (let i = 0; i < 4; i++) {
      var cpt = sct.map(([a,b,c,d])=>[a,b,c]);
      cpt.hp = i;
      cpt.vp = ln;
      parts.push(cpt);
      if (i!=4) sct = sct.map(([a,b,c,d])=>[b,c,d,a]);
    }
  } else {
    var o = sct.map(c=>c.slice(0,3));
    o.vp = ln;
    o.hp = 0;
    parts = [o];
  }
  parts.map(c=>adds(c, action, scorer));
}

function sabotage(where) {
  if (on(where, 1)) repair([where], true);
  else color(where, 1);
}


section(0, ()=>{
  if (isMiner) {
    if (on([r,ur,dr], Fo)  ||  on([u,d], Fo) && on([l,ul,dl], Mo)) { // FAKE RAIL
      color(on([dr,d],Fo)? [dr,u,ul] : [dr,d,dl], C7);
    }
    else if ([l,ul,dl].every(c=>on(c,{ant:{}})) && !random4()) move([r,u,d,ur,dr]); // peer pressure
    /* AV */ else if (found(EQo)) sabotage(find(EQo));
    else if (repair()) {
      if (on(r,Mo) && (random4() > 1 || random4() && friendCount > 3)) move([l,Mul,Mu,Mdl,Md]);
      if (on([Mu,Mur], ENEMY)) move([R, D, DR]); // move somewhere away from enemy
      else if (on(r, Qo) && on([l,ul,dl], {ant:{type:[Ut,Dt],food:1,friend:true}})) move([Mu, Mul, l, Mdl]); // make place for miners with food; Possibly stuck
      else if (on(r, Qo)) move([Mu, Mul]); // don't do stupid things around queen
      else if (on(l, Qo) && !foundEnemy) move([Mul, Mdl, Mu, Md]); //.. I've done stupid things around queen
      else if (on([Mu,Mur], OBSTACLE)) { // up is blocked :/
        if (random4()) move(on(Mu, FRIEND)? [Mur, r] : r);
        else move([r, Mul, Md, Mul, l, Mdl]);
      }
      else move([Mu, r, Mur, Md, Mdr]); // move along
    }
  } else if (isQueen) {
    var HM = Pattern.view.map(c=>+basedOn(c, Ho));
    var helperRows = [HM.slice(0,3),HM.slice(6,9)].map(c=>c.lastIndexOf(1)).map((c,i) => (c==0 && on(i==0? u : d, OBSTACLE)) ? -1 : c);
    var minH = Math.min(helperRows[0],helperRows[1]);
    var maxH = Math.max(helperRows[0],helperRows[1]);
    if (on(r, FRIEND) && [ur,dr].every(c=>on(c, ENEMY))) move([l,ul,dl]);
    if (found(EQo)) { // vampire?
      move(random4()%2? [ur,dr] : [dr,ur]);
      var eQueenRel = (findRel(EQo)-rp)%3;
      if (eQueenRel == 0) move(r,ur,dr);
      spawn(Mu, [u,d,r,ur,dr]);
    }
    if (foundEnemy) // spawn helpers against enemies
      if (food && minH == -1 && count(Ho) < 2) {
        if (helperRows[0] == -1) spawn([u,ur,ul],Ht);
        else                     spawn([d,dr,dl],Ht);
      }
    if ([r,ur,dr].every(c=>on(c, ENEMY))) move([ul,dl,l,u,d]); // OH GOD NO WHY
    if ((minH == -1 || maxH == 2) && on(r, [ENEMY,Ho]) && Pattern.incorr < 2 && count({ant:{}})-1 != count(Ho))
      move(on(ur, ENEMY)? [d,u,dr,ur,ul,dl,l] : [u,d,ur,dr,ul,dl,l]); // initialize transporting around enemy
    if ((!random4() && on(l, OBSTACLE) && on([ul, dl], OBSTACLE)) && Pattern.corr >= 7) move(r); // move forward sometimes if left is 2/3s full
    else if ([r,ur,dr].every(c=>c.ant && !c.ant.friend)) move([l,ul,dl]);
    else if (food && minH > 0 && (
        count(Mo) == 0 && selectedHp != 1 && Pattern.corr != 9 && food < LESSENFOOD
      ||
        //Pattern.corr === 9 && [u,d].every(c=>on(c,Ho)) && selectedHp === 1 && food >= LESSENFOOD && food < ENDINGFOOD && random4() < 2
        Pattern.corr === 9 && selectedHp == 0 && count(Mo) === 0 && food >= LESSENFOOD && food < ENDINGFOOD && random4() < 2
      )) { // spawn miners
      if (random4()%2) spawn([u,ul], Ut);
      else             spawn([d,dl], Dt);
    } else if (repair()) {
      if (food && minH == -1 && count(Ho) < 2) { // spawn helpers
        if (helperRows[0] == -1) spawn([u,ur,ul],Ht);
        else                     spawn([d,dr,dl],Ht);
      }
      else if (selectedHp != 1  ||  selectedHp==1 && /*(*/(maxH==2 || minH == -1 && helperRows.includes(2)) && (!random4() || food < LESSENFOOD || found(Mo))  ||  foundEnemy) move(r); // move forwards
      else if (on(ul, Do) && on(dl,Uo) && on(l, {ant:{}})) move(r); // miners are in wrong places
    }
  } else { // helper
    var repaired = repair();
    var queenRel = (findRel(Qo)-rp)%3;
    var dir = queenRel==0? 0 : 1;
    if (repair()) {
      if (on(r, EQo) && [u,d,ur,dr].map(c=>on(c, ENEMY)? 1 : 0).reduce(ADD) >= 3) move(c); // protect the queen from the evils ahead
      else if (on(r, Qo)) move([u,d,ur,dr,ul,dl,l]);
      else move((on([d,dr,dl], Ho)? [u+dir,d+dir] : [d+dir,u+dir]).concat([u+(1-dir), d+(1-dir)]), !random4());
    }
  }
})

section(1, ()=>{
  const A = selectedVp > 0? d : u; // away
  const I = selectedVp > 0? u : d; // in
  const AR = A+1;
  const AL = A-1;
  const IR = I+1;
  const IL = I-1;

  if (isMiner) {
    var queenRel = (findRel(Qo)-rp)%3;
    if (on([r,IR], Fo)) color([r,IR, I], C7); // FAKE RAIL
    else if ([l,IL].every(c=>on(c,{ant:{}})) && !random4()) move([r,IR]); // peer pressure
    /* AV */ else if (found(EQo)) sabotage(find(EQo));
    else if ((found(EQo) && random4() && Pattern.dist <= 1 && on(find(EQo), 1)) || repair()) {
      if (on(I, Qo)) move(l); // what am I doing here?
      else if (A == Mu) { // my dir!
        if (!food && selectedHp == 0 && (on(r,Ho) && on(IR, Qo)  ||  count(Mo) >= 6)) move(A); // move out!
        else if (on(IR,Qo) && on([l,IL], {ant:{type:[Mu,Md],friend:true,food:1}})) move(C); // waiting in line :D
        else if (on(IR,Qo)) move(C); // waiting in line :D
        else if (random4()) move([r, I, IR]);
        else move([r, I, IR, l, IL]);
      } else { // not my dir
        // TODO fix \\ if (selectedHp == 0 && count(Mo) >= 6 && food) move(A); // fake rail escape
        if (random4()) {
          move([I, IR, IL, l]);
        } else {
          move([r, I, IR, IL, l]);
        }
      }
    }
  } else if (isQueen) {
    if (found(EQo)) { // vampire?
      var eQueenRel = (findRel(EQo)-rp)%3;
      if (eQueenRel==0) move(r, IR);
      spawn(Mu, [r,IR]);
      spawn(Md, I);
    }
    /* AV */ if (food > 70 && (
      [IR,IL,I,l,r].every(c=>on(c,ENEMY)) // completely encased
      || on(IR, EQo) && [r,I].every(c=>on(c, ENEMY)) // getting leeched
      || on(I, EQo) && [r,l,IL,IR].map(c=>get(c)).map(c=>c.ant? (c.ant.friend? 1 : -1) : 0).reduce(ADD) < 0 // leeched
    )) move([A,AR,AL]); // BAD NEWS COMPLETELY DEAD
    if (!random4() || found(EQo) || repair())
      move(random4()? [IR,r,I,l] : [IR,r,I]);
  } else { // helper
    var queenRel = (findRel(Qo)-rp)%3;
    /* AV */ if (on(r,Qo) && on([I,IR], EQo)) move([IR,I,l,IL]);
    if (on(l, Qo)) { if (!random4() || repair()) move(r) } // queen's transporting
    if (on(I, Qo) && on(IR, {ant:"enemyworker"})) { if (!random4() || repair()) move(r) } // queen needs to transport
    // what was this? if ([l,IL,I].every(c=>on(c,OBSTACLE)) && (count(ENEMY) > 1 || find(EQo)) && !random4()) move([r,ur]);
    if ((selectedVp < 0? /...[#-W]{6}/ : /[#-W]{6}.../).test(Pattern.corrstr) && queenRel == 2 && count(Ho) == 1) move(r); // move forward without repairing
    if (!random4() && queenRel == 1 && selectedHp == 1 && on(AL, {ant:{}})) move(r); // something is out; don't repair
    else if (repair([r,l,A,AR,AL])) {
      if (on(r, ENEMY) && on(I, Qo) && [l,IL].every(c=>on(c,FRIEND))) move(IR); // protect from vampire
      if (on(r, ENEMY) && on(IL, Qo) && [l,IR].every(c=>on(c,FRIEND))) move(I);
      if (on(r, ENEMY) && !get(r).ant.food && on(I, Qo)) move(IR);
      if (queenRel == 1 && selectedHp == 1 && on(AL, {ant:{}})) move(r); // something is out
      else if (on([l,r], Ho)) { // move to the other side
        if (found(Qo)) move([I]); // TODO integrate ,IL,IR
        else move([l,r]);
      }
      else if (queenRel == 2) move(r); // move forward
    }
  }
}, (pscore, pt, corrects, falseN, match) => {
  if (match(".?(...)?@@.@@.*") && !foundEnemy) {
    if (!match(pt.vp>0? ".?@@.@@.*" : ".?...@@.@@.*")) pscore/=2;
  }
  return pscore;
})

if (isMiner) {
  section(2, () => {
    const A = selectedVp > 0? d : u; // away
    const I = selectedVp > 0? u : d; // in
    if (on(A,OBSTACLE)) move(I);
    else if (repair()) move(food? I: Mu);
  }, (pscore, pt, corrects, falseN, match) => match(pt.vp>0? "@@@.@...." : "....@.@@@")? match("@@@@@@@@@")? 100 : ((pt.vp>0) == (type==Dt)? 13 : 10) : 0);
  if (type==Dt) foodExt.reverse();
  if (!found(Ho) && !found(Qo)) {
    var lns = [rawRail[0], rawRail[1]].map(c=>c.slice(0,3));
    [[lns[0],lns[1],lns[0]], [lns[1],lns[0],lns[1]]].map(c=>{
      adds(c, () => {
        var onL;
        if (!food && ((onL = on(l, Fo)) || on(r, Fo))) {
          var foodpt = Pattern.raw.map((ln, i) => [ln[0], foodExt[i], ln[2]]);
          refPt(foodpt,undefined,undefined,selectedRot);
          if (repair()) move(onL? l : r);
        }
        else if (repair([l,r,ul,ur,dl,dr], on([Mul, Mur, Mdl, Mdr, l, r], {ant:{friend:true,type:[Ut,Dt],food:1}}) ||  on([Mu, Md], Mo))) {
          move(food? [Md, Mu] : [Mu, Md]);
        }
      }, (pscore, pt, corrects, falseN, match) => {
        var score = 0;
        var dMatch = match("...@.@@@@");
        var uMatch = match("@.@@.@...");
             if ((type==Ut ^ food) && dMatch) score = 15;
        else if ((type==Dt ^ food) && uMatch) score = 15;
        else if (uMatch || dMatch) score = 6;
        if ([0,2,3,5,6,8].some(c=>basedOn(pt.view[c], FRIEND) && !pt.view[c].ant.food)) score = 0;
        return score;
      });
      if (food) {
        var extp = c.map((ln, i) => [ln[0], foodExt[i], ln[2]]);
        [extp.map(([a,b,c])=>[0,a,b]), extp.map(([a,b,c])=>[b,c,0])].forEach((pt,i) => adds(pt, () => {
          move(i? l : r);
        }, (pscore, pt, corrects, falseN, match) => match("@@@@@@@@@")? 100 : 0));
      }
    });
  }
}

Pattern.choose();
var confident = ((Pattern.confidence >= 1 && (Pattern.score > 4 || Pattern.corr >= 4)) || (Pattern.score >= 9 && Pattern.confidence > 0.05)); // && (selectedHp !=  || !found(Qo));
var failAction = () => {
  if (foundEnemy) {
    log(view);
    log("dead around enemy :/");
    logMyLogs = true;
  }
  if (isQueen) {
    if (found(EQo)) {
      move([8-(find(EQo)-rp) + rp]);
      move(random4()%2? U : UR);
    }
    if (foundEnemy) move(random4()%2? U : UR);
  } else {
    // if (!found(Qo) && found(Fo)) move(find(Fo));
    var enemyPlace = find(ENEMY);
    if (enemyPlace !== -1) color(enemyPlace, get(enemyPlace).color==1? C3 : WH);
  }
}
if (!confident) Pattern.action = failAction;

if (isMiner) {
  if ((Pattern.hardcorr >= 4 || Pattern.score > 5) && confident) Pattern.action();
  else {
    failAction();
  }
} else if (isQueen) {
  if ((Pattern.hardcorr >= 6 || food > STARTINGFOOD+2 || friendCount>1 || found(Mo) || Pattern.score > 6 || (false)) && confident) Pattern.action();
  else if (food >= STARTINGFOOD && friendCount == 1) {
    clear();
    Pattern.add([[1,{ant:Ho.ant,color:1},1],
                 [1,1,1],
                 [1,1,1]], ()=>spawn([ur,ul],Ht));
    Pattern.add([[1,1,{ant:Ho.ant,color:1}],
                 [1,1,1],
                 [1,1,1]], ()=>spawn([ur,u],Ht));
    Pattern.choose();
    if (repair()) Pattern.action();
  } else if (food == 0 && friendCount == 0) { // diagonal search
    if (found(Fo)) {
      move(find(Fo));
    } else {
      clear();
      Pattern.add([[WH,WH,WH],
                   [WH,C1,WH],
                   [C1,WH,WH]], ()=>move(ur));
      Pattern.add([[WH,WH,WH],
                   [WH,WH,WH],
                   [C1,WH,WH]], ()=>color(C, C1));
      Pattern.add([[WH,WH,WH],[WH,WH,WH],[WH,WH,WH]], ()=>color(DL, C1));
      Pattern.choose();
      if (Pattern.corr == 9) Pattern.action();
      else move(random4()? [DL,UL,DR,UL] : [D,L,U,R]);
    }
  } else if (food == 1 && friendCount == 0) spawn([U,L,D,R,UL,DL,UR,DR], Ht);
  else if (friendCount == 1) lightSpeed();
  else if (friendCount > 0) {
    var pt = new Pattern(rawRail).select(0,2,4,3).rotate(L).rotate(L).pt;
    pt[1][2] = {color:pt[1][2], ant:{type:Ht, friend:true}};
    refPt(pt);
    repair([c,u,d,ur,dr]);
  } // TODO wtf to do after this
  else Pattern.action(); // eh fuck it
} else if (type == Ht) {
  if (confident && (Pattern.score >= 4 || Pattern.hardcorr >= 5 || friendCount>1)) Pattern.action();
  else if (found(Qo)) lightSpeed();
  else if (Pattern.hardcorr >= 3 && confident) repair();
}

function lightSpeed() {
  var other = find(isQueen? Ho : Qo);
  var orth = other%2;
  if (isQueen || (view[other].ant.food < STARTINGFOOD && count(Ho) == 1)) { // LS
    if (orth && found(Fo)) { // grab easy food
      var fp = find(Fo);
      if (sees(other, fp)) move(fp);
      else {
        refPt([[0,FRIEND,0],
                     [0,0,0],
                     [0,0,0]]);
        move(l);
      }
    }
    clear();
    // Pattern.when(U,find(FRIEND), ()=>isQueen? move(ul) : move(ur)); when I'm not lazy imma make this a replacement of the below
    Pattern.add([[0,FRIEND,0],
                 [0,0,0],
                 [0,0,0]], ()=>isQueen? move(ul) : move(ur));
    Pattern.add([[0,0,FRIEND],
                 [0,0,0],
                 [0,0,0]], ()=>move(u));
    Pattern.choose();
    Pattern.action();
  }
}

if (DEBUG) log("END", type, view.map(c=>c.ant? "A"+c.ant.type : c.color));
if (DEBUG && logMyLogs) {
  //for (let i = 0; i < toLog.length; i+=800)
  //  console.log(toLog.substring(i,i+800));
  for (let i of toLogRaw) console.log(...i);
}
if (toReturn) return toReturn;
else return {cell:4};

Früher war dies Miners on a Rail (siehe Änderungshistorie), wurde jedoch in Sliding Miners geändert, da es eine bessere Leistung erbringt und MoaR seinem Erfolg nur im Wege stehen würde, wenn dies ein weiterer Eintrag wäre.


Diesmal wieder als Erster in der Rangliste ...
trichoplax

14

Segelflugzeug

Segelflugzeug in AktionSegelflugzeug nach links drehenSegelflugzeug nach rechts drehen

//console.log(JSON.stringify(view))
var TRAIL = 6;
var SPAWN = 3;
var IDLE = 4;
var FOOD_THRESHOLD = 150;
var SPAWN_MIN = 3;
var HIGHWAY_COLORS = [7,6,4,2,3];
var HIGHWAY_THRESHOLD = 70;
var ret = {cell:4};
if(isOnHighway()) {
    var cont = true;
    //== Make best guess to if in a glider formation ==//
    if(view[4].ant.type == 5) {
        if((findWorker(1) >= 0 && findWorker(4) >= 0) || view[4].ant.food < HIGHWAY_THRESHOLD) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 4) {
        if(findWorker(1) >= 0 && findWorker(5) >= 0) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 3) {
        if(findWorker(2) >= 0 && findWorker(5) >= 0) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 2) {
        if(findWorker(5) < 0) {
            var pos3 = findWorker(3);
            if(pos3 >= 0 && view[pos3].ant.food == 0) {
                cont = false;
            }
        }
        else if(findWorker(3) >= 0 || (findWorker(1) >= 0 && view[findWorker(5)].color == SPAWN))
            cont = false;
    }
    else if(view[4].ant.type == 1) {
        if(findWorker(5) < 0) {
            var pos4 = findWorker(4);
            if(pos4 >= 0 && view[pos4].ant.food == 0) {
                cont = false;
            }
        }
        else if(!isHighwayCenter())
            cont = false;
    }
    if(findWorker(5) >= 0) {
        for(var i=0;i<9;i++) {
            if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                if(view[i].ant.food > 10 || view[i].ant.food == 0)
                    cont = true;
                else
                    cont = false;
            }
        }
    }
    //== End guesswork ==//
    if(cont) {
        ret = highwayRobbery();
        if(view[4].ant.type == 1) {
            //try to repair
            var curIndex = HIGHWAY_COLORS.indexOf(view[4].color);
            var prvCol = HIGHWAY_COLORS[(curIndex+1)%HIGHWAY_COLORS.length];
            var nxtCol1 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-1)%HIGHWAY_COLORS.length];
            var nxtCol2 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-2)%HIGHWAY_COLORS.length];
            var nxtCol3 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-3)%HIGHWAY_COLORS.length];
            var prevAt = -1;
            for(var i=0;i<9;i++) {
                if(i%2 == 1 && view[i].color == prvCol && view[deRotate(i,1)].color == nxtCol1 && view[deRotate(i,-1)].color == nxtCol1) prevAt = i;
            }
            if(prevAt >= 0) {
                // yep, brute force it. Because I'm lazy.
                var goNxt = 8-prevAt;
                if(view[deRotate(goNxt,1)].color == nxtCol3 && view[deRotate(goNxt,-1)].color == prvCol) ret = {cell:goNxt};
                else if(view[deRotate(goNxt,1)].color == prvCol && view[deRotate(goNxt,-1)].color == nxtCol3) ret = {cell:goNxt};
                else if(view[goNxt].color != nxtCol1) ret = {cell:goNxt,color:nxtCol1};
                else if(view[deRotate(goNxt,2)].color != nxtCol2) ret = {cell:deRotate(goNxt,2),color:nxtCol2};
                else if(view[deRotate(goNxt,-2)].color != nxtCol2) ret = {cell:deRotate(goNxt,-2),color:nxtCol2};
                else if(view[deRotate(goNxt,1)].color != nxtCol3) ret = {cell:deRotate(goNxt,1),color:nxtCol3};
                else if(view[deRotate(goNxt,-1)].color != nxtCol3) ret = {cell:deRotate(goNxt,-1),color:nxtCol3};
                else ret = {cell:goNxt};
                ret = sanityCheck(ret);
                return ret;
            }
        }
        if(view[4].ant.type == 5 && isHighwayCenter()) {
            if(ret.cell >= 0) {
                ret = {cell:8-ret.cell};
                if(view[4].color == SPAWN && (view[4].ant.food > 90 || view[4].ant.food % 7 == 0) && getHighestWorker() == 0 && (view[4].ant.food < 140 || view[4].ant.food % 9 == 0) && view[0].color == 2 && view[4].ant.food > 50 && view[4].ant.food < 200) {
            //fine
                    if(view[4].ant.food % 10 < 5)
                        ret = {cell:deRotate(ret.cell,3),type:3};
                }
                if(view[ret.cell].ant != null && !view[ret.cell].ant.friend && view[ret.cell].ant.food == 0 && view[4].ant.food > 0) {
                    if(view[deRotate(ret.cell,1)].ant == null)
                        ret = {cell:deRotate(ret.cell,1),type:3};
                    if(view[deRotate(ret.cell,-1)].ant == null)
                        ret = {cell:deRotate(ret.cell,1),type:3};
                }
            }
            for(var i=0;i<9;i++) {
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                    ret = {cell:8-i};
                }
            }
        }
        if(ret.cell >= 0) {
            for(var i=0;i<9;i++) {
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                    var rr = basicHighwayMove();
                    if(rr.cell >= 0 && view[4].ant.type != 5)
                        ret = {cell:deRotate(rr.cell,-2)};
                }
            }
            if(view[ret.cell].ant != null) {
                var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
                var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
                for(var i=0;i<9;i++) {
                    if(view[i].color == nextMove) {
                        if(view[i].ant == null) {
                            ret = {cell:i};
                            break;
                        }
                    }
                }
                if(view[4].ant.type == 5) ret = {cell:8-ret.cell};
            }
        }
        if(view[4].ant.type == 5) {
            var foodedEnemy = false;
            for(var i=0;i<9;i++) {
                if(getNumWorkers(3) >= 2) break;
                if(i != 4 && view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 25) {
                    if(view[deRotate(i,1)].ant == null) {
                        ret = {cell:deRotate(i,1),type:3};
                    }
                    else if(view[deRotate(i,-1)].ant == null) {
                        ret = {cell:deRotate(i,-1),type:3};
                    }
                    else if(i%2 == 1 && view[deRotate(i,2)].ant == null) {
                        ret = {cell:deRotate(i,2),type:3};
                    }
                    else if(i%2 == 1 && view[deRotate(i,-2)].ant == null) {
                        ret = {cell:deRotate(i,-2),type:3};
                    }
                }
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 3 && view[8-i].ant == null) {
                    if(i == ret.cell) {
                        ret = {cell:deRotate(i,1)}
                    }
                    else {
                        return {cell:8-i};
                    }
                }
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.food == 0) {
                    foodedEnemy = true;
                }
            }
        }
        var numAnts = 0;
        for(var i=0;i<9;i++) {
            if(view[i].ant != null)
                numAnts++;
        }
        if(numAnts > 2 && sanityCheck(ret).cell == 4) {
            ret = {cell:findOpenSpace(0,1)};
        }
        if(view[4].ant.type == 3) {
            if(getNumWorkers(5) > 0) {
                for(var i=0;i<9;i++) {
                    if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                        ret = {cell:4};
                    }
                }
            }
        }
        if(view[4].ant.type == 4 && getNumWorkers(1) && isHighwayCenter()) {
            var workerPos = findWorker(1);
            for(var i=0;i<9;i++) {
                if(!areAdjacent(i,workerPos)) ret = {cell:i};
            }
        }
        if(ret.cell == -1) {
            if(isHighwayCenter()) {
                for(var i=0;i<9;i++) {
                    var p1 = deRotate(i,3);
                    var p2 = deRotate(i,-3);
                    if(view[i].color == view[p1].color && view[i].color == view[p2].color) {
                        ret = {cell:8-i};
                    }
                }
                if(view[4].ant.type == 1 || view[4].ant.type == 5) {
                    ret = {cell:8-ret.cell};
                }
            }
        }
        if(ret.cell >= 0)
            return sanityCheck(ret);
    }
}

switch(view[4].ant.type) {
    case 5:
        ret = doQueen();
        break;
    case 1:
    case 2:
        ret = doSweep();
        break;
    case 3:
    case 4:
        ret = doGuide();
        break;
    default:
        break;
}
//basic sanity check
ret = sanityCheck(ret);
return ret;

function sanityCheck(ret) {
    if(!ret || ret.cell < 0 || ret.cell > 8) {
        return {cell:4};
    }
    if(ret.color) {
        return ret;
    }
    if((ret.cell != 4 && view[ret.cell].ant != null) || (view[ret.cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) {
        return {cell:4};
    }
    if(ret.type && (view[ret.cell].ant != null || view[ret.cell].food > 0 || view[4].ant.food == 0 || view[4].ant.type < 5)) {
        return {cell:4};
    }
    return ret;
}

function doQueen() {
    if((view[4].ant.food == SPAWN_MIN || (view[4].ant.food >= SPAWN_MIN && view[4].ant.food < FOOD_THRESHOLD && (view[4].ant.food % 3 == 1 || isOnHighway()))) && getHighestWorker() <= 1 ) {
        //prep for first ant
        var s0 = view[0].ant;
        var s1 = view[1].ant;
        var s2 = view[2].ant;
        var s3 = view[3].ant;
        var s5 = view[5].ant;
        var s6 = view[6].ant;
        var s7 = view[7].ant;
        var s8 = view[8].ant;
        var nullCount = 0 + (s0 == null?1:0) + (s1 == null?1:0) + (s2 == null?1:0) + (s3 == null?1:0) + (s5 == null?1:0) + (s6 == null?1:0) + (s7 == null?1:0) + (s8 == null?1:0);
        var nullCount2 = 0 + (s0 == null || s0.friend?1:0) + (s1 == null || s1.friend?1:0) + (s2 == null || s2.friend?1:0) + (s3 == null || s3.friend?1:0) + (s5 == null || s5.friend?1:0) + (s6 == null || s6.friend?1:0) + (s7 == null || s7.friend?1:0) + (s8 == null || s8.friend?1:0);
        if(nullCount >= 7 && nullCount2 >= 8 && view[1].food == 0 && view[3].food == 0 && view[5].food == 0 && view[7].food == 0) {
            var high = getHighestWorker();
            if (high <= 1 && view[4].color != SPAWN && !isOnHighway()) {
                // 50% chance of delaying the respawn by 1 additional move away from where we exploded
                // reduces the chance of a second, immediate explosion
                var pos1 = findWorker(1);
                if(findFirstTrail() < 2 && view[4].ant.food > SPAWN_MIN+1 && pos1 < 0) return foreverAlone();
                if(pos1 >= 0) {
                    var space = deRotate(pos1,2);
                    if(view[space].ant != null) return {cell:findOpenSpace(0,1)};
                }
                return {cell:4,color:SPAWN};
            }
            //spawn first ant
            else if(view[4].color == SPAWN) {
                var pos1 = findWorker(1);
                if(pos1 < 0)  {
                    pos1 = findFirstTrail();
                    if(pos1 % 2 == 0) pos1 = deRotate(pos1,1);
                    else pos1 = deRotate(pos1,4);
                }
                var space = findOpenSpace(pos1,2);
                var high = getHighestWorker();
                if(space < 0) return {cell:4,color:TRAIL}
                if(high == 0) { //no workers
                    return {cell:space,type:1};
                }
                else if(high < 4) { //1 worker of type:high
                    return {cell:space,type:high+1};
                }
                else { //1 worker of type 4
                    //we have all workers, skip!
                }
            }
        }
        else {
            return foreverAlone();
        }
    }
    else if(view[4].ant.food == 1 && getHighestWorker() == 0 ) {
        var space = findOpenSpace(1,2);
        return {cell:space,type:1};
    }
    else if(view[4].ant.food >= 1 && getHighestWorker() < 4 && findWorker(1) >= 0) {
        //spawn remaining ants
        if(view[4].color == SPAWN && !isHighwayCenter()) {
            var pos1 = findWorker(getHighestWorker());
            var space = deRotate(pos1,2);
            var high = getHighestWorker();
            if(space < 0 || view[space].ant != null) return {cell:findOpenSpace(0,1)};
            if(high == 0) { //no workers
                return {cell:space,type:1};
            }
            else if(high < 4) { //1 worker of type:high
                return {cell:space,type:high+1};
            }
            else { //1 worker of type 4
                //we have all workers, skip!
            }
        }
    }
    if(view[4].color == SPAWN && getNumWorkers(3) == 1 && getNumWorkers(4) == 1) {
        var one = getNumWorkers(1);
        var two = getNumWorkers(2);
        if((one ^ two) == 1 && (findWorker(1) % 2 == 0 || findWorker(2) % 2 == 0))
            return {cell:4,color:1};
    }
    if(getNumWorkers(1) == 0 && getNumWorkers(2) == 0) {
        if(getNumWorkers(4) == 1 && getNumWorkers(3) == 0) {
            var pos4 = findWorker(4);
            if(view[deRotate(pos4,1)].ant == null && view[deRotate(pos4,2)].ant == null && findWorker(4) % 2 == 1) {
                //finish rotate with only one glider arm
                return {cell:4};
            }
        }
        return foreverAlone();
    }
    else if(getNumWorkers(1) >= 1 && getNumWorkers(2) >= 1 && getNumWorkers(3) >= 1 && getNumWorkers(4) >= 1) {
        if(view[4].color != 2 && findWorker(1)%2 == 1 && findWorker(2)%2 == 1) {
            return {cell:4,color:TRAIL};
        }
        //move diagonally
        var pos = findWorker(4);
        pos = deRotate(pos,1);
        var checkpos = view[deRotate(pos,4)];
        if(checkpos.ant != null && checkpos.ant.friend) {
            if(checkpos.ant.type == 2)
                return {cell:4};
            if(checkpos.ant.type == 1)
                return {cell:4};
        }
        if(view[pos].ant) return {cell:4,color:1};
        return {cell:pos};
    }
    else {
        var pos = findWorker(4);
        if(pos < 0) {
            //if gliding along with only a buddy
            pos = findWorker(1);
            if(pos >= 0 && view[deRotate(pos,2)].food > 0 && view[deRotate(pos,1)].food == 0) {
                return {cell:4};
            }
        }
        if(pos < 0) {
            var s1 = view[1].ant;
            var s3 = view[3].ant;
            var s5 = view[5].ant;
            var s7 = view[7].ant;
            //return {cell:999}
            if(s1 == null) {
                if(s3 == null) {
                    return {cell:0};
                }
                if(s5 == null) {
                    return {cell:2};
                }
            }
            if(s7 == null) {
                if(s3 == null) {
                    return {cell:6};
                }
                if(s5 == null) {
                    return {cell:8};
                }
            }
            return {cell:4};
        }
        pos = deRotate(pos,1);
        var checkpos1 = view[pos];
        for(var i=0;i<9;i++) {
            if(i != 4 && view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 2) {
                if(i%2==0) {
                    if(view[deRotate(i,1)].ant == null) return {cell:deRotate(i,1),type:3};
                    if(view[deRotate(i,-1)].ant == null) return {cell:deRotate(i,-1),type:3};
                }
                else {
                    if(view[deRotate(i,1)].ant == null) return {cell:deRotate(i,1),type:3};
                    if(view[deRotate(i,-1)].ant == null) return {cell:deRotate(i,-1),type:3};
                    if(view[deRotate(i,2)].ant == null) return {cell:deRotate(i,2),type:3};
                    if(view[deRotate(i,-2)].ant == null) return {cell:deRotate(i,-2),type:3};
                }
                return {cell:4};
            }
        }
        if(checkpos1.ant != null && view[deRotate(pos,1)].ant != null && !view[deRotate(pos,1)].ant.friend) {
            return foreverAlone();
        }

        var checkpos2 = view[deRotate(pos,4)];
        var checkpos3 = view[deRotate(checkpos,1)];
        if(checkpos1.ant != null && checkpos1.ant.friend && checkpos1.ant.type == 1 && checkpos2.ant != null && checkpos2.ant.friend && checkpos2.ant.type == 2 && checkpos3.ant != null && checkpos3.ant.friend && checkpos3.ant.type == 3) {
            //move out of spawn orientation
            return {cell:4};
        }
        if(view[pos].ant != null) {
            if(checkpos2.ant == null && checkpos1.ant == null) {
                return {cell:8-pos};
            }
            if(!view[pos].ant.friend) {
                return foreverAlone();
            }
            if(view[4].color == TRAIL) return foreverAlone();
            return {cell:4,color:TRAIL};
        }
        if(8 - findWorker(3) == findWorker(4)) {
            //finish rotate to the right
            return {cell:4};
        }
        if((view[deRotate(pos,1)].food > 0 || view[deRotate(pos,2)].food > 0) && view[deRotate(pos,1)].ant == null && view[4].color != TRAIL) {
            if(findWorker(1) < 0 || view[deRotate(findWorker(1),1)].food == 0) {
                return {cell:4};
            }
        }
        return {cell:pos};
    }
    return {cell:100+view[4].ant.type}; //oh god
}

//guides sit next to the queen
function doGuide() {
    var queenPos = findWorker(5);
    var ty = view[4].ant.type==3?2:1;
    var dir = view[4].ant.type==3?1:-1;
    if(queenPos >= 0 && queenPos%2 == 1 && view[queenPos].color == SPAWN) {
        if(view[deRotate(queenPos,dir*2)].ant == null) {
            return {cell:4};
        }
    }
    if(queenPos < 0 || findWorker(ty) < 0) {
        if(findWorker(ty) >= 0 && view[0].color != IDLE) return {cell:0,color:IDLE}
        return firebreak();
    }
    var checkpos = view[deRotate(queenPos,-2*dir)];
    if(view[4].ant.type==4 && checkpos.ant != null && checkpos.ant.friend && checkpos.ant.type == 1) {
        //attempt rotate
        return {cell:deRotate(queenPos,-dir)};
    }
    checkpos = view[deRotate(queenPos,4)];
    if(checkpos.ant != null && checkpos.ant.friend && checkpos.ant.type == ty) {
        //attempt rotate
        if(getNumWorkers(ty) == 1) {
            return {cell:4};
        }
    }
    var pos = deRotate(queenPos,dir);
    if(pos >= 0 && view[4].ant.type==3 && findWorker(4) < 0) {
        //wait for rotate
        if(view[4].color == TRAIL) {
            return {cell:4};
        }
    }
    if(pos >= 0 && findWorker(2) >= 0 && view[deRotate(findWorker(2),1)].ant != null) {
        //rotate
        return {cell:4,color:TRAIL};
    }
    if(pos < 0) pos = 4;
    else if(view[pos].ant != null) return {cell:deRotate(queenPos,4)};
    if(pos == 4 && view[queenPos].color == TRAIL) return {cell:queenPos,color:1};
    return {cell:pos};
}

//sweepers sit next to guides
function doSweep() {
    var queenPos = findWorker(5);
    var followType = view[4].ant.type==1?4:3;
    var pos = findWorker(followType);
    if(pos % 2 == 0 && getNumWorkers(followType) > 1) {
        //if there's more than one worker #4, we want to use the best one
        for(var i=pos+1;i<9;i++) {
            if(i != 4 && view[i].ant != null) {
                if(view[i].ant.friend && view[i].ant.type == followType) {
                    pos = i;
                    break;
                }
            }
        }
    }
    if(queenPos >= 0 && queenPos%2 == 1 && view[queenPos].color == SPAWN) {
        var p = findWorker(view[4].ant.type);
        if(p >= 0 && (deRotate(p,1) == queenPos || deRotate(p,-1) == queenPos)) {
            return {cell:8-queenPos};
        }
        return {cell:4};
    }
    if(queenPos >= 0 && pos < 0) {
        //if Worker #1 is the only ant besides the queen:
        //TODO
    //good
        if(view[queenPos].ant.food <= SPAWN_MIN || !(view[queenPos].ant.food < FOOD_THRESHOLD && view[queenPos].ant.food % 3 == 1 && !isOnHighway())) {
            var go = deRotate(queenPos,-1);
            if((view[deRotate(queenPos,-2)].food > 0 || view[deRotate(queenPos,-3)].food > 0 || (queenPos %2 == 1 && view[deRotate(queenPos,-3)].food > 0)) && view[go].food == 0) {
                go = deRotate(queenPos,2);
                //return {cell:4};
            }
            return {cell:go};
        }
        else if(view[queenPos].ant.food < FOOD_THRESHOLD && view[queenPos].ant.food % 3 == 1) {
            return {cell:4};
        }
    }
    if(queenPos >= 0) {
        var dir = view[4].ant.type==1?1:-1;
        //var checkpos = view[deRotate(pos,-dir)];
        var moveTo = deRotate(pos,dir);
        if(moveTo >= 0 && view[moveTo].ant != null && view[moveTo].ant.friend && view[moveTo].ant.type == 5) {
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type == 2 && findWorker(1) < 0 && view[queenPos].color != TRAIL) {
            moveTo = 4;
        }
        return {cell:moveTo};
    }
    else {
        if(pos < 0) return {cell:4}; //firebreak();
        var dir = view[4].ant.type==1?-1:1;
        var moveTo = deRotate(pos,dir);
        if(view[4].ant.food > 0 && view[moveTo].food > 0) {
            //have food, attempt to give to queen
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type==1 && pos >= 0 && (view[deRotate(pos,dir*2)].food > 0 || view[deRotate(pos,dir*3)].food > 0)) {
            //attempt rotate
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type==2 && pos >= 0 && (view[deRotate(pos,dir*2)].food > 0 || view[deRotate(pos,dir*3)].food > 0)) {
            //attempt rotate
            moveTo = deRotate(pos,-dir);
        }
        if(moveTo >= 0 && view[moveTo].ant != null && view[moveTo].ant.type == 5) {
            if(view[moveTo].ant.friend)
                moveTo = deRotate(moveTo,dir*2);
            else
                moveTo = deRotate(pos,-dir);
        }
        return {cell:moveTo};
    }
    return {cell:100+view[4].ant.type};//oh god
}

function foreverAlone() {
    var s0 = view[0].ant;
    var s1 = view[1].ant;
    var s2 = view[2].ant;
    var s3 = view[3].ant;
    var s5 = view[5].ant;
    var s6 = view[6].ant;
    var s7 = view[7].ant;
    var s8 = view[8].ant;
    //good
    if(!(s0 == null && s1 == null && s2 == null && s3 == null && s5 == null && s6 == null && s7 == null && s8 == null) && view[4].color == TRAIL) {
        if (view[0].color == TRAIL && !view[8].ant && view[8].color != TRAIL) return {cell: 8};
        else if (view[2].color == TRAIL && !view[6].ant && view[6].color != TRAIL) return {cell: 6};
        else if (view[6].color == TRAIL && !view[2].ant && view[2].color != TRAIL) return {cell: 2};
        else if (view[8].color == TRAIL && !view[0].ant && view[0].color != TRAIL) return {cell: 0};
        //Can't find color, or path is blocked? try diagonals regardless of color
        else if (!view[0].ant) return {cell: 0};
        else if (!view[2].ant) return {cell: 2};
        else if (!view[6].ant) return {cell: 6};
        else if (!view[8].ant) return {cell: 8};
        //Everything else failed? Stay put.
        else return {cell: 4};
    }
    //good
    if (view[4].color == TRAIL) { //If on colored square, try to move
        var totGreen = 0;
        for (var i = 0; i < 9; i++) { //Look for food
            if (view[i].food) {
                return {cell: i};
            }
            if(view[i].color == TRAIL) totGreen++;
        }
        var ret = getTrailMove();
        if(view[deRotate(ret.cell,1)].color == TRAIL && totGreen <= 4) ret.cell = deRotate(ret.cell,-1);
        else if(view[deRotate(ret.cell,-1)].color == TRAIL && totGreen <= 4) ret.cell = deRotate(ret.cell,1);
        return ret;
    } else { //If not on colored square, look for food, or set current color to 2.
        for (var i = 0; i < 9; i++) { //Look for enemies
            if (i != 4 && view[i].ant != null && !view[i].ant.friend) {
                var r = findOpenSpace(8-i,1);
                if(view[r].color == TRAIL) r = deRotate(r,1);
                return {cell: r};
            }
        }
        return {cell: 4, color:TRAIL};
    }
}

function getTrailMove() {
    if (view[0].color == TRAIL && !view[8].ant && view[8].color != TRAIL) return {cell: 8};
    else if (view[2].color == TRAIL && !view[6].ant && view[6].color != TRAIL) return {cell: 6};
    else if (view[6].color == TRAIL && !view[2].ant && view[2].color != TRAIL) return {cell: 2};
    else if (view[8].color == TRAIL && !view[0].ant && view[0].color != TRAIL) return {cell: 0};
    //Can't find color, or path is blocked? try diagonals regardless of color
    else if (!view[0].ant) return {cell: 0};
    else if (!view[2].ant) return {cell: 2};
    else if (!view[6].ant) return {cell: 6};
    else if (!view[8].ant) return {cell: 8};
    //Everything else failed? Stay put.
    else return {cell: 4};
}

function firebreak() {
    var ret = -1;
    if(findWorker(5) >= 0) {
        return {cell:8-findWorker(5)};
    }
    if(view[4].color != 5) {
        var myView = [0,0,0,0,0,0,0,0,0]
        for(var i=0; i < 9; i++) {
            myView[i] = view[i].color
            if(view[4].ant.food > 0 && view[i].food > 0) {
                myView[i] = 8;
            }
            if(view[i].ant != null && !view[i].ant.friend) return {cell:findOpenSpace(deRotate(i,2),1)};
        }
        var ret = clearAhead(myView);
        if(ret == null)
            return {cell:4,color:5};
        else {
            if(!(view[ret.cell].ant != null && view[ret.cell].ant.friend == false) && (view[4].ant.food == 0 || view[ret.cell].food == 0))
                return ret;
            return {cell:4,color:5};
        }
    }
    if(view[1].color == 5 && view[3].color == 5 && view[5].color == 5 && view[7].color == 5) {
        if(view[0].color != 8) return {cell:0,color:8};
        if(view[1].color != 8) return {cell:1,color:8};
    }
    if(view[1].color == 5 && view[7].color != 5) ret = {cell:7};
    if(view[3].color == 5 && view[5].color != 5) ret = {cell:5};
    if(view[5].color == 5 && view[3].color != 5) ret = {cell:3};
    if(view[7].color == 5 && view[1].color != 5) ret = {cell:1};
    if(view[1].color != 5 && view[3].color != 5 && view[5].color != 5 && view[7].color != 5) ret = {cell:1};
    if((view[1].color == 5 && view[7].color == 5) || (view[3].color == 5 && view[5].color == 5)) ret = {cell:0};
    var loop = 0;
    while(ret.cell >= 0 && ((view[ret.cell].food > 0 && view[4].ant.food > 0) || view[ret.cell].ant != null) && loop < 9) {
        loop++;
        ret.cell = (ret.cell + 2) % 9;
    }
    if(loop < 9 && ret.cell >= 0) return ret;
    return {cell:4};
}

//7,6,4,2,3
//O7,D2

function highwayRobbery() {
    var move = basicHighwayMove();
    if(move.cell >= 0 && view[move.cell].ant != null) {
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + (view[4].color%2==0?1:HIGHWAY_COLORS.length);
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                return {cell:i};
            }
        }
    }
    return move;
}

function basicHighwayMove() {
    var isQueen = view[4].ant.type == 5;
    if(isHighwayCenter()) {
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                if(view[i].ant == null)
                    return {cell:i};
                else {
                    return {cell:deRotate(i,1)};
                }
            }
        }
    }
    else {
        if(view[4].color == 7) {
            //move diagonal to yellow (2)
            for(var i=0;i<9;i++) {
                if(i != 4 && i % 2 == 0 && view[i].color == 2) {
                    return {cell:i};
                }
            }
        }
        else {
            //move orthogonal to blue (7)
            for(var i=0;i<9;i++) {
                if(i % 2 == 1 && view[i].color == 7) {
                    //try ortho yellow first
                    for(var j=0;j<9;j++) {
                        if(j % 2 == 1 && view[j].color == 2 && areAdjacent(i,j))
                            return {cell:j};
                    }
                    return {cell:i};
                }
            }
            //if orthogonal blue doens't exist...
            //...try diagonal to magenta
            for(var i=0;i<9;i++) {
                if(i != 4 && i % 2 == 0 && view[i].color == 3) {
                    return {cell:i};
                }
            }
            if(view[4].color != 2) {
                //...try diagonal blue
                for(var i=0;i<9;i++) {
                    if(i % 2 == 0 && view[i].color == 7)
                        return {cell:i};
                }
            }
            //...and orthogonal yellow
            for(var i=0;i<9;i++) {
                if(i % 2 == 1 && view[i].color == 2)
                    return {cell:i};
            }
        }
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                return {cell:i};
            }
        }
    }
    return {cell:-1};
}

function isOnHighway() {
    var match = 0;
    var nxt = HIGHWAY_COLORS[(HIGHWAY_COLORS.indexOf(view[4].color)+1) % HIGHWAY_COLORS.length];//4
    var prv = HIGHWAY_COLORS[(HIGHWAY_COLORS.indexOf(view[4].color)+HIGHWAY_COLORS.length-2) % HIGHWAY_COLORS.length];//6
    for(var i=0;i<9;i++) {
        if(HIGHWAY_COLORS.indexOf(view[i].color) >=0 && (i == 4 || view[i].color != view[4].color))
            match++;
    }
    if(match >= 5) {
        //7,6,4,2,3


        if((view[1].color == nxt && view[7].color == prv)||(view[1].color == prv && view[7].color == nxt) || 
            (view[3].color == nxt && view[5].color == prv)||(view[3].color == prv && view[5].color == nxt)) {
            return true;
        }
        if((view[1].color == view[8].color && (view[1].color == nxt || view[1].color == prv))||(view[1].color == view[6].color && (view[1].color == nxt || view[1].color == prv)) || 
            (view[3].color == view[2].color && (view[3].color == nxt || view[3].color == prv))||(view[3].color == view[8].color && (view[3].color == nxt || view[3].color == prv))) {
            return true;
        }
        if((view[0].color == view[7].color && (view[0].color == nxt || view[0].color == prv))||(view[2].color == view[7].color && (view[2].color == nxt || view[2].color == prv)) || 
            (view[0].color == view[5].color && (view[0].color == nxt || view[0].color == prv))||(view[6].color == view[5].color && (view[6].color == nxt || view[6].color == prv))) {
            return true;
        }
        if(isHighwayCenter()) {
            return true;
        }
    }
    return false;
}

function isHighwayCenter() {
    if(HIGHWAY_COLORS.indexOf(view[4].color) >=0 && (((view[0].color != view[8].color || view[2].color != view[6].color) && view[4].ant.type == 1) || (view[0].color != view[8].color && view[2].color != view[6].color))){
        var m1 = view[1].color == view[7].color;
        var m2 = view[2].color == view[8].color;
        var m3 = view[0].color == view[6].color;
        var m4 = view[0].color != 1 && view[2].color != 1;
        if((m1?1:0)+(m2?1:0)+(m3?1:0) >= 2 && m4) {
            if(view[3].color != view[5].color && ((view[2].color != view[5].color && view[8].color != view[5].color) || view[4].ant.type == 1) && ((view[3].color != view[0].color && view[3].color != view[6].color) || view[4].ant.type == 1)) {
                return true;
            }
        }
        m1 = view[3].color == view[5].color;
        m2 = view[0].color == view[2].color;
        m3 = view[6].color == view[8].color;
        m4 = view[0].color != 1 && view[6].color != 1;
    //good
        if((m1?1:0)+(m2?1:0)+(m3?1:0) >= 2 && m4) {
            m1 = view[1].color != view[7].color;
            m2 = (view[0].color != view[1].color && view[1].color != view[2].color);
            m3 = (view[6].color != view[7].color && view[7].color != view[8].color);
            if(m1 && m2 && m3) {
                return true;
            }
            if(view[4].ant.type == 1 && ((m1?1:0)+(m2?1:0)+(m3?1:0)) >= 2) {
                return true;
            }
        }
    }
    return false;
}

function deRotateSide(m, amt) {
    return deRotate(m,amt*2);
}

/**Positive amount is clockwise**/
function deRotate(m, amt) {
    var rotationsCW = [1,2,5,8,7,6,3,0];
    var rotationsCCW = [3,6,7,8,5,2,1,0];
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m;
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8];
    amt = -amt;
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8];
}

function areAdjacent(A, B) {
    if(A == 4 || B == 4 || A == B) return true;
    if(A % 2 == 0 && B % 2 == 0) return false;
    if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A);
    if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A);
    if(A == 0 && (B == 1 || B == 3)) return true;
    if(A == 2 && (B == 1 || B == 5)) return true;
    if(A == 6 && (B == 3 || B == 7)) return true;
    if(A == 8 && (B == 5 || B == 7)) return true;
    return false;
}

function findFirstTrail() {
    var pos = 0;
    var b = 0;
    while(view[pos].color != TRAIL && b < 8) {
        pos=deRotate(pos,1);
        b++;
    }
    return pos;
}

function clearAhead(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(view[i].color == 5) c++;
        if(view[i].color == 5 && i%2 == 0) c+=10;
    }
    if(c == 2) {
        if(view[0].color == 5 || view[2].color == 5 || view[6].color == 5 || view[8].color == 5) {
            return {cell:4,color:5};
        }
        if(view[0].ant == null)
            return {cell:0};
        if(view[2].ant == null)
            return {cell:2};
        if(view[6].ant == null)
            return {cell:6};
        if(view[8].ant == null)
            return {cell:8};
    }
    c = 0;
    sides[4] = 0;
    var toMatch =[{state:[1,1,1,
                          2,0,2,
                          0,1,0]},
                 {state:[0,2,1,
                         1,0,1,
                         0,2,1]},
                 {state:[0,1,0,
                         2,0,2,
                         1,1,1]},
                 {state:[1,2,0,
                         1,0,1,
                         1,2,0]}];
    for(var m=0;m<4;m++) {
        var score=0;
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(sides[j] == 5 && toMatch[m].state[j] == 1) {
                    score++;
                }
                if(sides[j] != 5 && (toMatch[m].state[j] == 0 || toMatch[m].state[j] == 2)) {
                    score++;
                }
                if(sides[j] == 5 && toMatch[m].state[j] == 2) {
                    score--;
                }
            }
        }
        if(score >= 6) {
            var clearOrder=[1,0,2];
            for(var r=0;r<clearOrder.length;r++) {
                var s = deRotateSide(clearOrder[r],m);
                if(view[s].color == 5) {
                    if(view[s].ant == null)
                        return {cell:s,color:8};
                    else
                        return {cell:4};
                }
            }
        }
    }
    return null;
}

function findOpenSpace(pos, dir) {
    if(pos > 8 || pos < 0) return pos;
    var b = 0;
    while(view[pos].ant != null && b < 8) {
        pos=deRotate(pos,dir);
        b++;
    }
    return pos;
}

function getHighestWorker() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type > r) r = view[i].ant.type;
        }
    }
    return r;
}

function getNumWorkers(type) {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type == type) r++;
        }
    }
    return r;
}

function findWorker(type) {
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type == type) return i;
        }
    }
    return -1;
}

422% Beende
422%, weil das Update am 4/22 war. Dieser Witz hat zu lange gedauert

Invalid move detection: check
Turning: check
Respawning Workers, wenn sie getrimmt werden: most check
Vermeiden, auf anderen Ameisen festzustecken, die ...

Update 8/1

Dreht sich doppelt so oft, was zu ca. 15% mehr gesammelten Nahrungsmitteln führt

Update 8/2

Robustheit hinzugefügt, um nicht hängen zu bleiben.

Update 9/9

Code hinzugefügt, um Arbeiter 2 und 3 zu respawnen, wenn dieser Arm fehlt.

Update 9/12

Vorheriges Update wiederherstellen. Das Segelflugzeug könnte sich mit dem neu geborenen Arm vorwärts bewegen, aber die Arbeiter würden sich beim Versuch, sich zu drehen, in Unordnung verhalten, und es würde auseinandergehen.

Zusätzlich zur Behebung dieses Problems wurde der Respawn-All-Worker-Code diskriminierender gestaltet: Er reduziert Kollisionen mit "verlorenen" Arbeitern in der Nähe und verhindert das Respawn vollständig über 60 Lebensmittel: 60 Lebensmittel reichen aus, um in den Top 3 zu punkten Meistens ist dies besser, als 4 Lebensmittel für Arbeiter auszugeben, die wahrscheinlich in kurzer Zeit wieder entkleidet werden: Nur sehr wenige Ameisen erzielen jemals mehr als 60 Punkte, es sei denn, sie überbieten die Fähigkeiten von Glider. Außerdem wurde ein Code hinzugefügt, mit dem verlorene Arbeiter andere Ameisenspuren durcheinander bringen können.

Einige andere Fehlerbehebungen in Bezug auf Situationen, in denen das Segelflugzeug stecken bleiben konnte, selbst wenn ein gültiger Zug ausgeführt wurde (z. B. dass die Königin einen Arm stecken ließ und sie später wieder auftauchte).

Die Farbe wurde neu konfiguriert, um eine definierte Variable zu verwenden, und die Farbe des Pfads wurde von gelb in grün geändert, um zu verhindern, dass Bergleute im Schienensystem einer Schiene "eingeklemmt" werden (Solo Queen wird ständig umgeleitet).

Ein Teil des neuen Codes stammt aus meinem anderen Eintrag, Black Hole .

Update 9/13

Es wurde ein Fehler im Code für die Auslagerung von Spawns behoben. Paar von 2s Griff wurde in TRAILs umgewandelt.

Update 1/21

  • Der Schirm kann sich nun nach links drehen (Animation ausstehend).
  • Das frühe Spiel wird beschleunigt, indem ein Arbeiter laicht, sobald Futter verfügbar ist.
  • Einarmiges Gleiten kann auch beide Richtungen drehen.
  • Die Schwelle für "Arbeiter nicht respawnen" wurde angehoben
  • "Verschwenden Sie keine Lebensmittel, die versuchen, wieder aufzutauchen, und scheitern", reduziert von %5bis%3

Update 1/25

  • Einige Deadlock-Szenarien wurden behoben und die Effizienz in einigen Spielen um einige Punkte gesteigert.

Update 2/20

  • Verschiedene Edge-Case-Explosions- / Deadlock-Szenarien wurden behoben, wodurch die Konfigurationen mit 5 und 3 Ameisen stabiler wurden. Verschiedene Essensanordnungen können dazu führen, dass der Glider verwirrt wird und entweder festsitzt (sich wiederholt hin und her dreht) oder explodiert.
    • Eine Explosion war die Folge davon, dass sich die Kehrameise im letzten Schritt einer Rotation auf Nahrung bewegte und dann davon ausging, dass "Scheiße, ich habe Nahrung, opfere mich, um sie der Königin zu bringen", wenn es nicht nötig war (Standardbewegung war gültig) ).
    • Eine Explosion war die Folge davon, dass die Zelle unter der Königin bereits TRAIL(grün) war und eine der Bewegungen der Königin " foreverAlone()Stillstand " verursachte (die durch den Rückfall dazu geführt wurde, dass sich die Königin von der Gruppe entfernte. Dies wurde behoben, indem eine stationäre Wache modifiziert wurde Die Farbe der Zelle der Königin, kurz bevor die Königin ihre Aktion ausführt.
    • Wünschenswerter, als den Code der Königin zu korrigieren, da dies uns ermöglicht, Deadlocks zu entgehen, die andernfalls auftreten könnten foreverAlone(), wenn wir den Fallback beibehalten und die Königin herausholen, selbst wenn wir ihre Flügel nicht retten können.
  • foreverAlone()Die Funktion wurde so aktualisiert, dass sie nur dann auf Nahrung wechselt, wenn die Zelle unter der Königin TRAIL(grün) ist. Sehr geringer Wirkungsgradverlust.
  • Glider Rebuild MehrBetter findet eine benachbarte grüne Zelle, eine Änderung in findFirstYellow()und deren Verwendung. In Kombination mit der vorherigen Kugel werden zuvor gesehene Zellen und / oder der Ort einer kürzlich erfolgten Explosion besser vermieden: Ein neu geformter Segelflugzeug bewegt sich - in den meisten Fällen - in eine Richtung, die nicht dem Anflug der Königin entspricht Flugbahn.
    • Funktion umbenannt: Gelb war lange Zeit nicht mehr die Farbe der Königin.
    • Die Funktion hat auch die "erste Zelle der Spur" aufgrund einer invertierten Logikprüfung falsch lokalisiert: while ==stattwhile !=

Update 2/25

  • Mehr Fehlerbehebungen bei Explosionen / Abstürzen / Deadlocks, die die Leistung insgesamt verbessern.
  • Einige Änderungen am LightSpeed-ähnlichen Verhalten, die von Alion vorgeschlagen wurden (aufgrund des Verhaltens seines eigenen Eintrags ). Erhöht im Allgemeinen die Lebensmittelsammlung, vermeidet jedoch auch bestimmte Deadlock-Szenarien.

Update 3/11: Kaputte Segelflugzeugflügel, die sich auf dem Highway befinden, wechseln zu einem Vampir-ähnlichen "stehlen-Essen" -System. Vor allem wird dies nicht viel bedeuten, es sei denn, die Königin selbst ist allein auf dem Highway, der mit seinen riesigen Mengen an Grün einen unmöglichen Ort zur Flucht bietet.

Arbeiter 1 wird versuchen, Reparaturen auf dem Hauptkorridor der Autobahn durchzuführen und einen Best-Rate-Abbruch durchzuführen, wenn er sich an einer Stelle der Autobahn befindet, die NICHT auf der Schiene liegt (anstatt ein falsches Zentrum zu reparieren). Reist in die gleiche Richtung wie die Königin (entgegengesetzt zu Arbeiter 2, 3 und 4).

Die Arbeiter 2, 3 und 4 versuchen, auf der Highway Queen einen Drive-by-Diebstahl durchzuführen, und kehren dann zu ihrer eigenen Königin zurück, die Arbeiter hervorbringt, wenn sie ...

  • kann keine ihrer eigenen Arbeiter sehen
  • sitzt auf Magenta
  • hat Nahrung zwischen den Schwellen (mit weichen Grenzen auf beiden Seiten)
  • modifiziert durch einen einfachen Zufall

Dies vermeidet, dass zu viele Arbeiter geschaffen werden (ergibt ~ 100) und wenn eine Reihe von beladenen Arbeitern zurückkehrt, gibt es ein Nettoernährungseinkommen (aufgrund des Nicht-Laichens, wenn man die eigenen Arbeiter sieht), was schließlich dazu führt, dass Glider die oberste Schwelle überschreitet und massiv laicht reduziert) neue Arbeiter.

Dies war eine Taktik, die ich geplant hatte, um an Vampire zu schreiben, aber Vampires erster Versuch, die Highway Queen zu blockieren und sie nur trocken zu bluten, funktionierte, sodass es keinen Anreiz gab, dieses Verhalten zu erzeugen. Der On-Highway-Erkennungscode wurde von Grund auf neu geschrieben und unterscheidet sich von Vampire, obwohl die Spiegelerkennung (IsHighwayCenter) aufgrund der Einfachheit des Musters sehr ähnlich ist.

Update 3/16

Abgesehen von zwei kleineren Fehlerkorrekturen (siehe Bearbeitungsverlauf) wurde die Forever Alone-Funktion (ursprünglich gestohlen von ...?) Dahingehend optimiert, dass ein Zurückverfolgen vermieden wird, sodass die Königin durchschnittlich mehr Zellen beobachten kann, wenn sie alleine ist.

Fallback-Bewegungsmethode hinzugefügt, wenn die highwayRobbery()Funktion keine gültige Bewegung zurückgibt, wenn sich die Ameise im Autobahnzentrum befindet (und keine andere Logik dies auf einen vernünftigen Wert eingestellt hat). Wenn das fehlschlägt, führen Sie die Ameisen ihre Standardfliegen Logik.

Kleine Verbesserungen

  • Queen versucht nicht mehr zu laichen, als sie feststellt, dass sie auf der Autobahn ist
  • Königin verbrennt keine Kalorien mehr, wenn sie versucht, eine Segelflugformation zu vollenden, die niemals vollendet wird (bringt Arbeiter 4 hervor, Arbeiter 4 sieht Arbeiter 1 nicht, Arbeiter 4 bewegt sich weg, wiederholen)
  • Die Food-Modulo-Werte wurden so angepasst, dass sie mit dem Highway-Farbzyklus übereinstimmen.
  • Es wurde ein Fehler behoben, durch den ein Deadlock verursacht wurde
  • Kleinere Korrekturen am isOnHighway()Scheck
  • Zwangsameisen bewegen sich auf der Autobahn auf ein orthogonales Gelb, bevor sie auf eine orthogonale blaue Zelle übergehen.

Update 3/26

Weitere kleine Verbesserungen.

Update 4/21

Kleinere Korrekturen.


Lose basierend auf Steamroller .

Sammelt 4 Lebensmittel und hinterlässt eine gelbe Spur. Sobald es vier Nahrungsmittel hat, produziert es einen von jedem Arbeitertyp um sich herum. Dann beginnt es mit maximalem Schub zu gleiten. Das Bewegen der Animation am oberen Rand dauert 1 Spielzug: Da Arbeiter 1 zuerst produziert wird, führt er zuerst 2, 3 und 4 aus, bevor er den Zug mit der Königin beendet. Das Drehen der Animation dauert 2 Runden, die kurzen Pausen sind, wenn die Königin spielt, {cell:4}und das Essen bewegt sich, wenn die Arbeiter 3, 4 und die Königin spielen {cell:4}, um sich auf das weitere Gleiten vorzubereiten.


Ich mag diese Bot-Idee
Destructible Lemon

1
@DestructibleLemon Danke! Ich dachte an Steamroller / Piercer und dachte bei mir: "Moment mal, Königin, ich kann doch sicher fünf Ameisen zum Spielen bringen ..." Er griff nach einem Stück Papier, riss ein paar Quadrate ab und fing an, sie herumzubewegen. Es hat funktioniert, also habe ich einen Code geschrieben. Es mussten viele Fehlerbehandlungen und Fehlerbehebungen für Randfälle durchgeführt werden (der anfängliche Bot blieb hängen oder verbrauchte alles, was er konnte und was getan werden konnte. Durch Hinzufügen der Abbiegeoperation wurden viele Randfallszenarien eingeführt, in denen die Formation unterbrochen wurde und ein Absturz auftrat ).
Draco18s

@trichoplax bis es gegen eine andere Ameise kracht?
Destructible Lemon

@DestructibleLemon eeeehhhh ... vielleicht. Zumindest wird es nicht disqualifiziert ...
Draco18s

3
@ KZhang Oh, ich habe. Suchen Sie nach den Kommentaren "// Versuch zu drehen"
Draco18s

13

Windmühle

Hier ist eine Windmühle ... die auf den Mann aus La Mancha wartet (oder auf den Vampir aus La Mancha?), Der hochfahren wird, um ihn herunterzubringen.

Windmühle, frühe Mittelstufe des Spiels

Ich versuchte herauszufinden, wie weit das Design von Miners on a Rail - das nun in der Editierhistorie dieser Antwort enthalten ist - vorangetrieben werden konnte, da festgestellt wurde, dass die Trittsteine ​​besser auf andere Wände der Minenschächte gestrichen werden sollten . Und dann ist es einfach gewachsen und hat sich weiterentwickelt ...

Während unsere Schwergewichtsschiene und die leichte Schaftgeometrie nahezu identisch sind und die Schienenmuster so ähnlich aussehen, dass einige Ameisen die eine für die andere halten, wurde die Implementierung von Grund auf neu geschrieben und es wurde ein neuer Schlüssel eingeführt Idee. Untersuche die Nabe, um zu sehen, wie sie fräst .

var AJM=1;var ASM=2;var AE=3;var ASF=4;var AQ=5;var RW=true;var EFCO=false;var THC=1;var TH0=0;var TH1=15;var TH2=17;var TH3=67;var TH4=120;var TH5=390;var THX=15;var THFCO1=9;var THFCO2=26;var THFCO3=75;var RM1=7;var RD1=4;var RM2=19;var RD2=THX;var PW=1;var PY=2;var PP=3;var PC=4;var PR=5;var PG=6;var PB=7;var PK=8;var LN=0;var LCLR=PW;var LT=PB;var LLSF=PP;var LA=PP;var LRL0=PC;var LRL1=PG;var LRL2=LRL0;var LRM0=PR;var LRM1=PB;var LRM1_WRP=PK;var LRM2=PG;var LRR0=PG;var LRR1=PW;var LRR1U=PR;var LRR1V=PY;var LRR1X=PK;var LRR2=PY;var LMX_M0=LCLR;var LMX_M1IN=PC;var LMX_M1OUT=PY;var LMX_M2IN=PP;var LMX_M2OUT=PR;var LMX_M3IN=PB;var LMX_M3OUT=PK;var LMS_WRP=PK;var LMR0=PK;var LML1=PY;var LMR2=PR;var LML3=PC;var LMMF=PG;var LMMH=PP;var LG3=PK;var LG4=PR;var LG5=PK;var LG6=PB;var LP0=LCLR;var LPB=PC;var LPG=PY;var LPG1=PR;var LPX=PP;var FALSE_X9=[false,false,false,false,false,false,false,false,false];
var UNDEF_X9=[undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined];
var QCPERD=6;var LCL_QC_RESET=LCLR;var LCRQC=[PY,PP,PC,PR,PG,PK];var LCRQCVAL=Array.from(FALSE_X9);var LCRQC_VALUE=Array.from(UNDEF_X9);for (var i=0; i<QCPERD; i++){LCRQCVAL[LCRQC[i]]=true;LCRQC_VALUE[LCRQC[i]]=i;}var SCPERD=7;var LCL_SC_RESET=LCLR;var LCRSC=[PY,PP,PC,PR,PG,PB,PK];var LCRSCVAL=Array.from(FALSE_X9);var LCRSC_VALUE=Array.from(UNDEF_X9);for (i=0; i<SCPERD; i++){LCRSCVAL[LCRSC[i]]=true;LCRSC_VALUE[LCRSC[i]]=i;}var LCRPHR=Array.from(FALSE_X9);LCRPHR[LPG]=true;LCRPHR[LPG1]=true;var LCRPHASES=Array.from(LCRPHR);LCRPHASES[LPX]=true;var LCRGRM1=Array.from(FALSE_X9);LCRGRM1[LRM1]=true;LCRGRM1[LRM1_WRP]=true;var LCRGRM_ALL=Array.from(FALSE_X9);LCRGRM_ALL[LRM0]=true;LCRGRM_ALL[LRM1]=true;LCRGRM_ALL[LRM1_WRP]=true;LCRGRM_ALL[LRM2]=true;var LCRGRR1_OUT=Array.from(FALSE_X9);LCRGRR1_OUT[LRR1V]=true;LCRGRR1_OUT[LRR1X]=true;var LCRGRR1B=Array.from(LCRGRR1_OUT);LCRGRR1B[LRR1U]=true;var LCRGRR1=Array.from(LCRGRR1B);LCRGRR1[LRR1]=true;var LCRMX_IO=Array.from(FALSE_X9);LCRMX_IO[LMX_M1IN]=true;LCRMX_IO[LMX_M1OUT]=true;LCRMX_IO[LMX_M2IN]=true;LCRMX_IO[LMX_M2OUT]=true;LCRMX_IO[LMX_M3IN ]=true;LCRMX_IO[LMX_M3OUT]=true;var LCRMX=Array.from(LCRMX_IO);LCRMX[LMX_M0]=true;var LCRMX_IN=Array.from(FALSE_X9);LCRMX_IN[LMX_M1IN]=true;LCRMX_IN[LMX_M2IN]=true;LCRMX_IN[LMX_M3IN ]=true;var LCRMX_OUT=Array.from(FALSE_X9);LCRMX_OUT[LMX_M1OUT]=true;LCRMX_OUT[LMX_M2OUT]=true;var LCRMM_FOOD=Array.from(FALSE_X9);LCRMM_FOOD[LCLR]=true;LCRMM_FOOD[LMMF]=true;var LCRMM_HOME=Array.from(FALSE_X9);LCRMM_HOME[LCLR]=true;LCRMM_HOME[LMMH]=true;var LCRMS=Array.from(FALSE_X9);LCRMS[LCLR]=true;LCRMS[LMS_WRP]=true;var LCRFRLL0=Array.from(FALSE_X9);LCRFRLL0[LCLR]=true;LCRFRLL0[LMR0]=true;LCRFRLL0[LMR2]=true;LCRFRLL0[LRM0]=true;LCRFRLL0[LRM2]=true;var LCRFRLL1=Array.from(FALSE_X9);LCRFRLL1[LCLR]=true;LCRFRLL1[LMR0]=true;LCRFRLL1[LMR2]=true;LCRFRLL1[LRR0]=true;LCRFRLL1[LRM1]=true;LCRFRLL1[LRR2]=true;var LCRFRLL2=Array.from(FALSE_X9);LCRFRLL2[LCLR]=true;LCRFRLL2[LMR0]=true;LCRFRLL2[LMR2]=true;LCRFRLL2[LRM0]=true;LCRFRLL2[LRR1V]=true;var TN=8;var POSC=4;var NOP={cell:POSC};var AIMU=1;var AIML=3;var AIMR=5;var AIMD=7;var FWD_CELLS=[[ true,true,false,true,true,false,false,false,false ],[ true,true,true,true,true,true,false,false,false ],[ false,true,true,false,true,true,false,false,false ],[ true,true,false,true,true,false,true,true,false ],[ true,true,true,true,true,true,true,true,true ],[ false,true,true,false,true,true,false,true,true ],[ false,false,false,true,true,false,true,true,false ],[ false,false,false,true,true,true,true,true,true ],[ false,false,false,false,true,true,false,true,true ]];var PTNOM=-9;var PTHOME=[LRM0,LRL0,LRM0,LRL0,LN,LRL0,LN,LN,LRM0];var PTGARDEN=[LG6,LG5,LG4,LN,LN,LG3,LN,LRL0,LRL1];var PTFRM0=[LRL1,LRM1,LCRGRR1,LRL0,LRM0,LRR0,LRL2,LRM2,LRR2];var PTFRM1=[LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1,LRL0,LRM0,LRR0];var PTFRM2=[LRL0,LRM0,LRR0,LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1];var PTGRM0=[LRL1,LCRGRM1,LCRGRR1,LRL0,LRM0,LRR0,LRL2,LRM2,LRR2];var PTGRM1=PTFRM1;var PTGRM2=PTFRM2;var PTGRM2B=[LRL0,LRM0,LRR0,LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1B];var PTGRM1_WRP=[LRL2,LRM2,LRR2,LRL1,LRM1_WRP,LRR1X,LRL0,LRM0,LRR0];var PTFRL0=[LCRFRLL1,LRL1,LRM1,LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2];var PTFRL1=[LCRFRLL2,LRL2,LRM2,LCRFRLL1,LRL1,LCRGRM1,LCRFRLL0,LRL0,LRM0];var PTFRL0H=[LN,LRL1,LRM1,LN,LRL0,LRM0,LN,LN,LN];var PTFRL1G=[LCRFRLL2,LRL2,LRM2,LG3,LRL1,LCRGRM1,LCRPHASES,LRL0,LRM0];var PTFRL2=[LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2,LCRFRLL1,LRL1,LCRGRM1];var PTGRL0=[LCRFRLL1,LRL1,LCRGRM1,LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2];var PTGRL1=PTFRL1;var PTGRL2=PTFRL2;var PTGRR0=[LCRGRM1,LCRGRR1,LCLR,LRM0,LRR0,LMR0,LRM2,LRR2,LCRMX];var PTGRR2=[LRM0,LRR0,LMR0,LRM2,LRR2,LCRMX,LCRGRM1,LCRGRR1,LCLR];var PTGRR1=[LRM0,LCRGRM1,LRM2,LRR0,LCRGRR1,LRR2,LMR0,LCLR,LCRMX];var PTMS0R_IN=[LRR0,LRR1U,LRR2,LMR0,LCLR,LCRMX_IN,LCLR,LCLR,LML1];var PTMS0R_OUT=[LRR0,LRR1U,LRR2,LMR0,LCLR,LCRMX_IO,LCLR,LCLR,LML1];var PTMS0R_OUT1=[LRR0,LCRGRR1_OUT,LRR2,LMR0,LCLR,LCRMX,LCLR,LCLR,LML1];var PTMS0=[LCLR,LCRMM_HOME,LML3,LMR0,LCRMM_FOOD,LCLR,LCLR,LCLR,LML1];var PTMS1=[LMR0,LCRMM_HOME,LCLR,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS2=[LCLR,LCRMM_HOME,LML1,LMR2,LCRMM_FOOD,LCLR,LCLR,LCLR,LML3];var PTMS3=[LMR2,LCRMM_HOME,LCLR,LCLR,LCRMM_FOOD,LML3,LMR0,LCLR,LCLR];var PTMS1_IN=[LMR0,LCRMM_HOME,LCRMX_IN,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS1_IO=[LMR0,LCRMM_HOME,LCRMX_IO,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS0_OUT=[LCLR,LCLR,LML3,LMR0,LCLR,LCRMX_IO,LCLR,LCLR,LML1];var PTMS0_WRAPPING=[LCLR,LCRMM_HOME,LML3,LMR0,LCRMM_FOOD,LCRMS,LRL0,LRL1,LRL2];var PTGRL1_WRP=[LMR0,LCLR,LMS_WRP,LRL0,LRL1,LRL2,LRM0,LRM1_WRP,LRM2];var PTMS0FL=[LMMH,LML3,LN,LMMF,LCLR,LN,LCLR,LML1,LN];var PTMS1FL=[LMMH,LCLR,LN,LMMF,LML1,LN,LCLR,LCLR,LN];var PTMS2FL=[LMMH,LML1,LN,LMMF,LCLR,LN,LCLR,LML3,LN];var PTMS3FL=[LMMH,LCLR,LN,LMMF,LML3,LN,LCLR,LCLR,LN];var PTMS0FR=[LN,LCLR,LMMH,LN,LMR0,LMMF,LN,LCLR,LCLR];var PTMS1FR=[LN,LMR0,LMMH,LN,LCLR,LMMF,LN,LMR2,LCLR];var PTMS2FR=[LN,LCLR,LMMH,LN,LMR2,LMMF,LN,LCLR,LCLR];var PTMS3FR=[LN,LMR2,LMMH,LN,LCLR,LMMF,LN,LMR0,LCLR];var CCW=[6,7,8,5,2,1,0,3,6,7,8,5,2,1,0,3,6,7,8,5,2,1];
var xn=-1;var fwdWrong=[];var rearWrong=[];var here=view[POSC];var mC=here.color;var myself=here.ant;var mT=myself.type;var mF=myself.food;var mS=(mT==AE||(mT!=AQ&&mF>0));if (EFCO&&(mT==AQ)){if (mF<=THFCO1){QCPERD=5;} else if (mF<=THFCO2){QCPERD=4;} else if (mF<=THFCO3){QCPERD=5;}}var dOK=[true,true,true,true,true,true,true,true,true];
var uo=true;var sL=[0,0,0,0,0,0,0,0,0];var sD=[0,0,0,0,0,0,0,0,0];var sN=[0,0,0,0,0,0,0,0,0];var sT=[0,0,0,0,0,0,0,0,0];var fdL=0;var fdD=0;var fdT=0;sT[mC]++;for (i=0; i<TN; i+=2){var cell=view[CCW[i]];sD[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdD++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}for (i=1; i<TN; i+=2){var cell=view[CCW[i]];sL[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdL++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}var aF=[0,0,0,0,0,0];var aLF=[0,0,0,0,0,0];var aUF=[0,0,0,0,0,0];var fT=0;var mQ=0;var aE=[0,0,0,0,0,0];var aLE=[0,0,0,0,0,0];var aUE=[0,0,0,0,0,0];var eT=0;for (i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant){if (cell.ant.friend){aF[cell.ant.type]++;fT++;if (cell.ant.type==AQ){xn=i&6;mQ=i&1;}if (cell.ant.food>0){aLF[cell.ant.type]++;} else {aUF[cell.ant.type]++;}} else {aE[cell.ant.type]++;eT++;if (cell.ant.food>0){aLE[cell.ant.type]++;} else {aUE[cell.ant.type]++;}}dOK[CCW[i]]=false;uo=false;}}switch (mT){case AQ:return (rQSs());case ASF:if (mQ==1){return (rSSs());} else if (aF[AQ]>0){return (rGSs());} else {return (rLSSy());}case AE:return (rESs());case AJM:case ASM:if (aE[AQ]>0){return (rDSs());} else if (mF>0){return (rLSs());} else {return (rUSs());}default:return NOP;}function rQSs (){switch (aF[ASF]){case 0:return (rQScrSy());case 1:for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.type==ASF){xn=i&6;if (i&1){if (mF<=THX){return (rQLsSy());} else {return (rQLvSy());}} else {return (rQSgSy());}}}break;case 2:for (i=0; i<TN; i+=2){var cell0=view[CCW[i]];var cell1=view[CCW[i+1]];if ((cell0.ant&&(cell0.ant.type==ASF))&&(cell1.ant&&(cell1.ant.type==ASF))){xn=i;return (rQOSy());}}return (rQCSy());default:return (rQCSy());}return NOP;}function rSSs (){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&(view[CCW[xn+3]].ant.type==ASF)){return (rSOSy());} else if (view[CCW[xn+1]].ant.food<=THX){return (rSLSy());} else {return (rSESy());}}function rGSs (){var secCell=view[CCW[xn+7]];if (secCell.ant&&(secCell.ant.friend==1)&&(secCell.ant.type==ASF)){return (rGOSy());} else {return (rGSSy());}return NOP;}function rESs (){if (aF[AQ]>0){return (rEHyS());} else if (aF[AJM] +aF[ASM]>0){return (rEBRSy());} else {return (rEASy());}return NOP;}function rDSs(){if (aF[AQ]>0){return (rDHSy());} else {for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&(view[CCW[i]].ant.type==AQ)){if (i&1){if ((view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&view[CCW[i+2]].ant&&view[CCW[i+2]].ant.friend)||(view[CCW[i-1]].ant&&view[CCW[i-1]].ant.friend&&view[CCW[i+6]].ant&&view[CCW[i+6]].ant.friend)||(view[CCW[i+2]].ant&&view[CCW[i+2]].ant.friend&&view[CCW[i+6]].ant&&view[CCW[i+6]].ant.friend)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};}}} else {if (view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
view[CCW[i+7]].ant&&view[CCW[i+7]].ant.friend){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if (dOK[CCW[i+6]]){return {cell:CCW[i+6]};} else if (dOK[CCW[i+2]]){return {cell:CCW[i+2]};}}if ((i<=2)&&dOK[CCW[i+7]]){return {cell:CCW[i+7]};}if ((i>=4)&&dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}if (fT==0){if (view[CCW[i]].color!=PP){return {cell:CCW[i],color:PP};} else if (mC!=LCLR){return {cell:POSC,color:LCLR};}}}}}return NOP;}function rUSs (){if ((aF[AQ]>0)&&!LCRQCVAL[view[CCW[xn+mQ]].color]){return (rUHSy());} else if ((fT+eT>=4)&&(aF[AJM]+aF[ASM] +aF[AE]>=3)){return (rUCRSy());} else if (aF[AQ]>0){return (rUHSy());} else if (aF[ASF]>0){if (aF[ASF]>1){return (rM2R1Sy());} else {return (rURHSy());}} else if (aF[AE]>0){return (rUBRSy());} else if (spcRL1()){return (rULRL1Sy());} else if (spcRR0()){return (rULRR0Sy());} else if (spcRR2()){return (rULRR2Sy());} else if (spcMS()){return (rUDSSy());} else if (spcRL02()){return (rULRL02Sy());} else if (spcRM()){return (rUTRRSy());} else if (spcRR1()){return (rUPSSy());} else if (spcMS0R()){return (rUESSy());} else if (spcMS0W()){return (rUSWSy());}return (rLostMSy(true));}function rLSs (){if ((fT>=3)&&(fT+eT>=4)){return (rLCRSy());} else if (aF[ASF]>0){if (aF[ASF]>1){return (rM2R1Sy());} else {return (rLRHSy());}} else if (spcMFL()){return (rLLLWSy());} else if (spcMFR()){return (rLLRWSy());} else if (spcRL1()){return (rLLRL1Sy());} else if (spcRR0()){return (rLLRR0Sy());} else if (spcRR2()){return (rLLRR2Sy());} else if (spcMS0R()){return (rLLSSy());} else if (spcMS0ROut()){return (rLLVSSy());} else if (spcMS()&&(aF[AE]==0)){return (rLASSy());} else if (spcRL02()){return (rLLRL02Sy());} else if (spcRM()){return (rLTRRSy());} else if (spcRR1()){return (rLDSSy());} else if (aF[AE]>0){return (rLFRSy());}return (rLostMSy(true));}function rQScrSy(){if (uo){if (fdT>0){return (rQSETc());} else if (mF>=THC){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+1],type:ASF};}}return {cell:1,type:ASF};} else if (mC!=LT){if ((mC==LCLR)||(sN[LCLR]>=TN-1)){return {cell:POSC,color:LT};} else {return (rQSTCTc());}} else if ((sN[LCLR]>=4)&&(sN[LT]==1)){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+4]};}}} else if (sN[LCLR]==TN){return {cell:0};} else {return (rQSATc());}} else {if ((fdT>0)&&(eT>0)&&(eT==aE[AQ])){return (rQSSTc());} else {return (rQSEvTc());}}return NOP;}function rQSgSy(){if (fdT>0){if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {for (var i=2; i<TN-1; i++){if (dOK[CCW[xn+i]]&&(view[CCW[xn+i]].food>0)){return {cell:CCW[xn+i]};}}for (var i=2; i<TN-1; i++){if (dOK[CCW[xn+i]]){     return {cell:CCW[xn+i]};}}return NOP;}} else if ((mF>TH0)&&(mC==LCL_QC_RESET)){if (dOK[CCW[xn+7]]){return { cell:CCW[xn+7],type:AE};} else if (view[CCW[xn]].color==LPB){if (dOK[CCW[xn+3]]){return { cell:CCW[xn+3],type:AE};} else if (dOK[CCW[xn+5]]){return { cell:CCW[xn+5],type:AE};} else if (dOK[CCW[xn+6]]){return { cell:CCW[xn+6],type:AJM};} else if (dOK[CCW[xn+2]]){return { cell:CCW[xn+2],type:AJM};} else if (dOK[CCW[xn+4]]){return { cell:CCW[xn+4],type:AJM};} else if (dOK[CCW[xn+1]]){return { cell:CCW[xn+1],type:ASF};}}}return NOP;}function rQOSy(){if ((aE[AQ]>0)&&(mF>0)){for (var i=2; i<TN; i++){if (view[CCW[xn+i]].ant&&(view[CCW[xn+i]].ant.type==AQ)&&!view[CCW[xn+i]].ant.friend){var j=(xn&4) ? 1 : -1;if (dOK[CCW[xn+i-j]]){return {cell:CCW[xn+i-j],type:AJM};} else if (dOK[CCW[xn+i+j]]){return {cell:CCW[xn+i+j],type:AJM};} else if (i==5){var i1=5-2*j;var i2=5+2*j;if (dOK[CCW[xn+i1]]&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&
view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
view[CCW[xn+i2]].ant&&view[CCW[xn+i2]].ant.friend)){return {cell:CCW[xn+i1],type:AJM};} else if (dOK[CCW[xn+i2]]&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&
view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
view[CCW[xn+i1]].ant&&view[CCW[xn+i1]].ant.friend)){return {cell:CCW[xn+i2],type:AJM};}} else if ((i==3)&&dOK[CCW[xn+5]]&&!(view[CCW[xn+2]].ant&&view[CCW[xn+2]].ant.friend&&
view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend)){return {cell:CCW[xn+5],type:AJM};} else if ((i==7)&&dOK[CCW[xn+5]]&&!(view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend)){return {cell:CCW[xn+5],type:AJM};}}}} else if ((mF>0)&&(view[CCW[xn+7]].color==LA)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7],type:AJM};} else if (view[CCW[xn+1]].ant.food>0){if ((mF>0)&&dOK[CCW[xn+2]]){return {cell:CCW[xn+2],type:AJM};}} else if ((aLF[AJM]+aLF[ASM]>0)&&(mF>0)&&(sN[LA]>0)){for (var i=2; i<TN; i++){var c=CCW[xn+i];if (dOK[c]&&(view[c].color==LA)){return {cell:c,type:AJM};}}} else if (eT>0){var bandits=aUE[1]+aUE[2]+aUE[3]+aUE[4];if ((mF>THX)&&((bandits>=2)||((bandits>=1)&&view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].color==LA)&&(view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&
(view[CCW[xn+7]].color==LA))||(view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&
(view[CCW[xn+3]].color==LA))))){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}if (mF<RD1){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}} else if ((bandits>=1)&&(mF>0)){if ((mF>THX)&&(mF % RM2==RD2+xn/2)){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}for (var i=2; i<TN; i++){var c=CCW[xn+i];var c1,c2;if ((xn==2)||isSc0(view[CCW[xn+1]].color)){c1=CCW[xn+i-1];c2=CCW[xn+i+1];} else if ((xn==6)||isSc1(view[CCW[xn+1]].color)){c1=CCW[xn+i+1];c2=CCW[xn+i-1];} else {break;}if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.food==0)){if (dOK[c1]&&!(view[c2].ant&&view[c2].ant.friend&&view[c2].ant.food>0)){return {cell:c1,type:AJM};} else if (dOK[c2]&&!(view[c1].ant&&view[c1].ant.friend&&view[c1].ant.food>0)){return {cell:c2,type:AJM};}break;}}}}if (!(LCRQCVAL[mC])){return {cell:POSC,color:LCRQC[1]};} else if ((view[CCW[xn]].color==LPX)&&isSc0(view[CCW[xn+1]].color)){if ((mF<=TH0)||(mF % RM1==RD1)){return (rQHTc());} else if (mF<=TH2){if (dOK[CCW[0]]){return {cell:CCW[0],type:AJM};} else {return (rQHTc());}} else {var destCycle=[2,4,6,4,6,2,6,2,4];var destination=destCycle[mF % 9];if (!dOK[CCW[xn+destination]]){destination=destination % 6+2;}if (!dOK[CCW[xn+destination]]){destination=destination % 6+2;}if (!dOK[CCW[xn+destination]]){return (rQHTc());}if (mF<=TH3){if (xn<=2){return {cell:CCW[xn+destination],type:AJM};} else {return (rQHTc());}} else if (mF<=TH4){if (xn<=2){return {cell:CCW[xn+destination],type:((xn>0) ? AJM : ASM)};} else {return (rQHTc());}} else if (mF<=TH5){if (xn==0){return {cell:CCW[xn+destination],type:ASM};} else {return (rQHTc());}} else {return (rQHTc());}}} else {return {cell:POSC,color:incQc(mC)};}return NOP;}function rQLsSy(){if (mF>=TH1){if (mC!=LCLR){return {cell:POSC,color:LCLR};} else {for (var i=0; i<TN; i+=2){if (view[CCW[i]].color!=LCLR){return {cell:CCW[i],color:LCLR};}}}if ((eT==0)&&(fT==1)){return {cell:CCW[xn+3]};}}if ((eT==0)&&(fT==1)){if (view[CCW[xn+2]].food>0){return {cell:CCW[xn+2]};} else if ((view[CCW[xn+3]].food +view[CCW[xn+4]].food>0)&&(view[CCW[xn+1]].color!=LLSF)){return NOP;} else {return {cell:CCW[xn+2]};}} else if (dOK[CCW[xn+2]]&&(dOK[CCW[xn+3]]||(view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend))){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else {return NOP;}}function rQLvSy(){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}return NOP;}function rQCSy(){return NOP;}function rSOSy(){if (!(LCRSCVAL[mC])){return {cell:POSC,color:LCRSC[1]};} else if (isSc0(mC)&&isQc0(view[CCW[xn+1]].color)&&
(view[CCW[xn+3]].color==LPG)){return {cell:CCW[xn+3],color:LPX};} else {return {cell:POSC,color:incSc(mC)};}return NOP;}function rSLSy(){if ((eT==0)&&(fT==1)){if (view[CCW[xn]].food>0){return {cell:CCW[xn]};} else if (view[CCW[xn+7]].food +view[CCW[xn+6]].food>0){return {cell:POSC,color:LLSF};} else {return {cell:CCW[xn]};}} else if ((eT>0)&&view[CCW[xn+2]].ant&&!view[CCW[xn+2]].ant.friend){return {cell:POSC,color:LLSF};} else {if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}return NOP;}function rSESy(){if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].ant.type==ASF)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};}} else if ((mC==LRR0)&&(view[CCW[xn+5]].color==LG6)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};}} else {if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};}}return NOP;}function rGSSy(){if (view[CCW[xn]].color!=LCL_QC_RESET){return {cell:CCW[xn],color:LCL_QC_RESET};}if (mC!=LPB){return {cell:POSC,color:LPB};}return (rGGTc());}function rGOSy(){if (aE[AQ]>0){var c=CCW[xn+2];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}c=CCW[xn+3];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}}if (!LCRPHR[mC]){if ((mC==LPX)&&isSc0(view[CCW[xn+7]].color)){return NOP;} else if (eT==0){return {cell:POSC,color:LPG};}} else if (isSc1(view[CCW[xn+7]].color)&&isQc2(view[CCW[xn]].color)){switch (mC){case LPG:return {cell:POSC,color:LPG1};case LPG1:return {cell:POSC,color:LPG};default:return {cell:POSC,color:LPG};}}if ((eT>0)&&!LCRPHR[mC]&&(xn&4)){return {cell:POSC,color:LPG};} else {return (rGGTc());}}function rLSSy(){if (mC!=LCLR){return {cell:POSC,color:LCLR};}return NOP;}function rEHyS(){if (mQ==1){var ptrn=PTFRL0H;var msm=patC(ptrn,AIMU,0,1);if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (LCRQCVAL[view[CCW[xn+mQ]].color]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}}return NOP;}function rEBRSy(){if (aF[ASF]>0){return (rELGTc());} else {return (rEBRTc());}}function rEASy(){return NOP;}function rUHSy(){if ((mQ==0)&&(view[CCW[xn]].ant.food<RD1)&&view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&(view[CCW[xn+1]].ant.type==ASF)){var cc=[5,6,7,4,2];for (var i=0; i<cc.length; i++){var c=CCW[xn+cc[i]];if (dOK[c]){return {cell:c};}}return NOP;}if ((eT>0)&&(aE[AQ]+aUE[1]+aUE[2] +aUE[3]+aUE[4]>0)){var common;if (mQ==0){common=[1,7];} else {common=[0,2,3,7];}for (var i=0; i<common.length; i++){var c=CCW[xn+common[i]];if (view[c].ant&&!view[c].ant.friend&&((view[c].ant.type==AQ)||(view[c].ant.food==0))){if ((aE[AQ]==0)&&(mC!=LA)){return {cell:POSC,color:LA};} else {return NOP;}}}}if (mQ==0){if (mC!=LRM0){return {cell:POSC,color:LRM0};} else if (view[CCW[xn+3]].color!=LRR0){return {cell:CCW[xn+3],color:LRR0};} else if (view[CCW[xn+7]].color!=LRL0){return {cell:CCW[xn+7],color:LRL0};} else if (view[CCW[xn+5]].color!=LRM1){return {cell:CCW[xn+5],color:LRM1};} else if (view[CCW[xn+6]].color!=LRL1){return {cell:CCW[xn+6],color:LRL1};} else if ((!LCRGRR1[view[CCW[xn+4]].color])&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend)){return {cell:CCW[xn+4],color:LRR1};}if (LCRQCVAL[view[CCW[xn]].color]||(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
(view[CCW[xn+5]].ant.food>0))||(view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
(view[CCW[xn+6]].ant.food>0))){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};}}} else {var ptrn=PTFRL0H;var msm=patC(ptrn,AIMU,0,1);if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (LCRQCVAL[view[CCW[xn+mQ]].color]){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}}}return NOP;}function rUBRSy(){if (spcRL1()){return (rULRL1Sy());} else if (spcRL02()){return (rULRL02Sy());} else if (spcRM()){return (rUFCRTc());}for (var i=TN-1; i>=0; i--){if (view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
(view[CCW[i+1]].ant.type==AE)&&dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}function rUTRRSy(){return (rUCRTc());}function rULRL1Sy(){var ptrn=PTGRL1;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){if ((view[CCW[xn+6]].color==LMS_WRP)&&(view[CCW[xn+7]].color==LCLR)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+3]].color!=LRM1_WRP)){return {cell:CCW[xn+3],color:LRM1_WRP};} else if ((view[CCW[xn+3]].color!=LRM1_WRP)&&view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRL02Sy(){var ptrn;var msm;if (sL[LRM0]>0){ptrn=PTGRL0;msm=patC(ptrn,AIMR,1,1);}if (xn<0){ptrn=PTGRL2;msm=patC(ptrn,AIMR,1,1);}if (xn>=0){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRR0Sy(){var ptrn=PTGRR0;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (runUMLeaveRRTactic());} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRR2Sy(){var ptrn=PTGRR2;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (runUMLeaveRRTactic());}    return (rLostMSy(false));}function rUPSSy(){var ptrn=PTGRR1;var msm=patC(ptrn,AIMD,3,2);if (xn>=0){var c=CCW[xn+1];if (view[c].ant&&view[c].ant.friend&&(view[c].ant.food>0)){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}}if ((view[CCW[xn+2]].color==LMX_M3OUT)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&(mC!=LRR1X)){return {cell:POSC,color:LRR1X};} else if ((mT==AJM)&&(mC==LRR1U)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&LCRMX_OUT[view[CCW[xn+2]].color]){return {cell:POSC,color:LRR1V};} else if ((mC==LRR1X)||((mT==AJM)&&(mC==LRR1V))||((mC==LRR1U)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&LCRMX_IN[view[CCW[xn+2]].color])){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else if (mC!=LRR1U){return {cell:POSC,color:LRR1U};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return NOP;}}}return (rLostMSy(false));}function rUESSy(){var ptrn=PTMS0R_IN;var msm=patC(ptrn,AIMD,4,2);if (xn>=0){return (rUESTc(ptrn,msm));}return (rLostMSy(false));}function rUDSSy(){var ptrn;var msm;if ((sL[LML3]>=1)&&(sD[LMR2]+sD[LMR0]>=1)){ptrn=PTMS3;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(sL[LMR2]>=1)&&(sD[LML1]+sD[LML3]>=1)){ptrn=PTMS2;msm=patC(ptrn,AIMD,3,2);}if (xn>=0){if ((msm<0)&&(view[CCW[xn]].color==LRM0)&&(view[CCW[xn+1]].color==LRR0)&&(view[CCW[xn+2]].color==LMR0)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}return (rUDSTc(ptrn,msm));}if ((xn<0)&&(sL[LML1]>=1)&&(sD[LMR0]+sD[LMR2]>=1)){ptrn=PTMS1_IN;msm=patC(ptrn,AIMD,3,4);if (xn<0){ptrn=PTMS1;msm=patC(ptrn,AIMD,3,2);}}if (xn>=0){return (rUDSTc(ptrn,msm));}if ((sD[LML3]+sL[LMR0]>=2)&&(sD[LRL0] >=2)&&(sD[LML1]==0)){ptrn=PTMS0_WRAPPING;msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rUWRTc(ptrn,msm));}}if ((sL[LMR0]>=1)&&(sD[LML3]+sD[LML1]>=1)){ptrn=PTMS0;msm=patC(ptrn,AIMD,3,2);if (xn>=0){return (rUDSTc(ptrn,msm));}}if (spcRR1()){ptrn=PTGRR1;msm=patC(ptrn,AIMD,3,2);if (xn>=0){if (mC==LRR1){return {cell:POSC,color:LRR1U};}return NOP;}}if (spcMS0R()){ptrn=PTMS0R_IN;msm=patC(ptrn,AIMD,4,2);if (xn>=0){return (rUESTc(ptrn,msm));}}return (rLostMSy(false));}function rUSWSy(){var ptrn=PTMS0_WRAPPING;var msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rUWRTc(ptrn,msm));}return (rLostMSy(false));}function rURHSy(){return (rMNGTc());}function rUCRSy(){for (var i=TN; i>=1; i--){if (view[CCW[i]].ant&&dOK[CCW[i-1]]){return {cell:CCW[i-1]};}}return NOP;}function rLLLWSy(){var ptrn;var msm;if (mC==LML1){ptrn=PTMS1FL;msm=patC(ptrn,AIML,0,1);if (xn>=0){return (rLLLWTc());}} else if (mC==LML3){ptrn=PTMS3FL;msm=patC(ptrn,AIML,0,1);if (xn>=0){return (rLLLWTc());}} else if (sL[LML1]+sL[LML3]>=2){ptrn=PTMS0FL;msm=patC(ptrn,AIML,0,1);if (xn<0){ptrn=PTMS2FL;msm=patC(ptrn,AIML,0,1);}if (xn>=0){return (rLLLWTc());}} else if (spcMFR()){return (rLLRWSy());} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLLRWSy(){var ptrn;var msm;if (mC==LMR0){ptrn=PTMS0FR;msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLLRWTc());}} else if (mC==LMR2){ptrn=PTMS2FR;msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLLRWTc());}} else if (sL[LMR0]+sL[LMR2]>=2){ptrn=PTMS1FR;msm=patC(ptrn,AIMR,0,1);if (xn<0){ptrn=PTMS3FR;msm=patC(ptrn,AIMR,0,1);}if (xn>=0){return (rLLRWTc());}} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLASSy(){var ptrn;var msm;if ((sL[LML3]>=1)&&(sD[LMR2]+sD[LMR0]>=1)){ptrn=PTMS3;msm=patC(ptrn,AIMU,3,2);}if ((xn<0)&&(sL[LMR2]>=1)&&(sD[LML1]+sD[LML3]>=1)){ptrn=PTMS2;msm=patC(ptrn,AIMU,3,2);}if ((xn<0)&&(sL[LML1]>=1)&&(sD[LMR0]+sD[LMR2]>=1)){ptrn=PTMS1_IO;msm=patC(ptrn,AIMU,0,1);if (xn<0){ptrn=PTMS1;msm=patC(ptrn,AIMU,3,2);}}if (xn>=0){return (rLASTc(ptrn,msm));}if ((sD[LML3]+sL[LMR0]>=2)&&(sD[LRL0] >=2)&&(sD[LML1]==0)){ptrn=PTMS0_OUT;msm=patC(ptrn,AIMU,0,1);if (xn>=0){return {cell:CCW[xn+3],color:LCLR};}ptrn=PTMS0_WRAPPING;msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rLWRTc(ptrn,msm));}}if ((sL[LMR0]>=1)&&(sD[LML3]+sD[LML1]>=1)){ptrn=PTMS0;msm=patC(ptrn,AIMU,3,2);if (xn>=0){return (rLASTc(ptrn,msm));}}if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLSSy(){var ptrn=PTMS0R_OUT;var msm=patC(ptrn,AIMU,0,1);if (xn>=0){} else {ptrn=PTMS0R_IN;msm=patC(ptrn,AIMU,4,2);if (xn>=0){ptrn=PTMS0R_OUT;msm=patC(ptrn,AIMU,0,1);}}if (xn>=0){return (rLLSTc(ptrn,msm));} else if (spcMS()){return (rLASSy());} else {return (rLostMSy(false));}return NOP;}function rLLVSSy(){var ptrn=PTMS0R_OUT1;var msm=patC(ptrn,AIMU,0,1);if (xn>=0){if (view[CCW[xn+3]].color==LCLR){return {cell:CCW[xn+3],color:((mT==ASM) ? LMX_M2OUT : LMX_M1OUT)};
} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLDSSy(){var ptrn=PTGRR1;var msm=patC(ptrn,AIMU,1,1);if (xn>=0){if ((view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)){if ((mC==LRR1X)&&(view[CCW[xn+2]].color!=LMX_M3OUT)){return {cell:CCW[xn+2],color:LMX_M3OUT};}if ((view[CCW[xn+2]].color==LMX_M3OUT)&&(mC!=LRR1X)){return {cell:POSC,color:LRR1X};} else if ((LCRMX_OUT[view[CCW[xn+2]].color])&&
(mC!=LRR1V)){return {cell:POSC,color:LRR1V};} else if ((LCRMX_IN[view[CCW[xn+2]].color])&&(mC!=LRR1U)){return {cell:POSC,color:LRR1U};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}}return (rLostMSy(false));}function rLTRRSy(){return (rLCRTc());}function rLFRSy(){for (var i=1; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AE)&&dOK[CCW[i+2]]){return {cell:CCW[i+2]};}}return NOP;}function rLRHSy(){var ptrn=PTFRL1G;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLRLTc());}return (rMNGTc());}function rLLRL1Sy(){var ptrn=PTGRL1;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLRLTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLRL02Sy(){var ptrn;var msm;if (sL[LRM0]>0){ptrn=PTGRL0;msm=patC(ptrn,AIMR,1,1);}if (xn<0){ptrn=PTGRL2;msm=patC(ptrn,AIMR,1,1);}if (xn>=0){return (rLRLTc());}return (rLostMSy(false));}function rLLRR0Sy(){var ptrn=PTGRR0;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (rLRRTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLRR2Sy(){var ptrn=PTGRR2;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (rLRRTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLCRSy(){for (var i=TN; i>=1; i--){if (!dOK[CCW[i]]&&dOK[CCW[i-1]]){return {cell:CCW[i-1]};}}return NOP;}function rM2R1Sy(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)&&view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
(view[CCW[i+1]].ant.type==ASF)){if (i&1){} else {}if (dOK[CCW[i+7]]){return {cell:CCW[i+7]};} else {return NOP;}}}return (rLostMSy(true));}function rLostMSy(totally){if ((fdT>0)&&(mF==0)){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&dOK[CCW[i]]){return {cell:CCW[i]};}}}if (totally&(fT==0)){if (((mC==PY)&&(sN[PY]==0))||((mC==PR)&&(sN[PR]==0))){return {cell:POSC,color:PP};}if (((mC==PG)&&(sN[PG]==0))||((mC==PC)&&(sN[PC]==0))||((mC==PB)&&(sN[PB]==0))||((mC==PP)&&(sN[PP]==0))){return {cell:POSC,color:PW};} else if ((sT[PG]==0)&&(((mC==PK)&&(sN[PK]==0))||((mC==PY)&&(sN[PY]==0))||((mC==PR)&&(sN[PR]==0)))){return {cell:POSC,color:PW};} else if ((mC!=PW)&&(sN[mC]>=4)){return {cell:POSC,color:PW};}}if ((mC==PG)&&(sL[PG]>=2)){return {cell:POSC,color:PW};} else if (((mC==PK)||(mC==PR))&&(sN[PK]+sN[PR]>=3)){return {cell:POSC,color:PW};}if (sN[PW]<=4){var preferredColors =[PG,PB,PC,PP,PY,PR,PK];for (var ci=0; ci<preferredColors.length; ci++){var c=preferredColors[ci];if (mC==c){break;}if (sN[c]>0){for (var i=1; i<TN; i++){if ((view[CCW[i]].color==c)&&dOK[CCW[i]]){return {cell:CCW[i]};}}}}}if (RW){for (var i=1; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}for (i=0; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;} else {return NOP;}}function rDHSy(){if (mQ==0){var c=CCW[xn+2];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}c=CCW[xn+6];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}} else {var c=CCW[xn+4];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+3]].ant){return {cell:CCW[xn+3],color:LA};}c=CCW[xn+6];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}c=CCW[xn+5];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)){if (!view[CCW[xn+3]].ant){return {cell:CCW[xn+3],color:LA};} else if (!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}}}return NOP;}function rQSETc(){if (mC!=LT){return {cell:POSC,color:LT};}for (var i=0; i<TN; i++){if (view[CCW[i]].food>0){return {cell:CCW[i]};}}return NOP;}function rQSSTc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&(dOK[CCW[i]])){return {cell:CCW[i]};}}return NOP;}function rQSTCTc(){if ((mC!=LCLR)&&(sN[mC]>=4)){if (sN[LT]==0){return {cell:POSC,color:LT};} else if (sN[LT]>=3){return {cell:POSC,color:LT};} else {for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+2]].color!=LT)){return {cell:CCW[i+2],color:LT};}}return NOP;}} else if (sN[LT]==1){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+4]].color!=LCLR)){if (view[CCW[i+1]].color==LCLR){return { cell:CCW[i+1]};} else if (view[CCW[i+7]].color==LCLR){return { cell:CCW[i+7]};} else {return {cell:POSC,color:LT};}}}return {cell:POSC,color:LT};} else {return {cell:POSC,color:LT};}return NOP;}function rQSATc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LCLR)&&(view[CCW[i+1]].color==LCLR)&&(view[CCW[i+2]].color==LCLR)){if ((view[CCW[i+3]].color==LCLR)&&(view[CCW[i+4]].color==LCLR)){return {cell:CCW[i+2]};}return {cell:CCW[i+1]};}}for (i=TN-1; i>=0; i--){if (view[CCW[i]].color!=LT){return {cell:CCW[i]};}}for (i=0; i<TN; i++){if (view[CCW[i]].color!=LT){return {cell:CCW[i],color:LCLR};}}return {cell:0,color:LCLR};}function rQSEvTc(){if (sN[LT]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].color==LT){xn=i&6;}}if ( dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]] ){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+3]]&&dOK[CCW[xn+4]]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn]};} else {for (i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]&&dOK[CCW[i+3]]&&dOK[CCW[i+4]]){return {cell:CCW[i+2]};}}for (i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]){return {cell:CCW[i+1]};}}for (i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}return NOP;}function rQHTc(){var ptrn=PTHOME;var msm=patC(ptrn,POSC,0,1);if (msm!=0){var cc=fwdWrong[0];return {cell:cc.v,color:ptrn[cc.p]};} else {return NOP;}}function rGGTc(){var ptrn=PTGARDEN;var msm=patC(ptrn,POSC,0,1);if (msm!=0){var cc=fwdWrong[0];return {cell:cc.v,color:ptrn[cc.p]};} else {return NOP;}}function rUFCRTc(){var ptrn;var msm;if (mC==LRM0){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM2){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM1){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM1_WRP){ptrn=PTGRM1_WRP;msm=patC(ptrn,AIMR,1,1);}if ((xn<0)&&spcRR1()){return (rUPSSy());}if (xn<0){return (rLostMSy(false));}if (msm==0){if (fdL>0){if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if ((mC==LRM1)&&(view[CCW[xn+3]].color!=LRR1X)&&(view[CCW[xn+3]].color!=LRR1U)&&!((mT==AJM)&&(view[CCW[xn+3]].color==LRR1V))&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}return NOP;}for (var i=0; i<TN; i++){var ce=CCW[xn+i];if (view[ce].ant&&view[ce].ant.friend&&(view[ce].ant.type==AE)){if ((2<=i)&&(i<=4)){var msmSaved=msm;var fwdWrongSaved=Array.from(fwdWrong);var rearWrongSaved=Array.from(rearWrong);xn=xn % 4+4;msm=patC(ptrn,POSC,0,1);if (msm<msmSaved){xn=xn % 4+4;msm=msmSaved;fwdWrong=fwdWrongSaved;rearWrong=rearWrongSaved;} else {}break;}}}if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rUCRTc(){var ptrn;var msm;if (mC==LRM0){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&spcRR1()){return (rUPSSy());}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM2){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM1){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&(eT>0)){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM1_WRP){ptrn=PTGRM1_WRP;msm=patC(ptrn,AIMR,1,1);}if ((xn<0)&&spcRR1()){return (rUPSSy());}if (xn<0){return (rLostMSy(true));}if (msm==0){if (fdL>0){if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if ((mC==LRM1)&&(view[CCW[xn+3]].color!=LRR1X)&&(view[CCW[xn+3]].color!=LRR1U)){if ((((mT==AJM)&&(view[CCW[xn+3]].color==LRR1))||((mT==ASM)&&(view[CCW[xn+3]].color==LRR1V)))&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if (mC==LRM0&&(view[CCW[xn+5]].color==LRM1_WRP)&&!(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend)){return {cell:CCW[xn+5],color:LRM1};}var c=CCW[xn+5];if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){var evade=false;if (view[c].ant.food>0){evade=true;} else if (view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&(view[CCW[xn+1]].ant.food==0)){evade=true;}if (evade){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else {return NOP;}} else {return NOP;}}} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rUESTc(ptrn,msm){switch (view[CCW[xn+3]].color){case LMX_M0:return {cell:CCW[xn+3],color:LMX_M1IN};case LMX_M1OUT:if (mT==ASM){return {cell:CCW[xn+3],color:LMX_M2IN};}break;case LMX_M2OUT:if (mT==ASM){return {cell:CCW[xn+3],color:LMX_M3IN};}break;case LMX_M3OUT:break;case LMX_M1IN:case LMX_M2IN:case LMX_M3IN:if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if ((msm==0)&&dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {break;}default:break;}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}return NOP;}function runUMLeaveRRTactic(){if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}}function rUDSTc(ptrn,msm){var c=CCW[xn+1];if ((msm==0)&&(fdL>0)&&(view[CCW[xn+3]].food+view[CCW[xn+7]].food>0)){if (mC!=LMMF){return {cell:POSC,color:LMMF};} else if (view[CCW[xn+5]].color!=LMMH){return {cell:CCW[xn+5],color:LMMH};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};}} else if ((msm<0)&&!(view[c].ant&&view[c].ant.friend)){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>=0){if (dOK[c]){return {cell:c};} else {if (view[c].ant&&view[c].ant.friend){if (view[c].ant.food>0){if ((view[CCW[xn]].color==LCLR)&&dOK[CCW[xn]]){return {cell:CCW[xn]};} else if ((view[CCW[xn+2]].color==LCLR)&&dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else {var c=CCW[xn+5];if (view[c].ant&&view[c].ant.friend&&(view[c].ant.food==0)){if (view[c].color==LMMH){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else {return NOP;}} else if (mC!=LMMH){return {cell:POSC,color:LMMH};} else {return NOP;}} else {return NOP;}}} else {return NOP;}}}return NOP;}function rUWRTc(ptrn,msm){if (view[CCW[xn+3]].color!=LMS_WRP){return {cell:CCW[xn+3],color:LMS_WRP};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};}return NOP;}function rLLLWTc(){if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else {return NOP;}}function rLLRWTc(){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else {return NOP;}}function rLWRTc(ptrn,msm){if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else {return NOP;}}function rLASTc(ptrn,msm){var c=CCW[xn+5];if ((msm<0)&&!(view[c].ant&&view[c].ant.friend)){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (mC==LMMF){return {cell:POSC,color:LCLR};} else if (view[CCW[xn+5]].color==LMMH){return {cell:CCW[xn+5],color:LCLR};} else if (msm>=0){if (dOK[c]){return {cell:c};} else if ((view[c].food>0)&&(eT==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};}}}return NOP;}function rLLSTc(ptrn,msm){if (msm<0){if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend){return NOP;}var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}switch (view[CCW[xn+3]].color){case LMX_M1IN:return {cell:CCW[xn+3],color:LMX_M1OUT};case LMX_M2IN:return {cell:CCW[xn+3],color:LMX_M2OUT};case LMX_M3IN:default:return {cell:CCW[xn+3],color:LMX_M3OUT};case LMX_M1OUT:case LMX_M2OUT:case LMX_M3OUT:if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}break;}return NOP;}function rLCRTc(){var ptrn;var msm;var trust=(aF[AE]>0 ? 1 : 0);if (mC==LRM0){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM2){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM1){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM1_WRP){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);if (xn>=0){if (view[CCW[xn+3]].color!=LRR1X){return {cell:CCW[xn+3],color:LRR1X};} else if (!(view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend)){return {cell:POSC,color:LRM1};}}}if (xn<0){if (spcRR1()){return (rLDSSy());}return (rLostMSy(true));}if (msm==0){var c=CCW[xn+1];if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){var evade=false;if (view[c].ant.food==0){evade=true;} else if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].ant.food>0)){evade=true;}if (evade){if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else {return NOP;}} else {return NOP;}}} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rLRLTc(){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&
dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}function rLRRTc(){if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}function rMNGTc(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)){if (view[CCW[i]].color==LCLR){if (i&1){if ((view[CCW[i+3]].color==LG5)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};}} else {if ((view[CCW[i+4]].color==LG6)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if ((view[CCW[i+3]].color==LG5)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};}}return (rLostMSy(true));} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}}return NOP;}function rELGTc(){var ptrn=PTFRL1G;var msm;for (var i=0; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)){xn=i;break;}}msm=patC(ptrn,AIMU,0,1);if (xn<0){return NOP;} else if ((msm==0)&&dOK[CCW[xn+5]]&&((view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend)||
(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend))){return {cell:CCW[xn+5]};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {return NOP;}return NOP;}function rEBRTc(){var ptrn;var msm;if (mC==LRL0){for (var i=0; i<TN; i+=2){var c=CCW[i];if ((view[c].color==LRM0)&&view[c].ant&&view[c].ant.friend&&((view[CCW[i]].ant.type==AJM)||(view[CCW[i]].ant.type==ASM))){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);if (xn>=0){break;}}}if (xn<0){ptrn=PTFRL0;msm=patC(ptrn,AIMU,1,1);}if (xn<0){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRL1;msm=patC(ptrn,AIMU,1,1);}if (xn<0){return rECLRETc();}} else if (mC==LRL1){ptrn=PTFRL1;msm=patC(ptrn,AIMU,1,1);if ((xn<0)&&(eT>0)){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRL0;msm=patC(ptrn,AIMU,1,1);}if (xn<0){return rECLRETc();}} else if ((mC==LRR2)&&(sL[LRL1]>=1)&&(sL[LRL0]==0)){return {cell:POSC,color:LRL0};}if ((msm==0)&&dOK[CCW[xn+5]]&&((view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend)||
(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend))){return {cell:CCW[xn+5]};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {return NOP;}return NOP;}function rECLRETc(){var ptrn;var msm;for (var i=3; i<TN+2; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
((view[CCW[i]].ant.type==AJM)||(view[CCW[i]].ant.type==ASM))){xn=i-3;if (mC==LRL0){ptrn=PTFRL0;msm=patC(ptrn,AIMR,1,0.3);if (msm==PTNOM){ptrn=PTFRL2;msm=patC(ptrn,AIMR,1,0.3);}} else if (mC==LRL1){ptrn=PTFRL1;msm=patC(ptrn,AIMR,1,0.3);}if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}}return NOP;}function patC(ptrn,targetCell,qG,wt){if (xn>=0){return (patCO(ptrn,targetCell,qG,wt,xn));} else {var msm;for (var o=0; o<TN; o+=2){msm=patCO(ptrn,targetCell,qG,wt,o);if (xn>=0){return msm;}}return PTNOM;}}function patCO(ptrn,targetCell,qG,wt,ortn){var fwdFCs=FWD_CELLS[targetCell];var totDscs=0;fwdWrong=[];rearWrong=[];if ((Array.isArray(ptrn[POSC])&&!ptrn[POSC][mC])||
((ptrn[POSC]>0)&&(mC!=ptrn[POSC]))){if (fwdFCs[POSC]){fwdWrong.push({p:POSC,v:POSC});totDscs+=1;} else {rearWrong.push({p:POSC,v:POSC});totDscs+=wt;}}if ((xn<0)&&(totDscs>qG)){return PTNOM;}var jFrom=0;switch (targetCell){case AIMU:jFrom=4;break;case AIML:jFrom=6;break;case AIMR:jFrom=2;break;case AIMD:case POSC:default:break;}for (var j=jFrom; j<TN+jFrom; j++){var posP=CCW[j];var posV=CCW[ortn+j];var c=view[posV].color;if ((Array.isArray(ptrn[posP])&&!ptrn[posP][c])||
((ptrn[posP]>0)&&(c!=ptrn[posP]))){if (fwdFCs[posP]){fwdWrong.push({p:posP,v:posV});totDscs+=1;} else {rearWrong.push({p:posP,v:posV});totDscs+=wt;}}if ((xn<0)&&(totDscs>qG)){return PTNOM;}}if ((xn<0)){xn=ortn;}if (fwdWrong.length==0){return (totDscs);} else {return (-totDscs);}}function isQc0(color){return (LCRQCVAL[color]&&(LCRQC_VALUE[color]==0));
}function isQc2(color){return (LCRQCVAL[color]&&(LCRQC_VALUE[color]==2));
}function incQc(color){if (LCRQCVAL[color]){if (LCRQC_VALUE[color]>=QCPERD){return LCRQC[0];} else {return (LCRQC[(LCRQC_VALUE[color]+1) % QCPERD]);
}} else {return undefined;}}function isSc0(color){return (LCRSCVAL[color]&&(LCRSC_VALUE[color]==0));
}function isSc1(color){return (LCRSCVAL[color]&&(LCRSC_VALUE[color]==1));
}function incSc(color){if (LCRSCVAL[color]){return (LCRSC[(LCRSC_VALUE[color]+1) % SCPERD]);
} else {return undefined;}}function spcMS(){return (((mC==LCLR)||((mF+fdL>0)&&(mC==LMMF))||((mF>0)&&(mC==LMMH)))&&(sN[LMR0]+sN[LML1] +sN[LMR2]+sN[LML3]>=2)&&(sN[LCLR]>=3)&&(sN[LMMF] +sN[LMMH] +sN[PB]<=3)); }function spcRM(){return (LCRGRM_ALL[mC]&&(sT[LRL0]+sT[LRL1]>=3)&&(sN[LRL0]>=1)&&(sN[LRR0]+sN[LRR2]>=2)&&(sT[LRM0]+sT[LRM1_WRP] +sT[LRM1]+sT[LRM2]>=2)&&(sT[LCLR]<=4));}function spcRL1(){return ((mC==LRL1)&&(sL[LRL0]>=2)&&(sD[LRM0]>=1)&&(sL[LRM1_WRP]+sL[LRM1]>=1)&&(sD[LRM2]>=1));}function spcRL02(){return ((mC==LRL0)&&(sL[LRL1]+sL[LRL2]>=2)&&(sN[LRM0]>=1)&&(sD[LRM1_WRP]+sD[LRM1]>=1));}function spcRR0(){return ((mC==LRR0)&&(sL[LRM1]==0)&&(sD[LRM1]+sD[LRM1_WRP]>=1)&&(sL[LMR0]>=1)&&(sL[LRR2]>=1));}function spcRR1(){return (LCRGRR1[mC]&&(sN[LRR0]>=2)&&(sL[LRR2]>=1)&&(sD[LRM0]>=1)&&(sN[LCLR]<=3)&&(sL[LRM1] +sL[LRM1_WRP]>=1));}function spcRR2(){return ((mC==LRR2)&&(sD[LCLR]>=1)&&(sD[LRM0]>=1)&&(sD[LRM1]+sD[LMR0]>=2)&&(sL[LRR0]>=2));}function spcMS0R(){return((mC==LCLR)&&(sL[LMR0]>=1)&&(sL[LRR1U]>=1)&&(sD[LRR2]>=1)&&(sD[LRR0]>=1));}function spcMS0ROut(){return ((mC==LCLR)&&(sL[LMR0]+sL[LRR1V]>=2)&&(sD[LRR2]>=2)&&(sD[LRR0]>=1));}function spcMS0W(){return ((mC==LCLR)&&(sD[LRL0]>=3)&&(sL[LRL1]>=1)&&(sL[LMS_WRP]>=2)&&(sN[LCLR]>=2));}function spcMFL(){return ((sL[LMMF]>=1)&&(sD[LMMH]>=1)&&(sT[LCLR]>=2)&&(sT[LML1]+sT[LML3]>=1));}function spcMFR(){return ((sL[LMMF]>=1)&&(sD[LMMH]>=1)&&(sT[LCLR]>=2)&&(sT[LMR0]+sT[LMR2]>=1));}function fixup(ptrnCell){if (Array.isArray(ptrnCell)){for (var i=1; i<=9; i++){if (ptrnCell[i]){return i;}}return LCLR;} else {return ptrnCell;}}

(Auf Anraten von Trichoplax und Dzaima - wofür ich mich bedanke! - wurde dies auf Kosten der Lesbarkeit unschädlich gemacht, um in die PPCG-Größenbeschränkungen zu passen. Bearbeiten: Das ungekürzte Original, reichlich kommentiert und mit aussagekräftigen Variablen- und Funktionsnamen, ist jetzt verfügbar auf GitHub .)

Nach dem üblichen anfänglichen Durcheinander startet die Windmill Queen drei Schienen mit dem Ziel, einen näher gelegenen Bereich gründlicher zu reinigen, die Fahrzeiten zu verkürzen und Redundanz zu schaffen. Auf der vierten Seite befindet sich ein kleiner Garten mit roten, blauen und schwarzen Beeren.

Wir verzichten auf Miners on a Rails Repairer. (Ihre sehr lange Schiene ist ein gemischter Segen ... sie zieht tendenziell Vampire an.) Stattdessen haben wir auf jeder Schiene einen Ingenieur. Ihr ursprünglicher Zweck war es, den Bergleuten mitzuteilen, ob sie die Schiene verlängern (wenn sie den Ingenieur sehen können) oder reparieren (wenn sie nicht können), aber dies wurde etwas vergraben, als sich der Code weiterentwickelte. Sie kümmern sich jetzt um ein paar kleinere Aufgaben, wie zum Beispiel das Verhindern, dass Schienen verkehrt herum entstehen.

Es wird erwartet, dass ein Schaft mit einer Breite von 3 Zellen und einer Tiefe von 1000 Zellen durchschnittlich 3 Lebensmittel enthält, und nur 4% aller Schäfte mit dieser Tiefe enthalten keine. Sobald wir aus der Menge der gehorteten Lebensmittel abschätzen, dass die Schienen lang genug gewachsen sind, um es sich zu lohnen, wird die Königin damit beginnen, ältere Bergleute hervorzubringen, die die zuvor von einem Junior-Bergmann erkundeten Schächte erneut inspizieren werden. Wie bei Miners on a Rail sind wir darauf vorbereitet, Wellen zu handhaben, die sich auf der Rückseite (links) einer Schiene um die Arena wickeln.

Die Windmühlenkönigin beschäftigt zwei weitere Mitarbeiter: einen Gärtner und einen Sekretär. Sie sind die gleichen Ameisentypen und tun das eine oder andere, je nachdem, wo sie sich im Verhältnis zur Königin und zueinander befinden. Sie helfen der Königin, die Kreation der Ingenieure und der ersten Junior-Minenarbeiter zu orchestrieren, und unterstützen die Königin beim Laufenlassen einer Uhr: eines Oszillators, der alle 85 Züge klingelt (wenn er ungestört ist).

Diese Hauptuhr entkoppelt zusammen mit der Menge an gehorteten Nahrungsmitteln und den zufälligen 1-von-4-Orientierungen, die wir erhalten, die Erschaffung von Bergleuten von der Ankunft von Nahrungsmitteln und reguliert die Rate, mit der weitere Bergleute erscheinen. Wenn sich die Königin gerade niedergelassen hat, werden alle eingehenden Lebensmittel schnell in mehr Bergleute umgewandelt, bis die Rate der eingehenden Lebensmittel ungefähr 9 pro 1000 Züge erreicht (was ungefähr ein Dutzend produktiver Bergleute erfordert). Dann wird der Takt zum limitierenden Faktor und wir fangen an, Essen zu horten. Später, wenn die Futtermenge zunimmt, drosseln wir die Laichrate auf höchstens 6 neue Bergleute pro 1000 Züge und später auf höchstens 3 und hören schließlich auf, uns vollständig zu vermehren. Ein Ratschenmechanismus verhindert, dass die Königin beim Laichen zu viel Futter ausgibt, wenn alle Schienen blockiert oder beschädigt sind.

Selbst ohne größeren Schaden wird die Effizienz des Bergbaus mit jedem Spiel stark abnehmen. Zunächst erwarten wir, dass ein Bergmann, der mit halber Lichtgeschwindigkeit bohrt und mit Lichtgeschwindigkeit zurückkehrt, durchschnittlich 1 Nahrung pro 1000 Bewegungen abgibt. Gegen Ende sind es eher 0,12-0,14 Lebensmittel pro Bergmann und 1000 Züge. Das Befahren der längeren Schienen, das Aufmalen des meist weißen Schachtmusters auf eine nicht mehr größtenteils weiße Leinwand und das Reparieren von Schächten und Schienen nehmen viel Zeit in Anspruch. Bergleute bleiben stecken oder verlieren sich oder werden in Paintball-Gefechten mit Gegnern gefesselt.

Unsere Bergleute bemühen sich, alle größeren Staus zu beseitigen.

Und es gibt ein rudimentäres Immunsystem, um mit unfreundlichen Eindringlingen umzugehen.

Der Versuch, unseren Garten mit Dampf zu rollen, wird nicht empfohlen. Unser Personal wird nicht amüsiert sein.

Der Hauptnachteil ist, dass die Windmühlenkönigin 8 Lebensmittel benötigt, um ihren ersten Stab aus der Tasche zu bekommen, so dass die Rätselphase ziemlich lang ist und anderen ähnlichen Anwärtern einen Vorsprung verschafft. Wenn unser Nest überfüllt ist und die Schienen weggewischt sind, bevor wir Essen gehortet haben, endet der Tag für uns schlecht. Später und wenn mindestens eine Schiene läuft oder repariert werden kann, können wir in der Regel weitermachen oder zumindest das (meiste) halten, was wir bereits haben.

Die Implementierung behandelt Nachbarzellen als gegen den Uhrzeigersinn nummeriert, beginnend an einer Ecke, mit einem Array ( CCW), um diese Zahlen in die viewIndizes des Controllers zu übersetzen . Wenn wir unser Nordgefühl einschränken müssen (und können), setzen wir unseren Kompass, einen Basisindex, einCCW. Die Ameisenfunktion beginnt immer mit einer Bestandsaufnahme ihrer Umgebung, insbesondere mit der Aufzeichnung eines Spektrums (wie oft jede Farbe vorkommt) und verzweigt sich dann nach Ameisentyp und -situation entlang eines mehrstufigen Entscheidungsbaums aus Strategien und Taktiken. Dies macht es möglich, eine Vielzahl seltsamer Sonderfälle zu behandeln und dabei die häufigsten Situationen sehr schnell zu bewältigen. Der Baum hat fast 200 Blätter, die eine Zelle malen oder einen Schritt machen oder eine Ameise erschaffen, und mehr als 70, die nichts tun - aus den 2 ^ 27 möglichen Farbmustern multipliziert mit möglichen sichtbaren Ameisen, sichtbaren Nahrungsmitteln und Nahrungsmitteln getragen.

Hier ist ein ASCII-Rendering der Hub-Geometrie:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   | ^ |MR2|   | v |MR2|   | ^ |   |   |   |   |   |   |   |   |
|   |   |   |   | i |   |   | a | r |  rail 2   |   |   |   |   |   |   |   |
+---+---+---+---+-n-+---+---+-c-+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |ML1|   |   |ML1| a |   |RL0|RM0|RR0|   |   |   |   |   |   |   |
|   |   |   | y | u |   | y | t |   | c | r | g |   |   |   |   |   |   |   |
+---+---+---+---+-s-+---+---+-e-+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |MX | e |MR0|MX | d |MR0|RL2|RM2|RR2|MX |ML1|   |ML3|   |ML1|   |
|   |   |   | c |   | k | y |   | k | c | g | y | c | y |   | c |   | y |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |RR1|RR0|RR2|RR1|RR0|RR2|RR1|RR0|RL1|RM1|RR1|   | shaft in use  |  >|   |
|  r|   | g | y | r | g | y | y | g | g | b | r |   |   |   |   |   |   |   |
+--a+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| <i|RM1|RM0|RM2|RM1|RM0|RM2|RM1|RM0|RL0|RM0|RR0|MR0|   |MR2|   |MR0|   |   |
|  l| b | r | g | b | r | g | b | r | c | r | g | k |   | r |   | k |   |   |
+-- +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|  3|RL1|RL0|RL2|RL1|RL0|RL2|RL1|RL0|*Q*|RL0|RL1|RL2|RL0|RL1|RL2|RL0|RL1|   |
|   | g | c | c | g | c | c | g | c |clk| c | g | c | c | g | c | c | g |r  |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+a--+
|   |   |   |   |   |   |   |G3 |Grd|Sec|RM0|RM1|RM2|RM0|RM1|RM2|RM0|RM1|i >|
|   |   |   |   |   |   |   | k |r/y|clk| r | b | g | r | b | g | r | b |l  |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ --+
|   |   |   |   |   |   |   |G4 |G5 |G6 |RR0|RR1|RR2|RR0|RR1|RR2|RR0|RR1|1  |
|   |   |   |   |   |   |   | r | k | b | g | y | y | g | r | y | g |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |MR0|   |MX |MR0|   |MX |   |   |   |
|   |   |   |   |   |   |   |   |   |   | k |   | y | k |   | c |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

(zeigt MX- und RR1-Farben während und nach der ersten Abfahrt durch einen Junior-Bergmann).

Es gibt Dutzende von losen Enden. (ZB reinigen wir Lebensmittel von der Schiene, weil dies ein Hindernis für beladene Bergleute sein könnte, aber einige Lebensmittel von der Schiene werden nicht gegessen, da die zusätzliche Komplexität es einfach nicht wert zu sein schien.) Das ist gut genug der Feind des Besseren ...

v1.1 behebt einen Grund für die Disqualifikation und fügt ein paar kleinere Verbesserungen hinzu.

v1.2 fügt ein paar weitere Korrekturen und Verbesserungen hinzu.

v1.3 fügt zuvor mischende varDeklarationen hinzu, damit dies auf Controllern im strikten Modus wie Dave's funktioniert . keine funktionalen Änderungen.

v1.4 behebt ein Missverständnis zwischen Königin und Gärtner über die Verwendung von Knoblauch (wie Vampire, die auf Schiene 3 eintreffen, herausfinden werden), behebt einen dummen Fehler bei der Definition von Mustern und verbessert einige Randfälle.

v1.5 bringt der Windmühle einen neuen Trick bei - wollen Sie eine Ameise sehen?

Mit v1.6 können Bergleute Muster in große grünliche Flächen kritzeln, den Einbruchalarm zu Hause verfeinern und mit feindlichen Königinnen an anderer Stelle auf etwas elastischere Weise sowie mit geringfügigen Ausbesserungen umgehen .

Abgesehen von kleineren Resilienz-Korrekturen verwendet v1.7 eine Lightspeed- artige Startphase, um nicht früher abzurechnen, sondern um mit einem größeren Vorrat an Nahrungsmitteln abzurechnen. (Wir brauchen 7 Bergleute, um die erwartete Lebensmittelrücklaufrate eines Tandems mit Lichtgeschwindigkeit zu überschreiten. Es macht also keinen Sinn, auf Bergbau umzusteigen, bevor wir sie uns leisten können.)

v1.8 behebt einen Deadlock in der Lightspeed- Phasenlogik und behebt, was noch wichtiger ist, einen in v1.6 eingeführten Laichfehler, der zur Disqualifikation geführt hat.

v1.9 behebt einen weiteren exotischen Disqualifizierungsfehler, behebt vorläufig einige Staufälle in Schächten und versucht, mit den neuesten vampirischen Erfindungen umzugehen.

Mit v2.0 kann die Königin in dringenden Notfällen einen bestehenden Hub ganz aufgeben, auf Scrambling im Lightspeed-Stil zurückgreifen und sich mit etwas Glück später wieder niederlassen, um eine neue Mühle weit entfernt vom ursprünglichen Standort zu gründen. Experimente mit einem Food Controlled Oscillator mit variabler Geschwindigkeit führten dagegen nicht zu überzeugenden Verbesserungen. Das frühere Aussenden von mehr Bergleuten würde die Unfallrate einiger Wettbewerber, aber auch die unserer Mitbewerber, etwas erhöhen. Der FCO-Code bleibt bestehen, wurde jedoch vorerst deaktiviert.

v2.1 behebt drei Randfälle, in denen Arbeiter besser umziehen als sitzen bleiben mussten.


Der Grund, warum Bergmannswellen so sind wie sie sind, liegt am Hinterradierer: Bei meinem Design ist es sehr unwahrscheinlich, dass ein Hinterradierer, der eine Welle findet, dazu führt, dass sie auf die Schiene zurückgeführt wird, während Ihre zu mindestens 50% besteht einen Radiergummi auf die Schiene zu führen. Ich habe mindestens eine halbe Stunde lang nach einem besseren
Wellenmuster gesucht,

Ja, das ist ein Punkt. Auf der anderen Seite können sich Radiergummis mit der Schiene langweilen und über die Schachtwände im Zick-Zack- Verhältnis davonlaufen ... Das Wechselmuster ist robuster beim Bohren in schwierigem Gelände (obwohl nicht unfehlbar).
GNiklasch

Bergleute auf einer Schiene profitieren manchmal sogar von Trail-Eraser: Sie ermöglichen es den Bergleuten auf der Schiene, Schächte neu zu erkunden, ohne dies wirklich beabsichtigen zu müssen. Und Schienen und Schächte können repariert werden. Unfälle können und werden passieren. Wiederherstellungsoptionen sind wichtig. MoaR's sind schon beeindruckend!
GNiklasch

1
Ich liebe die Erklärung. Insbesondere die Verwendung eines Arbeitskrafttyps, um je nach lokalem Kontext zwei unterschiedliche Verhaltensweisen zu erzeugen.
Trichoplax

1
@trichoplax Whoops - danke für das Heads-up! Ursache bekannt (tiefgestellt um zwei), siehe Problem Nr. 6 . Ich werde die Gelegenheit nutzen, um ein paar andere kleine Fehler zu beheben. Aktualisiert meine Antwort nach einigen Regressionstests und erneutem Minimieren des Codes.
GNiklasch

12

Schwarzes Loch

var COLOR=8
var COLOR2=7
var COLOR3=2
var LOCKDOWN=8
var orthogonals = [1, 3, 7, 5]
var isQueen = view[4].ant.type==5
var rotationsCW = [1,2,5,8,7,6,3,0]
var rotationsCCW = [3,6,7,8,5,2,1,0]
var matchStates = [
    {state:[0,0,0,
            0,1,0,
            0,0,0],move:0,back:0,fill:true},
    {state:[1,0,0,
            0,1,0,
            0,0,0],move:1,back:3,fill:true},
    {state:[0,1,0,
            1,0,0,
            0,0,0],move:0,back:0,fill:false},
    {state:[0,1,0,
            0,0,1,
            0,0,0],move:2,back:2,fill:false},
    {state:[0,0,1,
            0,0,1,
            0,1,0],move:8,back:2,fill:false},
    //5:
    {state:[1,0,0,
            1,0,0,
            0,1,0],move:2,back:6,fill:false},
    {state:[0,1,0,
            0,1,0,
            0,0,0],move:2,back:0,fill:false},
    {state:[1,0,0,
            1,1,0,
            0,0,0],move:1,back:6,fill:false},
    {state:[0,1,1,
            0,1,0,
            0,0,0],move:5,back:0,fill:false},
    {state:[0,1,0,
            1,1,0,
            0,0,0],move:2,back:6,fill:false},
    //10:
    {state:[1,1,0,
            0,1,0,
            0,0,0],move:2,back:3,fill:false},
    {state:[1,1,0,
            1,1,0,
            0,0,0],move:2,back:6,fill:false},
    {state:[0,1,1,
            0,1,1,
            0,0,0],move:8,back:0,fill:false},
    {state:[1,1,1,
            0,1,0,
            0,0,0],move:5,back:3,fill:false},
    {state:[1,0,0,
            1,1,0,
            1,0,0],move:1,back:7,fill:false},
    //15:
    {state:[0,1,0,
            1,1,1,
            0,0,0],move:8,back:6,fill:false},
    {state:[1,1,1,
            1,1,0,
            0,0,0],move:5,back:6,fill:false},
    {state:[1,0,0,
            1,1,0,
            1,1,0],move:1,back:8,fill:false},
    {state:[1,1,0,
            1,1,0,
            1,0,0],move:2,back:7,fill:false},
    {state:[1,1,1,
            0,1,1,
            0,0,0],move:8,back:3,fill:false},
    //20:
    {state:[1,1,1,
            0,0,1,
            0,0,1],move:7,back:3,fill:true},
    {state:[1,1,1,
            1,0,0,
            1,0,0],move:5,back:7,fill:true},
    {state:[1,0,0,
            1,0,0,
            1,1,1],move:1,back:5,fill:true},
    {state:[0,0,1,
            0,0,1,
            1,1,1],move:3,back:1,fill:true},
    {state:[1,1,1,
            0,1,1,
            0,0,1],move:7,back:3,fill:true},
    //25:
    {state:[1,1,1,
            1,1,0,
            1,0,0],move:5,back:7,fill:true},
    {state:[1,0,0,
            1,1,0,
            1,1,1],move:1,back:5,fill:true},
    {state:[1,1,1,
            1,1,1,
            0,0,0],move:8,back:6,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,0,0],move:8,back:7,fill:true},
    {state:[1,1,1,
            1,1,0,
            1,1,0],move:5,back:8,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,1,0],move:8,back:8,fill:true},
    //30:
    {state:[1,1,1,
            1,1,1,
            1,0,1],move:7,back:7,fill:true},
    {state:[1,1,1,
            1,1,0,
            1,1,1],move:5,back:5,fill:true},
    {state:[1,0,1,
            1,1,1,
            1,1,1],move:1,back:1,fill:true},
    {state:[1,1,1,
            0,1,1,
            1,1,1],move:3,back:3,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,1,1],move:9,back:9,fill:false},
    //35:
]
function matchesColor(c) {
    return c==COLOR || c==COLOR2 || c==COLOR3 || (view[4] == COLOR3 && c == LOCKDOWN)
}
function matchesNonLineColor(c) {
    return c==COLOR || c==COLOR2
}
function isAnyColor(c) {
    var r=0
    for(var i=0;i<9;i++) {
        if(view[i].color == c) r++
    }
    return r
}
function howManyAnts() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(view[i].ant != null) r++
    }
    return r
}
function deRotate(m, amt) {
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8]
    amt = -amt
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8]
}
function deRotateSide(m, amt) {
    return deRotate(m,amt*2)
}
function matchWhileLost(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(view[i].color == COLOR3) c++
        if(view[i].color == COLOR3 && i%2 == 0) c+=10
    }
    if(c == 2) {
        if(view[0].color == COLOR3 || view[2].color == COLOR3 || view[6].color == COLOR3 || view[8].color == COLOR3) {
            return {cell:4,color:COLOR3}
        }
        if(view[0].ant == null)
            return {cell:0}
        if(view[2].ant == null)
            return {cell:2}
        if(view[6].ant == null)
            return {cell:6}
        if(view[8].ant == null)
            return {cell:8}
    }
    c = 0
    sides[4] = 0
    var toMatch =[{state:[1,1,1,
                         2,0,2,
                         0,1,0]},
                 {state:[0,2,1,
                         1,0,1,
                         0,2,1]},
                 {state:[0,1,0,
                         2,0,2,
                         1,1,1]},
                 {state:[1,2,0,
                         1,0,1,
                         1,2,0]}]
    for(var m=0;m<4;m++) {
        var score=0
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(sides[j] == COLOR3 && toMatch[m].state[j] == 1) {
                    score++
                }
                if(sides[j] != COLOR3 && (toMatch[m].state[j] == 0 || toMatch[m].state[j] == 2)) {
                    score++
                }
                if(sides[j] == COLOR3 && toMatch[m].state[j] == 2) {
                    score--
                }
            }
        }
        if(score >= 6) {
            var clearOrder=[1,0,2]
            for(var r=0;r<clearOrder.length;r++) {
                var s = deRotateSide(clearOrder[r],m)
                if(view[s].color == COLOR3) {
                    if(view[s].ant == null)
                        return {cell:s,color:COLOR}
                    else
                        return {cell:4}
                }
            }
        }
    }
    return null
}
function matchBlueStyle(sides) {
    return null
}
function bestMatch(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(sides[i] > 1) c++
    }
    if(!isQueen && view[4].ant.food > 0) {
        c++
        sides[4] = 8
    }
    if(c <= 1) {
        return {state:matchStates[0],rot:0,fill:matchStates[0].fill}
    }
    c = 0
    while(!matchesColor(sides[0]) && !matchesColor(sides[1]) && c < 4) {
        var s2 = [0,0,0,0,0,0,0,0,0]
        s2[0] = sides[2]
        s2[1] = sides[5]
        s2[2] = sides[8]
        s2[3] = sides[1]
        s2[5] = sides[7]
        s2[6] = sides[0]
        s2[7] = sides[3]
        s2[8] = sides[6]
        sides = s2
        c++
    }
    while(c < 8 && (matchesColor(sides[0]) || matchesColor(sides[1])) && matchesColor(sides[8])) {
        var s2 = [0,0,0,0,0,0,0,0,0]
        s2[0] = sides[2]
        s2[1] = sides[5]
        s2[2] = sides[8]
        s2[3] = sides[1]
        s2[5] = sides[7]
        s2[6] = sides[0]
        s2[7] = sides[3]
        s2[8] = sides[6]
        sides = s2
        c++
    }
    var bestState = null
    var bestMatchScore = -1
    for(var i = 0; i < matchStates.length; i++) {
        var score=0
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(matchesColor(sides[j]) && matchStates[i].state[j] == 1) {
                    score++
                }
                if(!matchesColor(sides[j]) && matchStates[i].state[j] == 0) {
                    score++
                }
            }
        }
        if(score >= bestMatchScore) {
            //console.log("state " + i + ": " + score);
            bestMatchScore = score
            bestState = matchStates[i]
        }
    }
    return {state:bestState,rot:c,fill:bestState.fill,score:bestMatchScore}
}
function getHighestWorker() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type > r) r = view[i].ant.type
        }
    }
    return r
}
function pathLost() {
    var i, j
    var safe = []
    for(var q=0;q<9;q++) {
        if(q != 4 && view[q].ant != null && view[q].ant.friend && (view[q].ant.type > view[4].ant.type && view[4].ant.food == 0 && view[q].ant.type < 5)) {
            if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
            return {cell:4}
        }
    }
    if (matchesNonLineColor(view[4].color)) {
        var myView = [0,0,0,0,0,0,0,0,0]
        for(var i=0; i < 9; i++) {
            myView[i] = view[i].color
            if(!isQueen && view[4].ant.food > 0 && view[i].food > 0) {
                myView[i] = COLOR;
            }
        }
        var ret = matchWhileLost(myView)
        if(ret == null)
            return {cell:4, color:COLOR3}
        else {
            if(!(view[ret.cell].ant != null && view[ret.cell].ant.friend == false) && (view[4].ant.food == 0 || view[ret.cell].food == 0 || isQueen))
                return ret
        }
    }
    for (i=0; i<view.length; i++) {
        if (view[i].ant === null && (view[4].ant.food == 0 || view[i].food == 0 || isQueen)) {
            safe.push(i);
        }
    }
    for (i=0; i<4; i++) {
        j = (i+2) % 4
        if (matchesNonLineColor(view[orthogonals[i]].color) && view[orthogonals[j]].color == COLOR3) {
            if (view[orthogonals[i]].ant == null) {
                return {cell:orthogonals[i]}
            } else if (safe.length > 0) {
                return {cell:safe[0]}
            } else if (view[0].ant === null && (view[4].ant.food == 0 || view[0].food == 0 || isQueen)) {
                return {cell:0}
            }
        }
    }
    if (view[1].ant === null && (view[4].ant.food == 0 || view[1].food == 0 || isQueen)) {
        return {cell:1}
    } else {
        if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
        return {cell:4}
    }
}
function isAllyAdjacentTo(view, place) {
    var i = deRotate(place, 1)
    var j = deRotate(place, -1)
    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type < 5) return 1
    if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return 1
    if(orthogonals.indexOf(place) >= 0) {
        i = deRotate(place, 2)
        j = deRotate(place, -2)
        if(view[i].ant != null && view[i].ant.friend && view[i].ant.type < 5) return 2
        if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return 2
    }
    return 0
}
function findOpenSpace(pos, dir) {
    if(pos > 8 || pos < 0) return pos
    if(view[pos].ant != null && view[pos].ant.friend && view[4].ant.food == 0) {
        pos=deRotate(pos,4)
    }
    //var inc = dir>0?1:-1
    var b = 0
    while(view[pos].ant != null && b < 8) {
        pos=deRotate(pos,dir)
        b++
    }
    return pos
}
//end functions
function getReturn() {
    var colToPlace=COLOR
    var blueAmt = isAnyColor(COLOR2)
    var myView = [0,0,0,0,0,0,0,0,0]
    for(var i=0; i < 9; i++) {
        myView[i] = view[i].color
        if(!isQueen && view[4].ant.food > 0 && view[i].food > 0) {
            myView[i] = COLOR;
        }
        if(!isQueen && view[4].ant.food == 0 && view[i].ant != null && view[i].ant.food > 0) {
            if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
            return {cell:4};
            //myView[i] = COLOR;
        }
        if(view[i].ant != null && !view[i].ant.friend) {
            myView[i] = COLOR;
        }
    }
    if(isQueen) {
        for(var i=0; i < 9; i++) {
            if(i != 4 && !matchesColor(view[i].color) && view[i].ant != null) {
                myView[i] = COLOR
            }
        }
    }
    //console.log("view:")
    //console.log(myView)
    //console.log("1")
    var match = bestMatch(myView)
    if(match.state.move != 9) {
        var ctY = 0
        var lastY = -1
        var ctW = 0
        var lastW = -1
        for(var i=0; i < 9; i++) {
            if(view[i].color == COLOR3) {
                myView[i] = 8
                ctY++
                lastY = i
            }
            else if(!matchesColor(view[i].color)) {
                ctW++
                lastW = i
            }
        }
        if(ctY > 0 && isQueen && view[4].ant.food > 0 && ctW >= 1) {
            if(view[4].color != COLOR3 && matchesColor(view[4].color))
                return {cell:4,color:COLOR3}
            var tt = deRotate(lastW,-1)
            if(view[tt].color != COLOR2)
                return {cell:tt,color:COLOR2}
            lastW = findOpenSpace(lastW,1)
            return {cell:lastW}
        }
        else if(ctY >= 2 && ctW >= 3)
            match = bestMatch(myView)
        else if(ctY > 0 && view[lastY].ant == null && ctW >= 3) {
            return {cell:lastY,color:1}
        }
    }
    //console.log("2")
    if(!isQueen) {
        for(var i=0; i < 9; i++) {
            if(view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 0 && view[i].ant.food <= 2) {
                if(view[4].ant.type == 4)
                    return {cell:4,color:COLOR2}
                return {cell:4}
            }
        }
    }
    //console.log("3")
    if(blueAmt > 0 && view[4].color != COLOR3 && match.state.move != 9) {
        //console.log("Some blue")
        var mb = match.state.back
        mb = deRotateSide(mb,match.rot)
        if(!isQueen || view[4].ant.food <= 2) {
            var a = deRotate(mb,1)
            var b = deRotate(mb,-1)//TODO should be -1
            //console.log("mb: " + mb + "," + a + "," + b)
            if(mb != 9 && (view[mb].color == COLOR2 || view[4].color == COLOR2 || view[a].color == COLOR2 || view[b].color == COLOR2)) {
                //blue behind
                //console.log("Blue behind")
                colToPlace = COLOR2
            }
            else {
                //console.log("No blue behind")
                //console.log(match)
                var myView2 = [0,0,0,0,0,0,0,0,0]
                //construct a view without blue in it
                for(var i=0; i < 9; i++) {
                    myView2[i] = view[i].color == COLOR2?1:view[i].color
                }
                var match2 = bestMatch(myView2)
                if(match2.state.move == 9 || match2.state == matchStates[0]) {
                    //zero or one black
                    //console.log("<= 1 Black")
                    //console.log(myView2)
                    //console.log(match2.state)
                    colToPlace = COLOR2
                }
                else if(view[4].ant.type != 4) {
                    var mf = match2.state.move
                    mf = deRotateSide(mf,match2.rot)
                    //console.log("mf: " + mf)
                    if(mf != 9 && view[mf].color == COLOR2 && view[mf].ant == null) {
                        //about to move onto blue
                        //console.log("Moving onto blue")
                        //console.log(view)
                        //console.log(myView2)
                        return {cell:mf,color:1}
                    }
                    var clearOrder=[1,3,5,7,0,2,6,8]
                    for(var r=0;r<clearOrder.length;r++) {
                        var s = deRotateSide(clearOrder[r],0)
                        if(view[s].color == COLOR2 && (view[s].ant == null || !view[s].ant.friend || (isQueen && view[4].ant.food == 0))) {
                            //console.log("DIE BLUE SCUM")
                            //console.log(view)
                            //console.log(myView2)
                            return {cell:s,color:1}
                        }
                        else if(isQueen && view[s].ant != null && view[s].ant.friend) {
                            //console.log("Blue Queen")
                            //console.log(view)
                            //console.log(myView2)
                            return {cell:4,color:COLOR2}
                        }
                    }
                }
            }
        }
        //console.log("Nothing happened")
    }
    //console.log("4")
    if(view[4].ant.type <= 2) {
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && !view[i].ant.friend) {
          var canSeeAlly = isAllyAdjacentTo(view,i)
          if(canSeeAlly == 0) {
            if(view[i].color == LOCKDOWN) {
              var a = deRotate(i, 1)
              var b = deRotate(i, -1)
              if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
              if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              if(orthogonals.indexOf(i) >= 0) {
                a = deRotate(i, 2)
                b = deRotate(i, -2)
                if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
                if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              }
            }
            else {
              return {cell:i,color:LOCKDOWN}
            }
            if(view[4].color == LOCKDOWN || view[4].color == COLOR) {
              var ii = deRotate(i,4)
              ii = findOpenSpace(ii,1)
              return {cell:ii}
            }
            return {cell:4,color:COLOR}
          }
          else if(canSeeAlly == 2) {
            var m = deRotate(i, 2)
            var j = deRotate(i, -2)
            if(view[m].ant != null && view[m].ant.friend && view[m].ant.type < 5) return {cell:m,color:LOCKDOWN}
            if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return {cell:j,color:LOCKDOWN}
          }
          else if(view[4].color == LOCKDOWN || view[4].color == 2) {

          }
          else {
            return {cell:4,color:2}
          }
        }
      }
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && view[i].ant.friend && (view[i].ant.type > view[4].ant.type && view[4].ant.food == 0)) {
          if(match.state.move == 9)
            return {cell:4}
          if(view[i].ant.type == 5)
            return {cell:4}
          var m = findOpenSpace(i,1)
          if(view[m].ant == null)
            return {cell:m}
          return {cell:4,color:2}
        }
      }
    }
    else if(view[4].ant.type <= 4) {
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && !view[i].ant.friend) {
          var canSeeAlly = isAllyAdjacentTo(view,i)
          if(canSeeAlly == 0) {
            if(view[i].color == LOCKDOWN) {
              var a = deRotate(i, 1)
              var b = deRotate(i, -1)
              if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
              if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              if(orthogonals.indexOf(i) >= 0) {
                a = deRotate(i, 2)
                b = deRotate(i, -2)
                if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
                if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              }
            }
            else {
              return {cell:i,color:LOCKDOWN}
            }
            if(view[4].color == LOCKDOWN || view[4].color == COLOR) {
              var ii = deRotate(i,4)
              ii = findOpenSpace(ii,1)
              return {cell:ii}
            }
            return {cell:4,color:COLOR}
          }
          else if(canSeeAlly == 2) {
                var m = deRotate(i, 2)
                j = deRotate(i, -2)
                if(view[m].ant != null && view[i].ant.friend && view[m].ant.type < 5) return {cell:m,color:LOCKDOWN}
                if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return {cell:j,color:LOCKDOWN}
          }
          else if(view[4].color == LOCKDOWN || view[4].color == 2) {

          }
          else {
            return {cell:4,color:2}
          }
        }
      }
    }
    else if(view[4].ant.food > 4) {
        for(var i=0; i < 9; i++) {
            if(view[i].ant != null && !view[i].ant.friend) {
                var canSeeAlly = isAllyAdjacentTo(view,i)
                if(canSeeAlly == 0) {
                    var m = findOpenSpace(i,1)
                    if(view[m].ant == null)
                        return {cell:m,type:1}
                    return {cell:4,color:3}
                }
            }
        }
        var high = getHighestWorker()
        if(high >= 3 && view[4].ant.food % 2 == 1 && view[4].ant.food < 40) {
            var typeToSpawn = 1
            if(view[4].ant.food < 10 && high == 4 && view[4].ant.food % 4 == 1) {
                typeToSpawn = 3
            }
            var m = findOpenSpace(0,1)
            var canSeeAlly = isAllyAdjacentTo(view,m)
            if(canSeeAlly == 0 && view[m].ant == null)
                return {cell:m,type:typeToSpawn}
        }
    }
    //console.log("5")
    var m = match.state.move
    if(isQueen && view[4].ant.food > 0 && view[4].ant.food <= 2 && isAnyColor(COLOR2) == 0 && isAnyColor(COLOR3) == 0 && m < 9) {
        var high = getHighestWorker()+1
        var num = howManyAnts();
        //high += Math.max(num-2,0)
        if(high < 5) {
            m = deRotate(m,match.rot+4) //get space behind
            m = findOpenSpace(m,1) //make sure its open
            if(view[m].ant == null && view[m].food == 0)
                return {cell:m,type:high}
            return {cell:4}
        }
        else {
            //return {cell:9}
            colToPlace = COLOR2
        }
    }
    if(!isQueen && view[4].ant.food > 0 /*&& view[4].ant.type >= 3*/) {
        //console.log("type 3")
        m = match.state.back
        //console.log(m)
        colToPlace = COLOR
    }
    if(view[4].ant.type == 3) {
        colToPlace = COLOR
    }
    //console.log("6")
    if(!matchesColor(view[4].color) && !(!isQueen && view[4].ant.food)) {
        //console.log("6a")
        /*for(var j=0; j < 9; j++) {
            if(j != 4 && view[j].ant != null && view[j].ant.friend && view[j].ant.food > 0 && j != match.back) {
                m = match.state.move
                if(m < 9) {
                    m = findOpenSpace(m,1)
                    if(view[m].ant == null)
                        return {cell:m}
                    return {cell:4}
                }
                return {cell:4,color:colToPlace}
            }
        }*/
        if(isQueen && view[4].color == LOCKDOWN) {
          m = deRotateSide(m,match.rot)
          m = findOpenSpace(m,1)
          return {cell:m}
        }
        return {cell:4,color:colToPlace}
    }
    if(match.fill && !matchesColor(view[4].color)) {
        return {cell:4,color:colToPlace}
    }
    if(m >= 9) {
        if(!matchesColor(view[4].color)) {
            return {cell:4,color:COLOR}
        }
        //console.log("lost! " + view[4].ant.food);
        //console.log(pathLost());
        return pathLost()
    }
    //console.log("7")
    //console.log("m0: " + m + "+" + match.rot)
    m = deRotateSide(m,match.rot)
    //console.log("m1: " + m)
    m = findOpenSpace(m,1)
    //console.log("m2: " + m)
    if(view[4].ant.food > 0 && !matchesColor(view[4].color) /*&& (view[4].ant.type >= 3)*/) {
        var anyFood = false
        for(var x=0;x<9;x++) {
            if(view[x].food > 0) anyFood = true;
        }
        if(!anyFood)
            return {cell:4,color:colToPlace}
    }
    //console.log("m3: " + m)
    m = findOpenSpace(m,1)
    //console.log("m4: " + m)
    if((!isQueen && view[4].ant.food > 0 && view[m].food > 0) && view[m].ant == null) return {cell:4}
    return {cell:m}
}
var ret = getReturn()
ret = sanityCheck(ret)
return ret
function sanityCheck(ret) {
    if(!ret || ret.cell < 0 || ret.cell > 8 || (ret.cell != 4 && (ret.color == null || ret.color == 0) && view[ret.cell].ant != null) || (view[ret.cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) {
        return {cell:4}
    }
    if(ret.type && (view[ret.cell].ant != null || view[ret.cell].food > 0)) {
        return {cell:4}
    }
    return ret;
}

Dies ist eine massive, massive Ameisenfunktion. Es macht das:

Das schwarze Loch

Die Funktionalität ist sehr einfach. Der obere Rand des Codeblocks definiert ein matchStatesObjekt, anhand dessen die Ameisen erkennen, in welche Richtung sie blicken. Auf diese Weise kreisen sie um das bekannte erkundete Gebiet. Dann ein paar Hilfsfunktionen (passende Farben, Ameisen zählen, etc).

bestMatch()Nimmt die Ansicht der Ameise auf (veränderlich) und findet die beste Übereinstimmung in matchStatesund gibt die beste Übereinstimmung zurück.

Queeny macht eine Sache, während sie sich bewegt und sich schwarz legt:

  • Bilden Sie Arbeiter, bis sie geht, um einen Arbeiter zu machen, der eine Königin sein würde, und wechselt dann zum Platzieren von Blau. Jede Ameise, die eine Farbe platziert, die in der Nähe Blau sieht, platziert stattdessen Blau.
  • Wenn die Königin blau sieht, hortet sie Essen.

Die Arbeiter vom Typ 1 und 2 verhalten sich wie die Königin, bis sie etwas zu Essen finden. Dann geben sie die Farbgebung auf, bis sie um den Kreis herumgelaufen sind und der Königin ihr Essen gegeben haben.

Arbeiter vom Typ 3 und 4 verhalten sich wie Königinnen, bis sie etwas zu Essen finden, und arbeiten dann rückwärts um den Kreis (wobei sie immer noch Farbe platzieren), bis sie ihr Essen an die Königin übergeben.

Jede Ameise, die sich selbst als verloren pathLost()erweist, ruft einen intelligenteren Algorithmus für gerade Linien auf (es handelt sich um die intelligente Funktion für gerade Pfade von Meta mit einigen Änderungen).

Diese Verbesserungen sind:

  • Ameisen vom Typ 1 handeln nach dem Zufallsprinzip und versuchen, Pfade zu löschen (meistens nicht erforderlich, aber Ameisen vom Typ 1 sind langfristig nicht wertvoll, und dies räumt diagonale Schachbretter auf)
  • Nicht-Königinnen werden nicht handeln, wenn sie die Königin sehen können
  • Jedes Mal, wenn die Ameise die Ausrichtung ihres Pfades sehen und bestimmen kann, die auf vorherige Pfade stößt, wird dieser Pfad gelöscht, um sicherzustellen, dass Ameisen ihn wieder finden:

Crossover-Beispiel

Darüber hinaus ist der Großteil des Restes nur die Fehlerbehandlung, um sicherzustellen, dass keine Ameisen illegale Operationen ausführen (Weiterziehen auf andere Ameisen, Weiterziehen auf Nahrung, Laichen von Ameisen auf Nahrung ...), obwohl sich der größte Fehler bei der Behandlung von Codestücken unten befindet :

if(view[4].ant.food > 0 && !matchesColor(view[4].color) && (view[4].ant.type >= 3) || surroundingColor > 6) {
  var anyFood = false
  for(var x=0;x<9;x++) {
    if(view[x].food > 0) anyFood = true;
  }
  if(!anyFood)
    return {cell:4,color:colToPlace}
}

Ameisen vom Typ 3 und 4, die rückwärts laufen, setzen keine Farbe um Lebensmittel, die sich noch auf dem Boden befinden (Lebensmittel werden zum Zwecke der Pfadorientierung ansonsten wie farbige Kacheln behandelt). Darüber hinaus setzen Ameisen vom Typ 1 oder 2, die glauben, eingeklemmt worden zu sein (<= 2 ungefärbte Bereiche in der Ansicht), die Farbe nach unten. Für kleine "Inseln" verlieren sie sich schließlich, anstatt permanent gefangen zu sein.

Das Maximum an Nahrung, das diese Fraktion erhalten kann, hängt nur davon ab, wie schnell sie die Farbe wechselt und wie lange ein Spiel maximal dauert (mindestens 10.000). Mehr Arbeiter sind nicht unbedingt von Vorteil, aber es ist wichtig, mehrere früh zu bekommen. Arbeiter vom Typ 3 und 4 sind am effizientesten (alle 6 Spielschritte 6 Schritte näher an die Königin heranrücken), aber zu frühes Erstellen führt zu weniger Arbeitern insgesamt. Die anfängliche Platzierung hat also einen großen Einfluss, aber da der vom Schwarm kartografierte Bereich mit nur wenigen noch nicht sichtbaren Feldern immer größer wird, greift er nach jedem letzten Stück, auch wenn keine Ameise das Risiko eingeht, verloren zu gehen, um ein Stück aufzunehmen.

Update 7/23

Einige Probleme in bestimmten Randfällen, wie diesem, wurden bemerkt:

Königin will auf einen Feind ziehen

Und sehr kleine Änderungen vorgenommen, um das zu erklären. Behandle feindliche Ameisen und beladene Arbeiter grundsätzlich wie farbige Kacheln.

Update 7/26

Fox, ich weiß nicht mal mehr.

  • Die Behandlung von Fehlern bei verlorenen gelben Spuren wurde überarbeitet, um robuster zu sein
  • Verbesserte Behandlung von Freund-Pfad-Kollisionen, um robuster zu sein
  • Trail Eraser Neutralizer Code hinzugefügt
  • Erkennungs- und Handhabungscode "Blaue Spuren" hinzugefügt [BETA]
  • Gemachte Königin produziert weiterhin Arbeiter, nachdem sie in den Hortmodus gegangen ist (zumindest gelegentlich)
  • Ungültige Umzugshygiene
  • Sonstiger Spaghetti-Code
  • Console.log wurde entfernt
  • Herobrine wurde entfernt
  • Akkretionsdiskette hinzugefügt

Neue Akkretionsscheibe

Ohne die gelben Spuren:

Abzüglich des Gelben

Update 7/26 TEIL 2

  • Komplett überarbeitet das "Was mache ich mit blau?" Code
    • Akkretionsscheibe entfernt
  • Salz und Pfeffer zugeben
  • Behobene Probleme mit "In welche Richtung stehe ich?" Erkennung
    • Kreisform entfernt
    • Quadratische Form hinzugefügt
      • Sieht jetzt langweilig aus
  • Die Immunität gegen Trail-Eraser wurde entfernt
  • Smarts im Code "Ich bin verloren" hinzugefügt, reduziert feststeckende Ameisen

Update 7/31

  • Anti-Trail-Eraser-Code wurde neu hinzugefügt (er ist im "Remove Blue" -Update verloren gegangen)
  • Die Funktion zur Überprüfung der geistigen Gesundheit verhinderte die Färbung von Zellen unter anderen Ameisen
  • Besserer Anti-Radiergummi-Fix: Es werden nicht mehr 3 Arbeiter benötigt, um einem einzelnen Radiergummi entgegenzuwirken

Update 8/4

Kleine Verbesserungen.

  • LOCKDOWN Farbe ist jetzt schwarz
  • Alle Ameisen laufen "rückwärts", um Nahrung zu liefern. Diese Mechanik wird dazu führen, dass weniger Ameisen in "Blasen" von Trail-Eraser gefangen werden
  • Besserer Umgang mit verlorenen Ameisen, die sich nicht festsetzen
  • "Nur Horten" -Schwelle auf 40 gesenkt

Schwächen

  • Löschen.
  • Farbmanipulation.

Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Martin Ender

12

Vampire Mk.8 (Re-Vamped)

Dies wurde in ein Community-Wiki umgewandelt, damit jeder es aktualisieren kann, um andere Opfer anzusprechen. Es verwendet das Konzept von Umgebungen, um die verschiedenen Targeting-Codes voneinander zu trennen. Wenn Sie eine Änderung vornehmen möchten, führen Sie einige Turniere durch, um sicherzustellen, dass Ihr neuer Code die durchschnittliche Punktzahl nicht senkt!


Alle meine Antworten haben die gleichen einfachen Hilfsfunktionen. Suchen Sie nach "Hier beginnt die übergeordnete Logik", um den für diese Antwort spezifischen Code anzuzeigen.

// == Shared low-level helpers for all solutions ==
var QUEEN = 5

var WHITE = 1
var COL_MIN = WHITE
var COL_LIM = 9

var CENTRE = 4

var NOP = {cell: CENTRE}

var DIR_FORWARDS = false
var DIR_REVERSE = true
var SIDE_RIGHT = true
var SIDE_LEFT = false

function sanity_check(movement) {
    var me = view[CENTRE].ant
    if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
        return false
    }
    if(movement.type) {
        if(movement.color) {
            return false
        }
        if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
            return false
        }
        if(view[movement.cell].ant || view[movement.cell].food) {
            return false
        }
        if(me.type !== QUEEN || me.food < 1) {
            return false
        }
        return true
    }
    if(movement.color) {
        if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
            return false
        }
        if(view[movement.cell].color === movement.color) {
            return false
        }
        return true
    }
    if(view[movement.cell].ant && movement.cell != 4) {
        return false
    }
    if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
        return false
    }
    return true
}

function as_array(o) {
    if(Array.isArray(o)) {
        return o
    }
    return [o]
}

function best_of(movements) {
    var m
    for(var i = 0; i < movements.length; ++ i) {
        if(typeof(movements[i]) === 'function') {
            m = movements[i]()
        } else {
            m = movements[i]
        }
        if(sanity_check(m)) {
            return m
        }
    }
    return null
}

function play_safe(movement) {
    // Avoid disqualification: no-op if moves are invalid
    return best_of(as_array(movement)) || NOP
}

var RAND_SEED = (() => {
    var s = 0
    for(var i = 0; i < 9; ++ i) {
        s += view[i].color * (i + 1)
        s += view[i].ant ? i * i : 0
        s += view[i].food ? i * i * i : 0
    }
    return s % 29
})()

var ROTATIONS = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8],
    [6, 3, 0, 7, 4, 1, 8, 5, 2],
    [8, 7, 6, 5, 4, 3, 2, 1, 0],
    [2, 5, 8, 1, 4, 7, 0, 3, 6],
]

function areAdjacent(A, B) {
    if(A == 4 || B == 4 || A == B) return true
    if(A % 2 == 0 && B % 2 == 0) return false
    if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A)
    if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A)
    if(A == 0 && (B == 1 || B == 3)) return true
    if(A == 2 && (B == 1 || B == 5)) return true
    if(A == 6 && (B == 3 || B == 7)) return true
    if(A == 8 && (B == 5 || B == 7)) return true
    return false
}

function try_all(fns, limit, wrapperFn, checkFn) {
    var m
    fns = as_array(fns)
    for(var i = 0; i < fns.length; ++ i) {
        if(typeof(fns[i]) !== 'function') {
            if(checkFn(m = fns[i])) {
                return m
            }
            continue
        }
        for(var j = 0; j < limit; ++ j) {
            if(checkFn(m = wrapperFn(fns[i], j))) {
                return m
            }
        }
    }
    return null
}

function identify_rotation(testFns) {
    // testFns MUST be functions, not constants
    return try_all(
        testFns,
        4,
        (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
        (r) => r
    )
}

function near(a, b) {
    return (
        Math.abs(a % 3 - b % 3) < 2 &&
        Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
    )
}

function try_all_angles(solverFns) {
    return try_all(
        solverFns,
        4,
        (fn, r) => fn(ROTATIONS[r]),
        sanity_check
    )
}

function try_all_cells(solverFns, skipCentre) {
    return try_all(
        solverFns,
        9,
        (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
        sanity_check
    )
}

function try_all_cells_near(p, solverFns) {
    return try_all(
        solverFns,
        9,
        (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
        sanity_check
    )
}

function ant_type_at(i, friend) {
    return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0
}

function friend_at(i) {
    return ant_type_at(i, true)
}

function foe_at(i) {
    return ant_type_at(i, false)
}

function foe_near() {
    for(var i = 0; i < 9; ++ i) {
        if(i !== 4 && view[i].ant && !view[i].ant.friend) {
            return true
        }
    }
    return false
}

function ant_type_near(p, friend) {
    for(var i = 0; i < 9; ++ i) {
        if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
            return true
        }
    }
    return false
}

function move_agent(agents) {
    var me = view[CENTRE].ant
    var buddies = [0, 0, 0, 0, 0, 0]
    for(var i = 0; i < 9; ++ i) {
        ++ buddies[friend_at(i)]
    }

    for(var i = 0; i < agents.length; i += 2) {
        if(agents[i] === me.type) {
            return agents[i+1](me, buddies)
        }
    }
    return null
}

function grab_nearby_food() {
    return try_all_cells((i) => (view[i].food ? {cell: i} : null), true)
}

function go_anywhere() {
    return try_all_cells((i) => ({cell: i}), true)
}

function colours_excluding(cols) {
    var r = []
    for(var i = COL_MIN; i < COL_LIM; ++ i) {
        if(cols.indexOf(i) === -1) {
            r.push(i)
        }
    }
    return r
}

function generate_band(start, width) {
    var r = []
    for(var i = 0; i < width; ++ i) {
        r.push(start + i)
    }
    return r
}

function colour_band(colours) {
    return {
        contains: function(c) {
            return colours.indexOf(c) !== -1
        },
        next: function(c) {
            return colours[(colours.indexOf(c) + 1) % colours.length]
        },
        prev: function(c) {
            return colours[(colours.indexOf(c) + colours.length - 1) % colours.length]
        }
    }
}

function random_colour_band(colours) {
    return {
        contains: function(c) {
            return colours.indexOf(c) !== -1
        },
        next: function() {
            return colours[RAND_SEED % colours.length]
        }
    }
}

function fast_diagonal(colourBand) {
    var m = try_all_angles([
        // Avoid nearby checked areas
        (rot) => {
            if(
                !colourBand.contains(view[rot[0]].color) &&
                colourBand.contains(view[rot[5]].color) &&
                colourBand.contains(view[rot[7]].color)
            ) {
                return {cell: rot[0]}
            }
        },

        // Go in a straight diagonal line if possible
        (rot) => {
            if(
                !colourBand.contains(view[rot[0]].color) &&
                colourBand.contains(view[rot[8]].color)
            ) {
                return {cell: rot[0]}
            }
        },

        // When in doubt, pick randomly but avoid doubling-back
        (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

        // Double-back when absolutely necessary
        (rot) => ({cell: rot[0]})
    ])

    // Lay a colour track so that we can avoid doubling-back
    // (and mess up our foes as much as possible)
    if(!colourBand.contains(view[CENTRE].color)) {
        var prevCol = m ? view[8-m.cell].color : WHITE

        var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
        for(var i = 0; i < 9; ++ i) {
            ++ colours[view[i].color]
        }

        return {cell: CENTRE, color: colourBand.next(prevCol)}
    }

    return m
}

function checkAllNearEnvirons(colours, buddies) {
        var nearMoves = [victims.length]
        for(var e = 0; e < victims.length; e++) {
                var env = victims[e]
                nearMoves[e] = null
                if(env.near_nest(colours)) {
                        nearMoves[e] = env.near_nest_move(colours, buddies)
                }
        }
        return best_of(nearMoves)
}

function follow_edge(obstacleFn, side) {
    // Since we don't know which direction we came from, this can cause us to get
    // stuck on islands, but the random orientation helps to ensure we don't get
    // stuck forever.

    var order = ((side === SIDE_LEFT)
        ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
        : [0, 1, 2, 5, 8, 7, 6, 3, 0]
    )
    return try_all(
        [obstacleFn],
        order.length - 1,
        (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
        sanity_check
    )
}

function start_dotted_path(colourBand, side, protectedCols) {
    var right = (side === SIDE_RIGHT)
    return try_all_angles([
        (rot) => ((
            !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
            !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
            !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
            !colourBand.contains(view[rot[1]].color)
        )
            ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
            : null)
    ])
}

function lay_dotted_path(colourBand, side, protectedCols) {
    var right = (side === SIDE_RIGHT)
    return try_all_angles([
        (rot) => {
            var ahead = rot[right ? 2 : 0]
            var behind = rot[right ? 8 : 6]
            if(
                colourBand.contains(view[behind].color) &&
                !protectedCols.contains(view[ahead].color) &&
                !colourBand.contains(view[ahead].color) &&
                !colourBand.contains(view[rot[right ? 6 : 8]].color)
            ) {
                return {cell: ahead, color: colourBand.next(view[behind].color)}
            }
        }
    ])
}

function follow_dotted_path(colourBand, side, direction) {
    var forwards = (direction === DIR_REVERSE) ? 7 : 1
    var right = (side === SIDE_RIGHT)

    return try_all_angles([
        // Cell on our side? advance
        (rot) => {
            if(
                colourBand.contains(view[rot[right ? 5 : 3]].color) &&
                // Prevent sticking / trickery
                !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
                !colourBand.contains(view[rot[0]].color) &&
                !colourBand.contains(view[rot[2]].color)
            ) {
                return {cell: rot[forwards]}
            }
        },

        // Cell ahead and behind? advance
        (rot) => {
            var passedCol = view[rot[right ? 8 : 6]].color
            var nextCol = view[rot[right ? 2 : 0]].color
            if(
                colourBand.contains(passedCol) &&
                nextCol === colourBand.next(passedCol) &&

                // Prevent sticking / trickery
                !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
                !colourBand.contains(view[rot[right ? 0 : 2]].color)
            ) {
                return {cell: rot[forwards]}
            }
        }
    ])
}

function escape_dotted_path(colourBand, side, newColourBand) {
    var right = (side === SIDE_RIGHT)
    if(!newColourBand) {
        newColourBand = colourBand
    }

    return try_all_angles([
        // Escape from beside the line
        (rot) => {
            var approachingCol = view[rot[right ? 2 : 0]].color
            if(
                !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
                !colourBand.contains(approachingCol) ||
                colourBand.contains(view[rot[7]].color) ||
                colourBand.contains(view[rot[right ? 6 : 8]].color)
            ) {
                // not oriented, or in a corner
                return null
            }
            return best_of([
                {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
                {cell: rot[right ? 3 : 5]},
                {cell: rot[right ? 0 : 2]},
                {cell: rot[right ? 6 : 8]},
                {cell: rot[right ? 2 : 0]},
                {cell: rot[right ? 8 : 6]},
                {cell: rot[right ? 5 : 3]}
            ])
        },

        // Escape from inside the line
        (rot) => {
            if(
                !colourBand.contains(view[rot[7]].color) ||
                !colourBand.contains(view[rot[1]].color) ||
                colourBand.contains(view[CENTRE].color)
            ) {
                return null
            }
            return best_of([
                {cell: rot[3]},
                {cell: rot[5]},
                {cell: rot[0]},
                {cell: rot[2]},
                {cell: rot[6]},
                {cell: rot[8]}
            ])
        }
    ])
}

function latch_to_dotted_path(colourBand, side) {
    var right = (side === SIDE_RIGHT)

    return try_all_angles([
        (rot) => {
            var approachingCol = view[rot[right ? 2 : 0]].color
            if(
                colourBand.contains(approachingCol) &&
                view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
                !colourBand.contains(view[rot[right ? 5 : 3]].color)
            ) {
                // We're on the wrong side; go inside the line
                return {cell: rot[right ? 5 : 3]}
            }
        },

        // Inside the line? pick a side
        (rot) => {
            var passedCol = view[rot[7]].color
            var approachingCol = view[rot[1]].color
            if(
                !colourBand.contains(passedCol) ||
                !colourBand.contains(approachingCol) ||
                colourBand.contains(view[CENTRE].color)
            ) {
                return null
            }
            if((approachingCol === colourBand.next(passedCol)) === right) {
                return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}])
            } else {
                return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}])
            }
        }
    ])
}


// == High-level logic begins here ==


var TARGET_COLOURS_ZIG = colour_band([4, 5, 7, 8])
var TARGET_COLOURS_FIREFLY = colour_band([2, 5, 8])
var GROUND_COLOURS_BH = colour_band([2, 7, 8])
var SAFE_COLOURS = random_colour_band([8])

var THIEF = 1
var BOUNCER = 2
var LANCE = 4
var LANCE_TIP = 3

var INITIAL_GATHER = 12

function colour_band_prev(band, base) {
    if(!band.contains(base)) {
        return band.next(WHITE)
    }
    var cur = band.next(base)
    var c
    while((c = band.next(cur)) !== base) {
        cur = c
    }
    return cur
}

function white_near(p) {
    for(var i = 0; i < 9; ++ i) {
        if(near(i, p) && view[i].color === WHITE) {
            return true
        }
    }
    return false
}

function white_near(p, min) {
    var c = 0
    for(var i = 0; i < 9; ++ i) {
        if(near(i, p) && view[i].color === WHITE) {
            if(++c >= min) return true
        }
    }
    return false
}

var TARGET_ARRANGEMENT_RAIL = [
    [8,4,5,8,5,2,4,2,6],
    [8,5,2,4,2,6,6,4,5],
    [4,2,6,6,4,5,8,4,5],
    [6,4,5,8,4,5,8,5,2]
]
var TARGET_NEAR_RAIL = [
    [2,4,0,5,8,0,4,8,0,1], //Not Valid for Worker #1
    [2,6,0,4,5,0,4,5,0,0],
    [4,6,0,2,4,0,5,8,0,0],
    [4,8,0,4,6,0,2,4,0,0],
    [4,5,0,5,2,0,2,6,0,1], //NV 1
    [4,5,0,4,5,0,5,2,0,5], //NV Q
    [5,2,0,2,6,0,4,5,0,0],
    [5,8,0,4,8,0,4,6,0,5]  //NV Q
]
var TARGET_COLOURS_RAIL = colour_band([4,5,2,4])
var rail_miners = {
    name:function() { return "rail_miners"; },
    near_nest: function(colours) {
        var bestScore = 0
        var enemyQueen = false
        // check every rotation for each 3x3 rail possibility
        TARGET_NEAR_RAIL.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var sevenVal = 1
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                    score += (arrangement[i] == 0 && view[rot[i]].color == 7)?sevenVal:0
                    score += (arrangement[i] == 0 && !(view[rot[i]].color == 7 || view[rot[i]].color == 1))?-1:0
                    if(arrangement[i] == 0 && view[rot[i]].color == view[rot[i-2]].color) score -= 2
                    if(view[rot[i]].color) sevenVal = 0
                    enemyQueen |= view[i].ant && view[i].ant.type == QUEEN && !view[i].ant.friend
                    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == THIEF && view[i].color == WHITE) score++
                }
                if(score > bestScore && arrangement[9] != view[4].ant.type) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= (5 - (enemyQueen && view[4].ant.type == 1?1:0))) {
            if(highway.likely_nest(colours)) return false
            return true
        }
        return false
    },
    worth_leeching: function(myFood, buddies) {
        var numFours = 0
        var foodNeed = 11
        for(var i = 0; i < 9; i++) {
            if(foe_at(i) == 4) numFours++
        }
        if(!buddies[THIEF]) return false
        if(view[4].ant.type != 5 && buddies[QUEEN] && myFood < 500 && myFood+buddies[THIEF] > (foodNeed-numFours*3)) return true
        return myFood < 500 && myFood >= (foodNeed-numFours*3)
    },
    near_nest_move: function(colours, buddies) {
        var victim_pos = -1
        var avoid_pos = -1
        var friend_pos = -1
        for(var i = 0; i < 9; i++) {
            if(foe_at(i) == QUEEN) victim_pos = i
            if(foe_at(i) > 0 && foe_at(i) < 4) avoid_pos = i
            if(friend_at(i) == THIEF) friend_pos = i
        }
        if(victim_pos >= 0) return rail_miners.follow_victim(view[4].ant, buddies, colours, victim_pos)
        if(view[4].ant.type == THIEF && buddies[QUEEN]) return NOP
        if(view[4].ant.type == QUEEN && rail_miners.worth_leeching(view[4].ant.food, buddies)) {
            if(avoid_pos >= 0 && view[4].color != WHITE) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[0]} : null),
                        (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[3]} : null)
                    ]),
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
                        (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
                    ]),
                    NOP
                ])
            }
            var allowed = [[8,4,8],[4,6,8],[6,8,4],[5,5,6],[6,5,2],[2,6,5]]
            var curr = [view[4].color,view[friend_pos].color,view[8-friend_pos].color]
            var found = false
            allowed.forEach(function (al) {
                if(al[0] == curr[0] && al[1] == curr[1] && al[2] == curr[2]) {
                    found = true
                }
            })
            if(!found) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF && [2,4,5].indexOf(view[rot[2]].color) >= 0 ? {cell: rot[2]} : null),
                        (rot) => (friend_at(rot[1]) === THIEF && [2,4,5].indexOf(view[rot[0]].color) >= 0 ? {cell: rot[0]} : null)
                    ]),
                    NOP
                ])
            }
            return NOP
        }
        return null
    },
    likely_nest: function(colours) {
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        var q = 0
        TARGET_ARRANGEMENT_RAIL.forEach(function (arrangement) {
            var j = 0
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == THIEF && view[i].color == WHITE) score++
                }
                if(score > bestScore) {
                    bestScore = score
                }
                j++
            })
            q++
        })
        if(view[4].ant.type == THIEF && rail_miners.near_nest(colours)) return true
        if(bestScore >= 7) {
            if(highway.likely_nest(colours)) return false
            return true
        }
        return false
    },

    likely_victim: function(victim_pos) {
        return true
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        if(me.type == QUEEN) {
            if(victim_pos % 2 == 0) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (foe_at(rot[0]) === QUEEN && friend_at(rot[5]) == THIEF ? {cell: rot[2]} : null),
                        (rot) => (foe_at(rot[0]) === QUEEN /*&& friend_at(rot[7]) == THIEF*/ ? {cell: rot[6]} : null)
                    ]),
                    NOP
                ])
            }
            else {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (foe_at(rot[1]) === QUEEN && friend_at(rot[2]) == THIEF ? {cell: rot[5], type: THIEF} : null),
                        (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[3], type: THIEF} : null),
                        (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[5], type: THIEF} : null),
                        (rot) => (buddies[THIEF] < 4 && foe_at(rot[1]) === QUEEN ? {cell: rot[2], type: THIEF} : null),
                        (rot) => (buddies[THIEF] < 4 && foe_at(rot[1]) === QUEEN ? {cell: rot[0], type: THIEF} : null)
                    ]),
                    NOP
                ])
            }
        }
        return NOP
    },
    find_victim: function(me, buddies, colours) {
        var forwardCell = -1
        var current = view[CENTRE].color
        var target = TARGET_COLOURS_RAIL.next(current)
        var antitarget = TARGET_COLOURS_RAIL.prev(current)
        var queenPos = -1
        for(var i = 0; i < 9; i++) {
            if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && current != WHITE){
                forwardCell = i
            }
            if(friend_at(i) == QUEEN) queenPos = i
        }
        if(forwardCell < 0 && current == 4) {
            target = 4
            antitarget = 2
            for(var i = 0; i < 9; i++) {
                if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
                    forwardCell = i
                }
            }
        }
        if(me.type == QUEEN) {
            var numEn = 0
            for(var i = 0; i < 9; i++) {
                if(i % 2 == 1 && friend_at(i) == THIEF && friend_at(8-i) == THIEF){
                    if(foe_at(deRotate(i,1)) > 0)
                        return {cell:forwardCell}
                    if(foe_at(deRotate(i,-1)) > 0)
                        return {cell:forwardCell}
                    return NOP
                }
                if(i % 2 == 0 && friend_at(i) == THIEF && friend_at(deRotate(i,2)) == THIEF){
                    return {cell:deRotate(i,3), type:THIEF}
                }
            }
            return wilderness.find_victim(me, buddies, colours)
        }
        else if(forwardCell >= 0) {
            if(friend_at(forwardCell) == QUEEN) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                        (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
                    ]),
                    go_anywhere
                ])
            }
        }
        else if(queenPos>=0 && view[queenPos].color == WHITE && (foe_at(deRotate(queenPos,2)) && foe_at(deRotate(queenPos,-2)))) {
            return wilderness.find_victim(me, buddies, colours)
        }
        if(me.type == THIEF && forwardCell >= 0 && buddies[THIEF] == 1) {
            return wilderness.find_victim(me, buddies, colours)
        }
        return NOP
    }
}

var TARGET_ARRANGEMENT_WIND = [
        [5,4,0,7,6,0,6,4,0],
        [7,6,0,6,4,0,5,4,0],
        [6,4,0,5,4,0,7,6,0]
]
var TARGET_ARRANGEMENT_WINDCENTER = [
        [2,7,6,2,6,4,6,5,4],
        [2,6,4,6,5,4,2,7,6],
        [6,5,4,2,7,6,2,6,4]
]
var WIND_BAND = colour_band([5,6,7])
var windmill = {
    name:function() { return "windmill"; },
    near_nest: function(colours) { return false; },
    near_nest_move: function(colours, buddies) { return null; },
    likely_nest: function(colours) { // Main nest detection
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_WIND.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 5 && view[4].ant.type != THIEF) {
            return true
        }

        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_WINDCENTER.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 8) {
            return true
        }
        var buddies = [0, 0, 0, 0, 0, 0]
        for(var i = 0; i < 9; ++ i) {
            ++ buddies[friend_at(i)]
        }
        return buddies[LANCE] || buddies[LANCE_TIP]
    },
    worth_leeching: function(myFood, buddies) {
        if(view[4].ant.type == THIEF && (buddies[LANCE] > 0 || buddies[LANCE_TIP] > 0)) return true
        return myFood > 5 || (myFood > 1 && buddies[LANCE])
    },
    likely_victim: function(victim_pos) {
        return false
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        // nest is chaotic and varies by direction of approach
        // We'll let the Find Victim logic handle this
        return NOP
    },

    find_victim: function(me, buddies, colours) {
        if(me.type == THIEF) {
            var queenPos = -1
            var lancePos = -1
            var tipPos = -1
            for(var i=0;i<9;i++) {
                if(friend_at(i) == QUEEN) queenPos = i
                if(friend_at(i) == LANCE) lancePos = i
                if(friend_at(i) == LANCE_TIP) tipPos = i
            }
            if(queenPos < 0 || (foe_at(deRotate(queenPos,1)) > 0 && foe_at(deRotate(queenPos,2)) > 0)) {
                if(queenPos < 0)
                    return go_anywhere
                return {cell:8-queenPos}
            }
            if(queenPos % 2 == 1 && tipPos % 2 == 0) {
                return go_anywhere
            }
            if(queenPos % 2 == 0 && lancePos % 2 == 1) {
                return go_anywhere
            }
            if(queenPos % 2 == 1 && foe_at(deRotate(queenPos,-2)) > 0) {
                return go_anywhere
            }
            return NOP
        }
        if(buddies[LANCE_TIP]) {
            var lancePos = -1
            for(var i=0;i<9;i++) {
                if(friend_at(i) == LANCE_TIP) {
                    lancePos = i
                }
            }
            if(buddies[LANCE]) {
                if(friend_at(8-lancePos) == LANCE) {
                    if(foe_at(deRotate(8-lancePos,1)) == 1 || foe_at(deRotate(8-lancePos,2)) == 1) {
                        var ret = NOP
                        if(lancePos % 2 == 1)
                            ret = {cell:deRotate(8-lancePos,-2)}
                        if(lancePos % 2 == 0)
                            ret = {cell:deRotate(8-lancePos,-3)}
                        if(!sanity_check(ret)) {
                            ret = best_of([
                                try_all_cells_near(lancePos, (i) => (ant_type_at(i) == 0 && view[i].color == 6 ? {cell: i} : null), true),
                                NOP
                            ])
                        }
                        return ret
                    }
                    if(foe_at(deRotate(lancePos,-2)) > 0) {
                        return {cell:deRotate(lancePos,2)}
                    }
                    return NOP
                }
                if(friend_at(deRotate(lancePos,3)) == LANCE) {
                    if((view[lancePos].color == 2 && view[4].color == 7) || foe_at(8-lancePos)) {
                        return {cell:deRotate(lancePos,1)}
                    }
                    return NOP
                }
                if(view[4].color == 6 && view[lancePos].color == 6 && friend_at(deRotate(lancePos,1)) == LANCE) {
                    if(foe_at(deRotate(lancePos,2)) > 0) {

                        return {cell:8-deRotate(lancePos,2)}
                    }
                    return NOP
                }
                if(view[lancePos].color == 2 && view[deRotate(lancePos,-3)].color == 5 && friend_at(deRotate(lancePos,-3)) == LANCE) {
                    return NOP
                }
                if(lancePos % 2 == 0) {
                    if(foe_at(deRotate(lancePos,-1)) > 0 && lancePos % 2 == 1) return {cell:deRotate(lancePos,2)}
                    if(view[deRotate(lancePos,-1)].color != 5) return {cell:deRotate(lancePos,-1),color:5}
                    if(view[deRotate(lancePos,-1)].color == 3 && view[4].color == 1) return {cell:4,color:3}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 3) return {cell:4,color:2}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 2) return {cell:4,color:1}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 7 && view[deRotate(lancePos,-1)].ant == null) return {cell:deRotate(lancePos,-1),type:THIEF}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 7) return {cell:4,color:3}
                }
                return {cell:deRotate(lancePos,-1)}
            }
            if(view[4].color == WHITE && view[lancePos].color == WHITE) {
                return {cell:deRotate(lancePos,-2),type:BOUNCER}
            }
            if(view[deRotate(lancePos,-1)].ant != null && view[deRotate(lancePos,-1)].ant.type == 5) {
                return {cell:deRotate(lancePos,2)}
            }
            if(view[4].color == 6 && view[deRotate(lancePos,1)].color == 7) {
                return {cell:deRotate(lancePos,1)}
            }
            if(foe_at(deRotate(lancePos,-2)) > 0 || foe_at(deRotate(lancePos,-3)) > 0) {
                if(foe_at(deRotate(lancePos,-2)) > 0 && foe_at(deRotate(lancePos,3)) > 0 && (foe_at(deRotate(lancePos,-1)) > 0 || foe_at(deRotate(lancePos,4)) > 0)) {
                    return {cell:deRotate(lancePos,1)}
                }
                if(foe_at(deRotate(lancePos,3)) > 0) {
                    return NOP
                }
                return {cell:deRotate(lancePos,1)}
            }
            if(foe_at(deRotate(lancePos,2)) > 0 && view[deRotate(lancePos,-1)].color != 2) {
                return {cell:deRotate(lancePos,-1),color:2}
            }
            if(foe_at(deRotate(lancePos,-1)) > 0) {
                return {cell:deRotate(lancePos,1)}
            }
            if(lancePos % 2 == 1 && friend_at(deRotate(lancePos,-1)) == THIEF) {
                return {cell:deRotate(lancePos,-2)}
            }
            return {cell:deRotate(lancePos,-1)}
        }
        else if(buddies[LANCE]) {
            var lancePos = -1
            for(var i=0;i<9;i++) {
                if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE) {
                    lancePos = i
                }
            }
            if(view[4].color == 3 && lancePos % 2 == 1) return NOP
            var moveNext = lancePos % 2 == 1 ? {cell:deRotate(lancePos,2)} : {cell:deRotate(lancePos,1)}
            if(view[moveNext.cell].ant != null && !view[moveNext.cell].ant.friend) {
                moveNext = {cell:deRotate(lancePos,1),type:LANCE_TIP}
            }
            if(view[lancePos].ant.food > 0) {
                if(lancePos % 2 == 1)
                    return {cell:deRotate(lancePos,4),type:LANCE_TIP}
                else
                    return {cell:deRotate(lancePos,3),type:LANCE_TIP}
            }
            if(view[lancePos].color == 6 && view[moveNext.cell].color == 8 && view[deRotate(lancePos,2)].color == 5) {
                return {cell:moveNext.cell,type:LANCE_TIP}
            }

            return moveNext
        }
        else {
            var current = view[CENTRE].color
            var standOn = WIND_BAND.next(WIND_BAND.next(WIND_BAND.next(current)))
            var target = WIND_BAND.next(current)
            var antitarget = WIND_BAND.next(target)
            if(current != standOn) return wilderness.find_victim(me, buddies, colours)

            var ret = best_of([
                try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && ([2,5,6].indexOf(view[deRotate(i,-1)].color) >= 0) && (view[i].color != 5 || view[deRotate(i,1)].color == 4)) ? {cell: i, type: LANCE} : null), true),
                NOP
            ])
            if(ret.cell == 4) {
                return wilderness.find_victim(me, buddies, colours)
            }
            return ret
        }
        return NOP
    }
}

var TARGET_ARRANGEMENT_HIGHWAY = [
    [2,3,7,6,8,2,3,7,6],
    [2,3,7,7,6,4,4,2,3],
    [2,4,6,7,3,2,4,6,7],
    [3,2,4,4,6,7,7,3,2],
    [3,4,7,7,2,6,6,3,4],
    [3,4,7,2,6,3,4,7,2],
    [3,6,2,2,7,4,4,3,6],
    [4,7,2,2,5,6,3,4,7],
    [4,6,7,2,6,3,3,4,7],
    [4,6,7,7,3,2,2,4,6],
    [6,4,2,3,7,6,4,2,3],
    [7,3,2,2,4,6,6,7,3],
    [7,4,3,6,2,7,4,3,5]
]
var HIGHWAY_BAND = colour_band([2,7,4,3,6])
var HIGHWAY_BAND2 = colour_band([2,3,7,6,4])

var highway = {
    name:function() { return "highway"; },                                     // For debugging
    near_nest: function(colours) { return false; },                // For dodging enemy workers without getting lost
    near_nest_move: function(colours, buddies) { return null; }, // How to move when near_nest is true
    likely_nest: function(colours) { // Main nest detection
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_HIGHWAY.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 7) {
            return true
        }
        if(this.isCenter(colours)) return true

        return false
    },         // Main nest detection
    isCenter: function(colours) {
        var bestScore = 0
        ROTATIONS.forEach(function (rot){
            var score = 0
            for(var i = 0; i < 9; i++) {
                if(i >= 3 && i <= 5 && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 4 || view[rot[i]].color != view[rot[8-i]].color)) {
                    if(i != 4) {
                        score++
                    }
                    else {
                        if(view[rot[3]].color != view[rot[5]].color && view[rot[1]].color == view[rot[7]].color && (view[rot[4]].color != view[rot[1]].color && view[rot[4]].color != view[rot[3]].color && view[rot[4]].color != view[rot[5]].color && view[rot[4]].color != view[rot[7]].color)) {
                            score++
                        }
                    }
                }
                else if(i >= 6) {
                    if(view[rot[i]].color == view[rot[i-6]].color && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 7 || view[rot[i]].color != view[rot[8-i]].color) && view[rot[i]].color != view[4].color) {
                        score += 2
                    }
                }
            }
            if(score > bestScore) {
                bestScore = score
            }
        })
        if(bestScore >= 7) {
            return true
        }
        return false
    },
    worth_leeching:function(myFood, buddies){ return myFood > 80 && myFood < 500; }, // Is this nest worth leeching?
    likely_victim: function(victim_pos) {
        return true
    },   // Identifying the target queen
    follow_victim: function(me, buddies, colours, victim_pos) {
        if(me.type == QUEEN && buddies[THIEF] < 3) {
            return best_of([
                try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
                try_all_cells((i) => ({cell: i, type: THIEF}), true)
            ])
        }
        if(me.type == THIEF && buddies[QUEEN])
            return NOP
        return go_anywhere
    },   // How to handle what happens when the enemy queen is found
    find_victim: function(me, buddies, colours) {
        if(me.type == THIEF && !buddies[QUEEN]) {
            for(var i=0;i<9;i++) {
                if(foe_at(i)) return NOP
            }
            var target = HIGHWAY_BAND.prev(view[4].color)
            var followRail = best_of([
                try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
                NOP
            ])
        }
        else {
            var target = HIGHWAY_BAND.next(view[4].color)
            var followRail = best_of([
                try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
                NOP
            ])
        }
        return followRail
    }                // How to follow the nest
}

var wilderness = {
    name:function() { return "wilderness"; },
    near_nest: function(colours) { return false; },
    near_nest_move: function(colours, buddies) { return null; },
    likely_nest: function(colours) {
        return true
    },
    worth_leeching: function(myFood, buddies) {
        return true
    },
    likely_victim: function(victim_pos) {
        return true
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        // We stumbled across a random queen; make the most of it
        // TODO
        if(rail_miners.near_nest(colours)) {
            return rail_miners.follow_victim(me, buddies, colours, victim_pos)
        }

        // avoids blocking off the rail miner queen from her workers
        // (we'd like to leech her again)
        if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {

            // Make a buddy to help us steal
            return best_of([
                try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
                try_all_cells((i) => ({cell: i, type: THIEF}), true)
            ])
        }
        else if(me.type === QUEEN){
            var enemyCount = 0
            var allyPos = -1
            for(var a=0; a<9; a++) {
                if(a != 4 && view[a].ant != null) {
                    if(view[a].ant.friend) {
                        if(near(a,victim_pos)){
                            allyPos = a
                        }
                    }
                    else if(view[a].ant.type != 5) {
                        enemyCount++
                    }
                }
            }
            if(enemyCount >= buddies[THIEF] && allyPos >= 0) {
                //if next to the queen and we're outnumbered, move back to the center of the rail.
                var target = TARGET_COLOURS_RAIL.prev(view[allyPos].color)
                var best = best_of([
                    try_all_cells((i) => (near(i, victim_pos) && i % 2 == 0 ? {cell: i, type: THIEF} : null), true),
                    try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true)
                ])
                if(best != null) return best

                best_of([
                    try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null))
                ])
                if(best != null) return best

                return best_of([
                    {cell:deRotate(allyPos,1)},
                    {cell:deRotate(allyPos,-1)}
                ])
            }
        }

        return NOP
    },
    find_victim: function(me, buddies, colours) {
        if(me.type === QUEEN) {
            var in_void = true
            for(var i = 0; i < 9; ++ i) {
                if(view[i].color !== WHITE && !SAFE_COLOURS.contains(view[i].color)) {
                    in_void = false
                    break
                }
            }
            if(!in_void) {
                // because of avoiding returning Miner on a Rail workers
                // we dodge sideways and this takes us back onto track
                var nearMove = checkAllNearEnvirons(colours, buddies)
                if(nearMove) return nearMove
            }
            return best_of([
                // Make a buddy once we have a reasonable stash of food so we can
                // search the board faster
                // (but avoid making buddies when there's a potential nest nearby
                // better to wait until we find their queen)
                (!buddies[THIEF] && me.food >= INITIAL_GATHER && in_void) &&
                    try_all_cells((i) => ({cell: i, type: THIEF}), true),

                // Follow buddy in search of victims
                buddies[THIEF] && try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
                    (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
                ]),
                buddies[THIEF] && try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[3]} : null)
                ]),
                buddies[THIEF] && NOP, // Don't lose our buddy!

                // Random walk until we can make a buddy or find the victim
                grab_nearby_food,
                foe_near() ? go_anywhere : fast_diagonal.bind(null, SAFE_COLOURS),
                go_anywhere
            ])
        } else if(me.type === THIEF) {
            return best_of([
                // Lost the queen! Random walk because we have nothing better to do.
                // (don't leave lines; they could disrupt the pattern)
                !buddies[QUEEN] && go_anywhere,
                buddies[BOUNCER] && go_anywhere,
                buddies[THIEF] > 1 && go_anywhere, //untested
                // Follow queen in search of victims
                try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
                ]),
                NOP // Don't lose our buddy!
            ])
        }
    }
}

var victims = [highway, rail_miners, windmill]

function guess_environment(colours, buddies) {
    var food = view[4].ant.food
    if(view[4].ant.type !== QUEEN) {
        for(var i = 0; i < 9; i++) {
            if(i != 4 && view[i].ant && view[i].ant.friend && view[i].ant.type === QUEEN) {
                food = view[i].ant.food
            }
        }
    }
    for(var i = 0; i < victims.length; ++ i) {
        if(victims[i].likely_nest(colours) && victims[i].worth_leeching(food, buddies)) {
            return victims[i]
        }
    }

    return wilderness
}

function is_safe(i) {
    var nearThief = false
    var nearOfficer = false
    for(var j = 0; j < 9; ++ j) {
        if(friend_at(j) === THIEF) {
            nearThief = true
        }
        if(foe_at(j) && foe_at(j) !== QUEEN) {
            nearOfficer = true
        }
    }
    return nearThief && !nearOfficer
}

function move(me, buddies) {
    var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    for(var i = 0; i < 9; ++ i) {
        ++ colours[view[i].color]
    }
    var env = guess_environment(colours,buddies)
    var victim_pos = -1
    var queen_pos = -1
    for(var i = 0; i < 9; ++ i) {
        if(foe_at(i) === QUEEN && env.likely_victim(i) && view[i].ant.food > 0) {
            victim_pos = i
            if(view[i].ant.food > 0) {
                break
            }
        }
        if(friend_at(i) === QUEEN) {
            queen_pos = i
        }
    }

    var in_void = true
    for(var i = 0; i < 9; ++ i) {
        if(view[i].color !== WHITE || (i != 4 && me.type === BOUNCER && friend_at(i) === BOUNCER)) {
            in_void = false
            break
        }
    }
    if(me.type === BOUNCER) {
        if(env === wilderness && in_void) {
            // Our work is done; leave queen and wander at random
            if(buddies[QUEEN]) {
                return best_of([
                    try_all_cells((i) => (ant_type_near(i, true) ? null : {cell: i}), true),
                    go_anywhere
                ])
            }
            return NOP
        }
        else if(env === rail_miners) {
            // Our work is done; leave queen and wander at random
            if(buddies[QUEEN]) {
                var allAngles = try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
                    NOP
                ])
                return best_of([
                    //if next to an enemy queen, move out of the way
                    try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
                    try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
                    allAngles
                ])
            }
            return NOP
        } else if(buddies[QUEEN]) {
            // Escort queen out of nest
            var allAngles = try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
                NOP
            ])

            return best_of([
                //if next to an enemy queen, move out of the way
                try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
                try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
                allAngles
            ])
        }
        else {
            return go_anywhere
        }
    } else if(buddies[BOUNCER]) {
        if(me.type === QUEEN) {
            // Be escorted out of nest
            return try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[2]} : null),
                (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[1]} : null),
                go_anywhere,
                NOP
            ])
        } else {
            // Get out of the way
            return try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[7]} : null),
                (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[8]} : null),
                (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[7]} : null),
                (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[8]} : null),
                go_anywhere
            ])
        }
    }
    if(victim_pos !== -1) {
        // abandon the queen if she's dry.
        // abandon rail miner's queen so she has at least 10 food (otherwise she produces workers 3:4 food she aquires)
        // value is higher than 10 because there's two to three rounds of theft (at 4 ants each) before the queen gets out of range
        // this can still leave the rail miner's queen lower than 10, but unlikely
        // other queens are abandoned if they have less than 5 food, due to the "max 4 ants stealing" and at 0 food, she's not a target.
        if(view[victim_pos].ant.food < 5 || (env == rail_miners && view[victim_pos].ant.food < 28)) {
            if(me.type == THIEF) {
                if(rail_miners.near_nest(colours)) {
                    // we'd rather reuse the workers
                    return NOP
                }
            }
            // Victim is out of food; bounce out of nest
            if(env == rail_miners) {
                if(me.type == QUEEN && me.food < 300 && !buddies[BOUNCER]) {
                    if(friend_at(deRotate(victim_pos,2)) == THIEF && foe_at(deRotate(victim_pos,3)) == 0) return {cell:deRotate(victim_pos,3),type:BOUNCER}
                    if(friend_at(deRotate(victim_pos,-2)) == THIEF && foe_at(deRotate(victim_pos,-3)) == 0) return {cell:deRotate(victim_pos,-3),type:BOUNCER}
                }
                // murder SlM
                return NOP
            }
            var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true)
            if(m) {
                return m
            }
        }
        if(me.type === QUEEN && buddies[THIEF] && !is_safe(CENTRE)) {
            // Try to avoid getting food stolen back from us
            var m = try_all_cells((i) => (is_safe(i) ? {cell: i} : null), true)
            if(m) {
                return m
            }
        }
        return env.follow_victim(me, buddies, colours, victim_pos)
    } else {
        return env.find_victim(me, buddies, colours)
    }
}

// LANCE is only used by windmill targetting, easier to break this out as its own method
function moveLance(me, buddies) {
    var queenPos = -1
    var tipPos = -1
    var enQueenPos = -1
    if(buddies[BOUNCER]) {
        for(var i=0;i<9;i++) {
            if(friend_at(i) == BOUNCER) {
                return {cell:8-i}
            }
        }
    }
    for(var i=0;i<9;i++) {
        if(friend_at(i) == QUEEN) {
            queenPos = i
        }
        if(friend_at(i) == LANCE_TIP) {
            tipPos = i
        }
        if(foe_at(i) == QUEEN) enQueenPos = i
    }
    if(!buddies[QUEEN]) {
        for(var i=0;i<9;i++) {
            if(i % 2 == 0 && friend_at(i) == QUEEN) {
                if(view[deRotate(i,3)].ant != null && view[deRotate(i,3)].ant.friend && view[deRotate(i,3)].ant.type == LANCE_TIP) return NOP
                return {cell:deRotate(i,1)}
            }
        }
        if(!buddies[LANCE_TIP] && !buddies[THIEF] && view[4].color == 2) {
            for(var i = 0; i < 9; ++ i) {
                if(view[i].color == 1) return {cell:i}
            }
        }
        if(enQueenPos >= 0 && enQueenPos % 2 == 0 && foe_at(deRotate(enQueenPos,1)) == 1) {
            return {cell:deRotate(enQueenPos,-3)}
        }
        if(enQueenPos >= 0 && enQueenPos % 2 == 1 && foe_at(deRotate(enQueenPos,2)) == 1) {
            return {cell:8-enQueenPos}
        }
        if(enQueenPos >= 0 && (me.food > 0 || foe_at(deRotate(enQueenPos,-1)) || foe_at(deRotate(enQueenPos,3)))) {
            if(enQueenPos % 2 == 0 && (foe_at(deRotate(enQueenPos,4)) || friend_at(deRotate(enQueenPos,4)) == THIEF)) {
                return {cell:deRotate(enQueenPos,-3)}
            }
        }
        return NOP
    }
    if(buddies[LANCE_TIP]) {
        if(deRotate(queenPos,-1) == tipPos && view[tipPos].color == 8) return {cell:8-tipPos}
        if(deRotate(queenPos,-1) == tipPos) return try_all_cells((i) => (areAdjacent(i,tipPos) && view[i].color == 5 ? {cell:i} : null))
        if(foe_at(8-tipPos) == QUEEN) return {cell:8-tipPos,color:6}
        if(foe_at(8-queenPos) > 0 || foe_at(deRotate(8-queenPos,1)) > 0) return NOP
        return try_all_cells((i) => (!areAdjacent(i,queenPos) && !areAdjacent(i,tipPos) ? {cell:i} : null))
    }
    if(view[4].color != 4 && view[4].color != 6) {
        if(foe_at(8-queenPos) == QUEEN) {
            var formation = try_all_angles.bind(null, [
                (rot) => (foe_at(rot[1]) === 1 && foe_at(rot[2]) === QUEEN ? {cell: rot[3]} : null),
                (rot) => (foe_at(rot[1]) === 1 && foe_at(rot[0]) === QUEEN ? {cell: rot[7]} : null),
                (rot) => (foe_at(rot[1]) === 1 && view[rot[1]].ant.food > 0 && foe_at(rot[6]) === QUEEN && friend_at(rot[2]) === QUEEN ? {cell: rot[5]} : null),
            ])()
            if(formation != null) {
                return formation
            }
            return NOP
        }
        if(foe_at(deRotate(queenPos,1)) > 0 && foe_at(deRotate(queenPos,-1)) > 0) {
            return {cell:deRotate(queenPos,-3)}
        }
        return best_of([
            try_all_cells((i) => (enQueenPos ==-1 && i % 2 == 1 && (view[i].color == 4 || view[i].color == 6) && view[deRotate(i,1)].color != 2 && view[deRotate(i,-1)].color != 2 && areAdjacent(i,queenPos) ? {cell: i} : null), true),
            ((view[4].color != 6 || view[4].color != 4) && queenPos % 2 == 0 && view[deRotate(queenPos,-3)].color == 5) ? {cell:4,color:6} : null,
            NOP
        ])
    }
    else {
        var queenOn = view[8-queenPos].color
        var target = WIND_BAND.next(queenOn)
        var prior = WIND_BAND.next(target)
        var followRail = best_of([
            try_all_cells((i) => (view[deRotate(i,-3)].color == prior && view[deRotate(i,-1)].color == target && areAdjacent(i,queenPos) && (view[i].color == 4 || view[i].color == 6) ? {cell: i} : null), true),
            queenPos % 2 == 1 ? (view[queenPos].color == 4 || view[4].color == 4 ? NOP : {cell:deRotate(queenPos,-2)}) : (view[queenPos].color == 4 || view[queenPos].color == 6 ? {cell:deRotate(queenPos,-1)} : NOP)
        ])

        if(view[deRotate(queenPos,-1)].ant != null) {
            if(!view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant != null && !view[deRotate(queenPos,-2)].ant.friend) {
                return NOP
            }
            if(queenPos % 2 == 0 && !view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant == null && (view[queenPos].color == 3 || view[queenPos].color == WHITE)) {
                return {cell:deRotate(queenPos,-3)}
            }
            if(queenPos % 2 == 0 && friend_at(deRotate(queenPos,-1)) == THIEF && view[deRotate(queenPos,-2)].ant == null &&