Intro
Dies ist ein interaktiver King-of-the-Hill- Wettbewerb, bei dem der Controller vollständig in einem Stapel-Snippet am Ende der Frage enthalten ist. Der Controller liest die Antworten automatisch und spielt die Spiele durch. Jeder kann es jederzeit direkt in seinem Browser ausführen.
Die Mechanik dieses Wettbewerbs ist der von Red vs. Blue - Pixel Team Battlebots sehr ähnlich . Abgesehen davon ist das gespielte Spiel, obwohl es noch gitterbasiert ist, völlig anders. Jedes Spiel ist 1 gegen 1 und es gibt keine Teams. Jeder Beitrag kämpft für sich und nur einer wird der endgültige Champion.
Der Controller verwendet JavaScript. Da JavaScript die einzige clientseitige Skriptsprache ist, die von den meisten Browsern unterstützt wird, müssen alle Antworten auch in JavaScript geschrieben werden .
In dieser Spezifikation wird kursiver Text verwendet, um den formalen Begriff für eine Spielmechanik oder -eigenschaft anzugeben. Diese Begriffe werden durchgehend verwendet, um eine zusammenhängende und klare Art der Bezugnahme auf die verschiedenen Teile des Spiels zu gewährleisten.
Spielweise
Grundlagen
Jede Antwort auf diese Frage repräsentiert einen Spieler . Ein Spiel ist ein Wettbewerb zwischen zwei Spielern, P1 und P2 . Jeder Spieler kontrolliert eine Herde von 8 Bots , die von 0 bis 7 nummeriert sind. Die Spiele finden im Raster statt , in einer Arena mit 128 × 64 Zellen , deren unterste 8 Reihen als Wände (die "Blöcke") beginnen, und in anderen Reihen als Luft . Zellen außerhalb der Gittergrenzen werden als Luft betrachtet.
Die x-Koordinate des Gitters reicht von 0 links bis 127 rechts und y reicht von 0 oben bis 63 unten.
Beispiel Startaufstellung:
Bots bleiben immer an den Rasterzellen ausgerichtet und mehrere Bots können dieselbe Zelle belegen. Bots können nur Luftzellen besetzen. Die Bots von P1 beginnen immer in einer Zeile 0-7 ganz links in der Zeile über den Wänden und die Bots von P2 beginnen immer in einer Zeile 7-0 ganz rechts.
Die Nachbarn eines Bots oder einer Zelle sind die 8 Zellen, die direkt orthogonal und diagonal dazu sind.
Das Sichtfeld ( FOV ) eines Bots ist das 13 × 13-Zellenquadrat, das auf einem Bot zentriert ist. Ein Zellen- oder Feindbot befindet sich im Sichtfeld eines Spielers, wenn es sich im Sichtfeld von mindestens einem der Bots des Spielers befindet.
Bewegungen und Aktionen
Während eines Spiels darf sich jeder Spieler 1000 Mal bewegen . P1 bewegt sich zuerst, dann P2, dann P1 und so weiter, bis insgesamt 2000 Züge ausgeführt wurden. An diesem Punkt endet das Spiel.
Während eines Zuges erhält jeder Spieler Informationen über den Spielstatus und die Gitterzellen und feindlichen Bots in seinem Sichtfeld und entscheidet anhand dieser Informationen über eine Aktion, die jeder seiner Bots ausführen soll.
Die Standardaktion ist nichts zu tun , wenn sich der Bot nicht bewegt oder mit dem Gitter interagiert.
Die anderen Aktionen sind Bewegen , Greifen und Platzieren :
Ein Bot kann bewegt auf eine ihrer Nachbarzellen C , wenn:
- C ist nicht außerhalb der Grenzen,
- C ist Luft (dh keine Wand),
- und mindestens einer von Cs Nachbarn ist eine Mauer.
Im Erfolgsfall wechselt der Bot nach C.
Ein Bot kann eine seiner Nachbarzellen C greifen, wenn:
- C ist nicht außerhalb der Grenzen,
- C ist eine Wand,
- und der bot trägt noch keine mauer.
Bei Erfolg wird C zu Luft und der Bot trägt jetzt eine Wand.
Ein Bot kann in eine seiner Nachbarzellen C legen , wenn:
- C ist nicht außerhalb der Grenzen,
- C ist Luft,
- Keine Bots eines Spielers besetzen C,
- und der bot trägt eine mauer.
Bei Erfolg wird C zu einer Mauer und der Bot trägt keine Mauer mehr.
Erfolglose Aktionen führen zu einem Nichtstun.
Über einer Zelle, die von mindestens einem Bot mit Wänden besetzt ist, ist ein kleines, wandfarbenes Quadrat gezeichnet. Bots starten ohne Wände.
Erinnerung
Während eines Zuges kann ein Spieler auf sein Gedächtnis zugreifen und es ändern , eine anfangs leere Zeichenfolge, die das ganze Spiel über gültig ist und zum Speichern strategischer Daten verwendet werden kann.
Tor
Die Zelle im gelben Fadenkreuz ist das Ziel , das an einer zufälligen Position beginnt. Jeder Spieler hat eine Punktzahl , die bei 0 beginnt. Wenn sich der Bot eines Spielers zum Ziel bewegt, erhöht sich die Punktzahl dieses Spielers um 1 und das Ziel wird vor dem nächsten Zug zufällig neu positioniert. Der Spieler mit der höchsten Punktzahl am Ende eines Spiels gewinnt. Es ist ein Gleichstand, wenn die Ergebnisse gleich sind.
Wenn sich mehrere Bots während eines Zuges zum Ziel bewegen, erhält der Spieler immer noch nur einen Punkt.
Wenn sich das Ziel 500 Züge lang an derselben Stelle befunden hat, wird es nach dem Zufallsprinzip neu positioniert. Jedes Mal, wenn das Ziel zufällig positioniert wird, wird es garantiert nicht auf eine Zelle gelegt, die von einem Bot besetzt ist.
Was zu programmieren
Schreiben Sie einen Body für diese Funktion:
function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
//body goes here
}
Es wird jedes Mal aufgerufen, wenn sich Ihr Spieler bewegt, und muss die Aktionen zurückgeben, die jeder Ihrer Bots während dieser Bewegung ausführen soll.
Sie können den Basiscode als Ausgangspunkt verwenden.
Parameter
p1
ist ein Idiot,true
wenn du P1false
bist und wenn du P2 bistid
ist eine Ganzzahl, die die Antwort-ID Ihrer Antwort ist.
- Sie finden die ID einer Antwort, indem Sie auf den Link "Teilen" darunter klicken und
a/
in der URL direkt nach der Nummer suchen .- Die ID des Testeintrags ist -1.
eid
ist eine Ganzzahl, die die Antwort-ID der Antwort Ihres Feindes ist.move
ist eine ganze Zahl von 1 bis 1000, die angibt, in welcher Bewegung Sie sich befinden.goal
ist ein Objekt mitx
undy
Eigenschaften. Dies sind die Koordinaten des Ziels. Sie werden auch dann gegeben, wenn das Ziel außerhalb Ihres Sichtfelds liegt.grid
ist eine Funktion, die x- und y-Argumente akzeptiert, zgrid(x,y)
. Es gibt zurück:
-1
für 'unbekannt', wenn die Argumente keine zwei Ganzzahlen sind oder wennx,y
nicht in Ihrem Sichtfeld.0
für "Luft", wennx,y
außerhalb der Grenzen oder wenn die Zelle beix,y
Luft ist.1
für 'Wand', wenn die Zelle beix,y
eine Wand ist.
bots
ist ein Array Ihrer 8 Bots. Seine Elemente sind Objekte mit Eigenschaftenx
,y
undhasWall
:
x
undy
sind die Koordinaten des Bots.hasWall
ist,true
wenn der Bot eine Wand trägt undfalse
wenn nicht.
bots
Wird immer normal geordnet, entspricht der N-te Index der Bot-Nummer N.ebots
ist ein Array von Objekten mitx
,y
undhasWall
Eigenschaften wiebots
. Nur die feindlichen Bots in Ihrem FOV sind inebots
. Es hätte also die Länge 0, wenn sich keine feindlichen Bots in Ihrem Sichtfeld befinden. Es wird zufällig bestellt.getMem
ist eine Funktion ohne Argumente, die Ihr Gedächtnis zurückgibt.setMem
ist eine Funktion, die ein Argument M akzeptiert. Wenn M eine Zeichenfolge mit maximal 256 Zeichen ist, wird Ihr Speicher auf M aktualisiert, andernfalls passiert nichts.
Das Browserobjekt console
ist nur für den Testeintrag verfügbar.
Rückgabewert
Ihre Funktion muss ein Array mit genau 8 Ganzzahlen im Bereich von 0 bis 24 zurückgeben. Der Wert am Index N ist die Aktion, die der Bot Nummer N ausführen wird.
Alle Ihre Bots werden nichts tun, wenn Ihre Funktion:
- Wirft einen Fehler jeglicher Art. ( Fehler )
- Die Ausführung dauert länger als 20 Millisekunden . ( Zeitüberschreitung )
- Gibt kein Array mit 8 ganzen Zahlen zwischen 0 und 24 zurück. ( Fehlerhaft )
Der Einfachheit halber werden die Anzahl der Fehler, Zeitüberschreitungen und fehlerhaften Aktionen angezeigt, wenn ein Spiel endet.
Jede der Zahlen von 0 bis 24 entspricht einer bestimmten Bot-Aktion:
- 0 ist für nichts zu tun.
- 1-8 sind für den Umzug.
- 9-16 sind zum greifen.
- 17-24 sind für die Platzierung.
Jeder der 8 Werte für Verschieben, Greifen und Platzieren entspricht einer der Nachbarzellen des Bots, wie hier gezeigt:
So ist zum Beispiel 15
die Aktion zum Ergreifen der Zelle unter dem Bot.
Bot-Aktionen werden in der Reihenfolge Bot 0 bis Bot 7 ausgeführt. Wenn beispielsweise Bot 0 während eines Zuges angewiesen wird, eine Wand in dieselbe Luftzelle zu platzieren, zu der sich Bot 1 bewegen soll, wird die Luftzelle zu einer Wand vor Bot Die Aktion von 1 wird ausgeführt und Bot 1 ist nicht erfolgreich.
Erfolglose Handlungen werden zu Nichts und sollen gescheitert sein . Fehlgeschlagene Aktionszähler werden auch angezeigt, wenn das Spiel endet.
Regeln
Ich kann Benutzer oder Antworten, die diesen Regeln nicht entsprechen, vorübergehend oder dauerhaft disqualifizieren . Disqualifizierte Einsendungen sind nicht gewinnberechtigt.
Wenn Sie Variablen oder Funktionen deklarieren, müssen Sie das
var
Schlüsselwort verwenden.
ZBvar x = 10
odervar sum = function(a, b){ return a + b }
Dinge, die deklariert wurden, ohnevar
global zu werden, und die den Controller stören könnten. Es wurden Schritte unternommen, um diese Störung auszuschließen, aber stellen Sie dies sicher.Ihr Code sollte nicht langsam laufen oder Zeit verschwenden.
Es ist unmöglich, die Ausführung von JavaScript-Funktionen während der Ausführung zu stoppen, sodass der Code jedes Spielers vollständig ausgeführt wird. Wenn die Ausführung Ihres Codes viel Zeit in Anspruch nimmt, wird jeder, der Ihren Player ausführt, dies bemerken und sich darüber ärgern. Im Idealfall werden die Einträge immer innerhalb der 20-ms-Grenze ausgeführt.- Sie müssen Code verwenden, der mit ECMAScript 5 in der neuesten Version von Firefox kompatibel ist, da ich ihn hier ausführen werde. Verwenden Sie keine Funktionen aus ECMAScript 6, da dies in vielen Browsern noch nicht unterstützt wird.
- Sie können bis zu dreimal antworten , aber nur, wenn jede Ihrer Strategien erheblich anders ist. Sie können die Antworten beliebig oft bearbeiten.
- Sie dürfen nicht versuchen, irgendeine Art von Speicher zu haben, außer durch die Verwendung von
getMem
undsetMem
. - Sie dürfen nicht versuchen, auf den Controller, den Code eines anderen Players oder externe Ressourcen zuzugreifen oder diese zu ändern.
- Sie dürfen nicht versuchen, etwas in JavaScript eingebautes zu ändern.
- Antworten müssen nicht deterministisch sein. Sie können verwenden
Math.random
.
Antwortformat
#EntryName
Notes, etc.
<!-- language: lang-js -->
//function body
//probably on multiple lines
More notes, etc.
Der erste mehrzeilige Codeblock muss Ihren Funktionskörper enthalten.
Der Eintragsname ist auf 20 Zeichen begrenzt.
Ihr Eintrag wird im Controller mit dem Titel EntryName - Username [answer ID]
sowie, [DQ]
falls er disqualifiziert ist, angezeigt.
Gewinnen
Wenn die Frage mindestens drei Wochen lang gestellt wurde und sich die Beantwortung beruhigt hat, werde ich den Champion krönen.
Ich verwende die Autorun- Funktion des Controllers . In einer Autorun-Runde spielt jeder nicht disqualifizierte Spieler zwei Spiele gegeneinander, eines als P1, eines als P2 (Double Round Robin).
Ich werde in ein paar Stunden so viele Runden wie möglich automatisch starten. Dies hängt davon ab, wie viele Einreichungen es gibt und wie zeitintensiv sie sind. Aber seien Sie versichert, ich bin bestrebt, eine genaue endgültige Rangliste zu erhalten. Der Spieler mit den meisten Gewinnen ist der Champion und seine Antwort wird akzeptiert.
Ich verwende Firefox auf einem Laptop mit Windows 8.1 64-Bit, 4 GB RAM und einem 1,6-GHz-Quad-Core-Prozessor.
Preis
Ich werde eine PPCG-Challenge schreiben und veröffentlichen, die speziell dem Champion gewidmet ist. Es wird irgendwie ihren Benutzernamen oder Avatar oder etwas über sie beinhalten. Ich werde privat entscheiden, worum es bei der Herausforderung geht, wenn dieser Wettbewerb vorbei ist. Ich werde es nach besten Kräften schreiben und versuchen, sicherzustellen, dass es sich um eine Hot Network-Frage handelt.
Regler
Führen Sie dieses Snippet aus oder rufen Sie dieses JSFiddle auf, um den Controller zu verwenden. Es beginnt mit zufälligen Spielern. Ich habe es nur gründlich in Firefox und Chrome getestet.
<style>html *{font-family:Consolas,Arial,sans-serif}canvas{margin:6px}button,input table,select{font-size:100%}textarea{font-family:monospace}input[type=text],textarea{padding:2px}textarea[readonly]{background-color:#eee}select{width:250pt;margin:3px 0}input[type=radio]{vertical-align:-.25em}input[type=checkbox]{vertical-align:-.15em}.c{margin:12px}.h{font-size:125%;font-weight:700}#main td{padding:12px;text-align:left}#main table{margin-left:auto;margin-right:auto}#main{text-align:center}#title{margin:12px;font-size:175%;font-weight:700;color:#333}#delay{text-align:right}#statsTable table{border-collapse:collapse}#statsTable td{border:1px solid gray;padding:3pt;font-family:monospace;text-align:center}#footnotes{margin:18px 0 0;font-size:75%}#arWrapper{border:2px solid red;background-color:#fff4f4}</style><div id=loadStatus>Loading entries...</div><div id=main><div id=title>Block Building Bot Flocks</div><div><span id=p1Title class=p1Color></span> vs. <span id=p2Title class=p2Color></span></div><canvas id=canvas>Canvas unsupported!</canvas><div><span id=p1Score class=p1Color>0</span> | <span id=moveCounter>0</span> | <span id=p2Score class=p2Color>0</span></div><div class=c><button id=runPause type=button onclick=runPause()>Run</button> <button id=moveOnce type=button onclick=moveOnce()>Move Once</button> <button type=button onclick=newGame()>New Game</button></div><div class=c><input id=delay size=4 value=20> ms delay <input id=showNumbers type=checkbox onclick=toggleNumbers()><label for=showNumbers>Show bot numbers</label> <input id=showLOS type=checkbox onclick=toggleLOS()><label for=showLOS>Show field of view</label></div><table><tr><td><div id=p1Header class="p1Color h">Player 1</div><div><select id=p1Select onchange=changeSelect(!0)></select></div><div><a id=p1Link href=javascript:;>Answer Link</a></div><td><div id=p2Header class="p2Color h">Player 2</div><div><select id=p2Select onchange=changeSelect(!1)></select></div><div><a id=p2Link href=javascript:;>Answer Link</a></div></table><div>Test Entry</div><div><textarea id=testEntry rows=8 cols=64>return [0,0,0,0,0,0,0,0]</textarea></div><div class=c><button type=button onclick=autorun()>Autorun N Rounds</button> N = <input id=N size=4 value=1> <input id=arTestEntry type=checkbox><label for=arTestEntry>Include Test Entry</label></div><div id=footnotes><input id=debug type=checkbox onclick=toggleDebug()><label for=debug>Console debug messages</label> | Scale: <input id=sc1 type=radio name=sc value=1><label for=sc1>Micro</label><input id=sc3 type=radio name=sc value=3><label for=sc3>Small</label><input id=sc6 type=radio name=sc value=6 checked><label for=sc6>Normal</label><input id=sc9 type=radio name=sc value=9><label for=sc9>Large</label> | Colors: <input id=normalCo type=radio name=co value=normal checked><label for=normalCo>Normal</label><input id=pastelCo type=radio name=co value=pastel><label for=pastelCo>Pastels</label><input id=neonCo type=radio name=co value=neon><label for=neonCo>Neon</label> <button type=button onclick=reload()>Reload</button><div id=invalidWrapper><br>No entry name/code found: <span id=invalid></span></div></div></div><div id=arWrapper><div id=arInfo class=c>Autorun in progress. Running game <span id=arProgress></span>.</div><div id=arResults><div class="c h">Autorun Results</div><div class=c>Players: <span id=arPlayers></span><br>Rounds: <span id=arRounds></span><br>Games per round: <span id=arGpR></span><br>Total games: <span id=arTotal></span><br></div><div class=c><strong>Leaderboard:</strong></div><div id=leaderboard class=c></div><div class=c>(W = wins, T = ties, L = losses, G = total goals, E = errors, I = timeouts, M = malformed actions, F = failed actions)</div><div class=c><strong>Player vs. Player Statistics:</strong></div><div id=statsTable class=c></div><div class=c>The top row has the ID's of P1.<br>The left column has the ID's of P2.<br>Every other cell not on the diagonal has the form "[P1 win count] [tie count] [P2 win count]".</div><div class=c><button type=button onclick=closeAutorun()>Close</button></div></div></div><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script>function setGlobals(e){G={},G.QID=50690,G.SITE="codegolf",G.DQ_ANSWERS=[],G.DQ_USERS=[],G.DEBUG=Q("#debug").is(":checked"),G.SHOW_NUMBERS=Q("#showNumbers").is(":checked"),G.SHOW_LOS=Q("#showLOS").is(":checked"),G.BOTS=8,G.LOS=6,G.W=128,G.H=64,G.SCALE=e?6:parseInt(Q('input[name="sc"]:checked').val()),G.CW=G.SCALE*G.W,G.CH=G.SCALE*G.H,G.TOTAL_MOVES=2e3,G.GOAL_LIFESPAN=500,G.MEM_MAX_LENGTH=256,G.TIME_LIMIT=20;var t=Q('input[name="co"]:checked').val();e||"normal"===t?G.COLORS={AIR:"#ccc",WALL:"#888",GOAL:"rgba(255,255,0,0.6)",BG:"#f7f7f7",P1:"#00f",P1_TEXT:"#008",P1_LOS:"rgba(0,0,255,0.1)",P2:"#f00",P2_TEXT:"#800",P2_LOS:"rgba(255,0,0,0.1)"}:"pastel"===t?G.COLORS={AIR:"#cef0ff",WALL:"#66cc66",GOAL:"rgba(0,0,0,0.3)",BG:"#fdfde6",P1:"#f4a034",P1_TEXT:"#a35f00",P1_LOS:"rgba(255,179,71,0.2)",P2:"#f67cf6",P2_TEXT:"#b408b4",P2_LOS:"rgba(249,128,249,0.2)"}:"neon"===t&&(G.COLORS={AIR:"#000",WALL:"#444",GOAL:"rgba(255,255,0,0.9)",BG:"#999",P1:"#0f0",P1_TEXT:"#5f5",P1_LOS:"rgba(255,128,0,0.15)",P2:"#f0f",P2_TEXT:"#f5f",P2_LOS:"rgba(0,255,255,0.15)"}),G.SCOREBOARD={P1SCORE:void 0,MOVE:void 0,P2SCORE:void 0},G.CTX=void 0,G.PLAYERS=void 0,G.GAME=void 0,G.TIMER=void 0,G.RUNNING=!1}function reload(){var e="undefined"==typeof G;e||stopTimer(),setGlobals(e);var t=Q("#canvas");t.width(G.CW).height(G.CH).prop({width:G.CW,height:G.CH}),G.CTX=t[0].getContext("2d"),G.CTX.font=(2*G.SCALE).toString()+"px Courier New",G.SCOREBOARD.P1SCORE=Q("#p1Score"),G.SCOREBOARD.MOVE=Q("#moveCounter"),G.SCOREBOARD.P2SCORE=Q("#p2Score"),Q("body").css("background-color",G.COLORS.BG),Q(".p1Color").css("color",G.COLORS.P1),Q(".p2Color").css("color",G.COLORS.P2),Q("#invalidWrapper").hide(),Q("#arWrapper").hide(),loadAnswers(G.SITE,G.QID,function(e){Q.isArray(e)?(Q("#loadStatus").remove(),loadPlayers(e),newGame()):Q("#loadStatus").text("Error loading entries - "+e)})}function maskedEval(e,t){var r={};for(i in this)r[i]=void 0;for(i in t)t.hasOwnProperty(i)&&(r[i]=t[i]);return new Function("with(this) { "+e+";}").call(r)}function toKey(e,t){return G.W*t+e}function fromKey(e){return{x:e%G.W,y:Math.floor(e/G.W)}}function outOfBounds(e,t){return 0>e||e>=G.W||0>t||t>=G.H}function rnd(e){return Math.floor(Math.random()*e)}function isInt(e){return"number"==typeof e&&e%1===0}function isString(e){return"string"==typeof e||e instanceof String}function decode(e){return Q("<textarea>").html(e).text()}function shuffle(e){for(var t,r,o=e.length;o;t=rnd(o),r=e[--o],e[o]=e[t],e[t]=r);}function makeTable(e){for(var t=Q("<table>"),r=0;r<e.length;r++){for(var o=Q("<tr>"),a=0;a<e[r].length;a++)o.append(Q("<td>").text(e[r][a]));t.append(o)}return t}function toggleDebug(){G.DEBUG=Q("#debug").is(":checked")}function toggleNumbers(){G.SHOW_NUMBERS=Q("#showNumbers").is(":checked"),drawGame(G.GAME)}function toggleLOS(){G.SHOW_LOS=Q("#showLOS").is(":checked"),drawGame(G.GAME)}function closeAutorun(){Q("#arWrapper").hide(),Q("#main").show()}function changeSelect(e){var t=Q(e?"#p1Select":"#p2Select").val(),r=Q(e?"#p1Link":"#p2Link");null===t&&0>t?r.attr("href","javascript:;"):r.attr("href",G.PLAYERS[t].link)}function stopTimer(){"undefined"!=typeof G.TIMER&&clearInterval(G.TIMER)}function moveOnce(){gameOver(G.GAME)||(moveGame(G.GAME),drawGame(G.GAME),gameOver(G.GAME)&&(stopTimer(),Q("#runPause").text("Run").prop("disabled",!0),Q("#moveOnce").prop("disabled",!0),G.DEBUG&&console.log("======== GAME OVER: "+G.GAME.p1.score+" TO "+G.GAME.p2.score+" ========"),alert(gameOverMessage(G.GAME))))}function runPause(){if(G.RUNNING)stopTimer(),Q("#runPause").text("Run"),Q("#moveOnce").prop("disabled",!1);else{var e=parseInt(Q("#delay").val());if(isNaN(e)||0>e)return void alert("Delay must be a non-negative integer.");Q("#runPause").text("Pause"),Q("#moveOnce").prop("disabled",!0),G.TIMER=setInterval(moveOnce,e)}G.RUNNING=!G.RUNNING}function newGame(){stopTimer();var e=G.PLAYERS[Q("#p1Select").val()],t=G.PLAYERS[Q("#p2Select").val()];G.RUNNING=!1,Q("#runPause").text("Run").prop("disabled",!1),Q("#moveOnce").prop("disabled",!1),Q("#p1Title").text(e.title),Q("#p2Title").text(t.title),G.GAME=createGame(e,t),drawGame(G.GAME)}function tryParse(e,t){var r=parseInt(Q(e).val());return!isNaN(r)&&r>=0?r:void alert(t+" must be a non-negative integer.")}function autorun(){function e(){for(var e=new Array(a.length),t={},r=["wins","goals","errors","timeouts","malformed","invalid"],n=0;n<e.length;n++){t.wins=t.ties=t.losses=t.goals=t.errors=t.timeouts=t.malformed=t.invalid=0;for(var l=0;l<e.length;l++)n!==l&&(t.ties+=s[n][l].ties+s[l][n].ties,t.losses+=s[n][l].p2.wins+s[l][n].p1.wins,r.forEach(function(e){t[e]+=s[n][l].p1[e]+s[l][n].p2[e]}));e[n]={wins:t.wins,text:a[n].title+" : "+t.wins+"W, "+t.ties+"T, "+t.losses+"L, "+t.goals+"G, "+t.errors+"E, "+t.timeouts+"I, "+t.malformed+"M, "+t.invalid+"F"}}e=e.sort(function(e,t){return t.wins-e.wins}).map(function(t,r){return r+1+". "+t.text+(r<e.length-1?"<br>":"")});for(var i=new Array(s.length+1),G=0;G<i.length;G++){i[G]=new Array(s.length+1);for(var c=0;c<i.length;c++){var f;i[G][c]=0===c&&0===G?"P2\\P1":0===c?a[G-1].id:0===G?a[c-1].id:(f=s[c-1][G-1])?f.p1.wins+" "+f.ties+" "+f.p2.wins:"-"}}Q("#arPlayers").text(a.length),Q("#arRounds").text(o),Q("#arGpR").text(S/o),Q("#arTotal").text(S),Q("#leaderboard").empty().append(e),Q("#statsTable").empty().append(makeTable(i)),Q("#arInfo").hide(),Q("#arResults").show()}function t(e,t){for(var r=createGame(a[e],a[t]);!gameOver(r);)moveGame(r);r.p1.score>r.p2.score?s[e][t].p1.wins++:r.p1.score<r.p2.score?s[e][t].p2.wins++:s[e][t].ties++,["p1","p2"].forEach(function(o){s[e][t][o].goals+=r[o].score,s[e][t][o].errors+=r[o].stats.errors,s[e][t][o].timeouts+=r[o].stats.timeouts,s[e][t][o].malformed+=r[o].stats.malformed,s[e][t][o].invalid+=r[o].stats.invalid.reduce(function(e,t){return e+t},0)})}function r(){if(c!==f&&(t(c,f),++p<=S&&Q("#arProgress").text(p+"/"+S)),f+1<a.length)f++;else if(f=0,c+1<a.length)c++;else{if(c=0,!(o>i+1))return void e();i++}setTimeout(r,0)}var o=parseInt(Q("#N").val());if(isNaN(o)||1>o)return void alert("N must be a positive integer.");var a=[];Q("#arTestEntry").is(":checked")&&a.push(G.PLAYERS[0]);for(var n=1;n<G.PLAYERS.length;n++)G.PLAYERS[n].dq||a.push(G.PLAYERS[n]);for(var s=new Array(a.length),n=0;n<a.length;n++){s[n]=new Array(a.length);for(var l=0;l<a.length;l++)n!==l&&(s[n][l]={ties:0,p1:{wins:0,goals:0,errors:0,timeouts:0,malformed:0,invalid:0},p2:{wins:0,goals:0,errors:0,timeouts:0,malformed:0,invalid:0}})}var i=0,c=0,f=0,p=1,S=o*a.length*(a.length-1);Q("#arProgress").text("1/"+S),Q("#main").hide(),Q("#arInfo").show(),Q("#arResults").hide(),Q("#arWrapper").show(),setTimeout(r,0)}function gameOver(e){return e.move>=G.TOTAL_MOVES}function gameOverMessage(e){function t(e,t){return"P"+(t?1:2)+": "+e.entry.title+"\nScore: "+e.score+"\nErrors: "+e.stats.errors+"\nTimeouts: "+e.stats.timeouts+"\nMalformed actions: "+e.stats.malformed+"\nFailed actions: ["+e.stats.invalid.toString().replace(/,/g,", ")+"]"}var r="GAME OVER - ";return r+=e.p1.score>e.p2.score?"PLAYER 1 WINS":e.p1.score<e.p2.score?"PLAYER 2 WINS":"TIE GAME",r+="\n\n"+t(e.p1,!0)+"\n\n"+t(e.p2,!1)}function createGame(e,t){function r(e){return{entry:e,bots:new Array(G.BOTS),mem:"",score:0,stats:{errors:0,timeouts:0,malformed:0,invalid:Array.apply(null,new Array(G.BOTS)).map(Number.prototype.valueOf,0)}}}var o={},a=Math.floor(.875*G.H)-1;o.move=0,o.walls=new Array(G.H);for(var n=0;n<G.H;n++){o.walls[n]=new Array(G.W);for(var s=0;s<G.W;s++)o.walls[n][s]=n>a}o.p1=r(e),o.p2=r(t);for(var l=0;l<G.BOTS;l++)o.p1.bots[l]={x:l,y:a,hasWall:!1},o.p2.bots[l]={x:G.W-1-l,y:a,hasWall:!1};if(-1===o.p1.entry.id||-1===o.p2.entry.id){var i=decode(Q("#testEntry").val());-1===o.p1.entry.id&&(o.p1.entry.code=i),-1===o.p2.entry.id&&(o.p2.entry.code=i)}return resetGoal(o),G.DEBUG&&console.log("======== NEW GAME: "+o.p1.entry.title+" VS "+o.p2.entry.title+" ========"),o}function moveGame(e){movePlayer(e,++e.move%2===1),++e.goal.age>=G.GOAL_LIFESPAN&&resetGoal(e)}function setupParams(e,t){function r(e,t){var r=toKey(e,t);if(!n.hasOwnProperty(r)){n[r]=!1;for(var a=0;a<G.BOTS;a++)if(Math.abs(o.bots[a].x-e)<=G.LOS&&Math.abs(o.bots[a].y-t)<=G.LOS){n[r]=!0;break}}return n[r]}var o=t?e.p1:e.p2,a=t?e.p2:e.p1,n={},s={};s.p1=t,s.id=o.entry.id,s.eid=a.entry.id,s.score=o.score,s.escore=a.score,s.move=Math.floor((e.move+1)/2),s.goal={x:e.goal.x,y:e.goal.y},s.getMem=function(){return o.mem},s.setMem=function(e){isString(e)&&e.length<=G.MEM_MAX_LENGTH&&(o.mem=e)},s.grid=function(t,o){return isInt(t)&&isInt(o)&&r(t,o)?outOfBounds(t,o)?0:e.walls[o][t]?1:0:-1},s.bots=new Array(G.BOTS),s.ebots=[];for(var l=0;l<G.BOTS;l++)s.bots[l]={x:o.bots[l].x,y:o.bots[l].y,hasWall:o.bots[l].hasWall},r(a.bots[l].x,a.bots[l].y)&&s.ebots.push({x:a.bots[l].x,y:a.bots[l].y,hasWall:a.bots[l].hasWall});return shuffle(s.ebots),-1===o.entry.id&&(s.console=console),s}function movePlayer(e,t){var r,o,a=t?e.p1:e.p2,n=t?e.p2:e.p1,s=setupParams(e,t);G.DEBUG&&(console.log("######## MOVE "+e.move+" - P"+(t?1:2)+" ########"),console.log("PARAMETERS:"),console.log(s)),o=performance.now();try{r=maskedEval(a.entry.code,s)}catch(n){return a.stats.errors++,void(G.DEBUG&&(console.log("!!!! ERRORED !!!!"),console.log(n)))}if(o=performance.now()-o,G.DEBUG&&console.log("TIME TAKEN: "+o+"ms"),o>G.TIME_LIMIT)return a.stats.timeouts++,void(G.DEBUG&&console.log("!!!! TIMED OUT !!!!"));if(G.DEBUG&&(console.log("ACTIONS:"),console.log(r)),!Array.isArray(r)||r.length!==G.BOTS)return a.stats.malformed++,void(G.DEBUG&&console.log("!!!! MALFORMED ACTIONS !!!!"));for(var l=0;l<G.BOTS;l++)if(!isInt(r[l])||r[l]<0||r[l]>24)return a.stats.malformed++,void(G.DEBUG&&console.log("!!!! MALFORMED ACTIONS !!!!"));performActions(e,a,r)}function performActions(e,t,r){function o(e){t.stats.invalid[e]++,G.DEBUG&&console.log("!! BOT"+e+" ACTION FAILED !!")}function a(e){return e.x!==i||e.y!==c}for(var n=!1,s=0;s<G.BOTS;s++){var l=r[s];if(l){var i,c;switch((l-1)%8){case 0:i=-1,c=-1;break;case 1:i=0,c=-1;break;case 2:i=1,c=-1;break;case 3:i=-1,c=0;break;case 4:i=1,c=0;break;case 5:i=-1,c=1;break;case 6:i=0,c=1;break;case 7:i=1,c=1}if(i+=t.bots[s].x,c+=t.bots[s].y,outOfBounds(i,c))o(s);else switch(Math.floor((l-1)/8)){case 0:!e.walls[c][i]&&(i>0&&c>0&&e.walls[c-1][i-1]||c>0&&e.walls[c-1][i]||i<G.W-1&&c>0&&e.walls[c-1][i+1]||i>0&&e.walls[c][i-1]||i<G.W-1&&e.walls[c][i+1]||i>0&&c<G.H-1&&e.walls[c+1][i-1]||c<G.H-1&&e.walls[c+1][i]||i<G.W-1&&c<G.H-1&&e.walls[c+1][i+1])?(t.bots[s].x=i,t.bots[s].y=c,i!==e.goal.x||c!==e.goal.y||n||(n=!0,G.DEBUG&&console.log("** BOT"+s+" REACHED GOAL **"))):o(s);break;case 1:e.walls[c][i]&&!t.bots[s].hasWall?(e.walls[c][i]=!1,t.bots[s].hasWall=!0):o(s);break;case 2:!e.walls[c][i]&&t.bots[s].hasWall&&e.p1.bots.every(a)&&e.p2.bots.every(a)?(e.walls[c][i]=!0,t.bots[s].hasWall=!1):o(s)}}}n&&(t.score++,resetGoal(e)),G.DEBUG&&(console.log("FINAL PLAYER STATE:"),console.log(t))}function resetGoal(e){for(var t={},r=[],o=0;o<G.BOTS;o++)t[toKey(e.p1.bots[o].x,e.p1.bots[o].y)]=!0,t[toKey(e.p2.bots[o].x,e.p2.bots[o].y)]=!0;for(var a=0;a<G.H;a++)for(var n=0;n<G.W;n++){var s=toKey(n,a);t.hasOwnProperty(s)||r.push(s)}var l=fromKey(r[rnd(r.length)]);e.goal={age:0,x:l.x,y:l.y}}function drawGame(e){function t(e,t){G.CTX.fillRect(e*G.SCALE,t*G.SCALE,G.SCALE,G.SCALE)}function r(e,t){G.CTX.fillRect(e*G.SCALE+1,t*G.SCALE+1,G.SCALE-2,G.SCALE-2)}G.CTX.fillStyle=G.COLORS.AIR,G.CTX.fillRect(0,0,G.CW,G.CH),G.CTX.fillStyle=G.COLORS.WALL;for(var o=0;o<G.H;o++)for(var a=0;a<G.W;a++)e.walls[o][a]&&t(a,o);if(G.SHOW_LOS){var n=(2*G.LOS+1)*G.SCALE;G.CTX.fillStyle=G.COLORS.P1_LOS;for(var s=0;s<G.BOTS;s++)G.CTX.fillRect((e.p1.bots[s].x-G.LOS)*G.SCALE,(e.p1.bots[s].y-G.LOS)*G.SCALE,n,n);G.CTX.fillStyle=G.COLORS.P2_LOS;for(var s=0;s<G.BOTS;s++)G.CTX.fillRect((e.p2.bots[s].x-G.LOS)*G.SCALE,(e.p2.bots[s].y-G.LOS)*G.SCALE,n,n)}G.CTX.fillStyle=G.COLORS.P1;for(var s=0;s<G.BOTS;s++)t(e.p1.bots[s].x,e.p1.bots[s].y);G.CTX.fillStyle=G.COLORS.P2;for(var s=0;s<G.BOTS;s++)t(e.p2.bots[s].x,e.p2.bots[s].y);G.CTX.fillStyle=G.COLORS.WALL;for(var s=0;s<G.BOTS;s++)e.p1.bots[s].hasWall&&r(e.p1.bots[s].x,e.p1.bots[s].y),e.p2.bots[s].hasWall&&r(e.p2.bots[s].x,e.p2.bots[s].y);if(G.SHOW_NUMBERS){var l=-.1,i=2.75;G.CTX.fillStyle=G.COLORS.P1_TEXT;for(var s=0;s<G.BOTS;s++)G.CTX.fillText(s.toString(),(e.p1.bots[s].x+l)*G.SCALE,(e.p1.bots[s].y+i)*G.SCALE);G.CTX.fillStyle=G.COLORS.P2_TEXT;for(var s=0;s<G.BOTS;s++)G.CTX.fillText(s.toString(),(e.p2.bots[s].x+l)*G.SCALE,(e.p2.bots[s].y+i)*G.SCALE)}G.CTX.fillStyle=G.COLORS.GOAL,t(e.goal.x+1,e.goal.y),t(e.goal.x-1,e.goal.y),t(e.goal.x,e.goal.y+1),t(e.goal.x,e.goal.y-1),G.SCOREBOARD.P1SCORE.text(e.p1.score),G.SCOREBOARD.MOVE.text(e.move),G.SCOREBOARD.P2SCORE.text(e.p2.score)}function loadPlayers(e){var t=/<pre\b[^>]*><code\b[^>]*>([\s\S]*?)<\/code><\/pre>/,r=/<h1\b[^>]*>(.*?)<\/h1>/;G.PLAYERS=[];var o={id:-1,dq:!1,code:void 0,link:"javascript:;",title:"TEST ENTRY [-1]"};G.PLAYERS.push(o);var a=[];e.forEach(function(e){var o=decode(e.owner.display_name),n=t.exec(e.body),s=r.exec(e.body);if(null===n||n.length<=1||null===s||s.length<=1)return a.push(" "),void a.push(Q("<a>").text(o).attr("href",e.link));var l={};l.id=e.answer_id,l.dq=G.DQ_ANSWERS.indexOf(e.answer_id)>-1||G.DQ_USERS.indexOf(e.owner.user_id)>-1,l.code=decode(n[1]),l.link=e.link,l.title=s[1].substring(0,20)+" - "+o+" ["+l.id.toString()+"]",l.dq&&(l.title+="[DQ]"),G.PLAYERS.push(l)}),a.length>0&&(Q("#invalid").empty().append(a),Q("#invalidWrapper").show());for(var n=new Array(G.PLAYERS.length),s=new Array(G.PLAYERS.length),l=0;l<G.PLAYERS.length;l++)n[l]=Q("<option>").text(G.PLAYERS[l].title).val(l),s[l]=Q("<option>").text(G.PLAYERS[l].title).val(l);Q("#p1Select").empty().append(n).val(rnd(G.PLAYERS.length)),changeSelect(!0),Q("#p2Select").empty().append(s).val(rnd(G.PLAYERS.length)),changeSelect(!1)}function loadAnswers(e,t,r){function o(){Q.get("https://api.stackexchange.com/2.2/questions/"+t.toString()+"/answers?page="+(s++).toString()+"&pagesize=100&order=asc&sort=creation&site="+e+"&filter=!YOKGPOBC5Yad4mOOn8Z4WcAE6q",a)}function a(e){e.hasOwnProperty("error_id")?r(e.error_id.toString()):(n=n.concat(e.items),e.hasMore?o():r(n))}var n=[],s=1;o(s,a)}Q=jQuery,Q(reload);</script>
Diese Frage hat einen eigenen Chatroom. Ich werde dort alle paar Tage Bestenlisten veröffentlichen.
quelle
Antworten:
Schwarzer Ritter
Der Name des Bots stammt von einem frühen Plan, dass er sich wie ein Schachritter bewegen soll: über zwei, über einen usw., was in einigen Fällen schneller wäre.
Erläuterung
Das Bestimmen, welcher Zug für jeden Bot ausgeführt werden soll, kann in zwei Hauptaufgaben unterteilt werden: Herausfinden, wohin und wie man dorthin kommt.
Wo hin
Die grundlegende Aufgabe, herauszufinden, wohin es gehen soll, ist einfach: Gehen Sie auf das Ziel zu, wenn Sie am nächsten sind, oder versuchen Sie auf andere Weise, sich so weit wie möglich von Ihren Teamkollegen zu entfernen. Zuerst durchläuft es jeden Bot und stellt fest, ob er gestrandet ist (dh er hat keine Blöcke um ihn herum und hält keine Wand, oder er ist von Wänden umgeben und hält eine Wand). Anschließend durchläuft es die Bots erneut, um den nicht gestrandeten Bot zu finden, der dem Ziel am nächsten ist. Alle anderen Bots bahnen sich ihren Weg nach außen, wobei sich die untere Reihe auf der Oberfläche der Blöcke (
y=55
) und die obere Reihe auf befindeny=27
. Sobald es weiß, wohin es gehen soll, gibt es es an diemoveTo
Funktion weiter.Wie man dorthin kommt
Die Entscheidung, wie man zum Ziel kommt, ist weitaus schwieriger, da die Bots immer an einer Wand stehen müssen, um sich zu bewegen. Zunächst wird der Richtungscode (1–8) des Ziels relativ zu seiner aktuellen Position ermittelt. Wenn sich beispielsweise ein Bot in der unteren linken Ecke befindet und nach rechts oben gehen möchte, wird der Richtungscode 3 verwendet. Für jede Richtung habe ich eine Liste von Zügen fest codiert, wobei der erste der ideale obere ist -Prioritätszug, und der letzte ist der letzte Ausweg. Dies wird dadurch getrennt, ob der Bot eine Wand hat oder nicht, da Sie keine Ortsbewegung ohne Wand oder keine Greifbewegung ausführen können, während Sie bereits eine Wand haben.
Natürlich funktioniert die Verwendung des idealen Zuges nicht immer und es würde zu vielen fehlgeschlagenen Aktionen führen. Hier
checkMove
kommt es an. Diese Funktion überprüft die mögliche Bewegung gegen jede Anforderung, um beispielsweise zu verhindern, dass sich der Bot außerhalb der Grenzen oder in eine Wand bewegt. Wenn ein in der Nähe befindlicher feindlicher Bot gestrandet werden kann (er hat nur eine angrenzende Wand, die vom Bot erobert werden kann), wird dies zu seiner Priorität, sodass die Funktionfalse
für einen ansonsten legitimen Zug zurückkehrt und zu den Greifzügen springen und diese ausführen kann der Feind. Die Funktion verhindert mehrere andere dumme Bewegungen wie das Platzieren einer Mauer im Tor oder eines anderen Bots.Die Speicherzeichenfolge
Manchmal ist der Bot nicht wirklich gestrandet, sondern versucht den gleichen Zug weiter und landet nicht in Bewegung (normalerweise hebt er eine Wand auf und setzt sie ab, hebt sie auf und setzt sie ab usw.). Um dies zu verhindern, verwendet es die Speicherzeichenfolge, um sich an die letzten beiden Züge, die letzten x- und y-Positionen und die Anzahl der Standbilder zu erinnern. Jedes Datum ist als einzelnes Zeichen codiert, um die Aufteilung zu vereinfachen. (Die Zeichenfolge muss aus 256 Zeichen und nicht aus Bytes bestehen. Daher ist die Verwendung von Multibyte-Unicode-Zeichen kein Problem, wie dies bei typischen Golfherausforderungen der Fall ist.)
Angenommen, ein Bot hat in
12
dieser Runde die Wand auf der linken Seite (Code ) gepackt , sie20
in der vorherigen Runde auf die linke Seite (Code ) zurückgesetzt und war in den letzten Runden an den Koordinaten (107
,3
)16
. Die Speicherzeichenfolge für diese Instanz würde folgendermaßen codiert:ck
: Die beiden letzten Aktionscodes werden in base36 konvertiert, um die zweistelligen Zahlen zu einem einzigen Buchstaben zu machen.@
: Die Anzahl von Malen, die noch vorhanden waren, wird als ASCII-Zeichen mit diesem Code + 48 dargestellt, um nicht druckbare Zeichen zu überspringen. Die ersten neun Malen zeigen daher immer noch die tatsächliche Anzahl (String.fromCharCode(0 + 48)
→0
).Ħ¾
: Die x- und y-Koordinaten werden auch als Zeichen mit diesem Wert dargestellt, diesmal versetzt um den etwas willkürlichen Wert von 187, um problematische Zeichen zu vermeiden.Eine typische Memory-Zeichenfolge während des Spiels könnte
53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬò
eine Gruppe von fünf Zeichen für jeden der acht Bots sein.quelle
Außenposten
Die 8 Bots nehmen jeweils ein 32 x 32 Quadrat und rennen in die Mitte davon (ich versetze die Zentren ein wenig, da sie sich sonst paaren und vertikal mit einem Mauerblock zwischen ihnen fortbewegen, sodass einer von ihnen gestrandet wird).
Jeder Bot bleibt in der Mitte seines Quadrats, es sei denn, das Ziel befindet sich innerhalb von 32 Zellen seines jeweiligen Zentrums. In diesem Fall rennt er zum Ziel und dann zurück in die Mitte.
Dabei wird immer noch die Baseline-Methode zum Erreichen des Ziels (Ziel oder Mittelpunkt) verwendet, sodass keine diagonale Bewegung erfolgt. Nur ein Ausgangspunkt ...
quelle
Grundlinie
Dies ist der einfachste, beständig funktionierende Bot-Flock-Controller, den ich mir vorstellen kann. Es wird meine einzige nicht disqualifizierte Antwort sein und als Basis dienen, um andere Antworten danach zu beurteilen. Es ist technisch gesehen im Rennen, um den Wettbewerb zu gewinnen, aber es sollte nicht schwierig sein, ihn zu schlagen.
Jeder Code hier kann kopiert und in einer anderen Antwort verwendet werden, ohne dass eine Zuordnung erforderlich ist.
Jeder der 8 Bots folgt unabhängig voneinander der gleichen Grundmethode. Sie neigen deshalb dazu, sich zu verklumpen, es sei denn, sie werden durch etwas Äußeres getrennt. Den Bots ist es egal, wo sich Teamkollegen oder Feinde befinden, sie versuchen nur, sich dem Ziel zu nähern. Sie bewegen sich nur orthogonal und stimmen zuerst ihr x mit dem Ziel x ab, dann ihr y. Sich niemals diagonal zu bewegen bedeutet, dass sie viel Zeit auf Reisen verschwenden.
Der Bewegungsalgorithmus jedes Bots ist wie folgt:
quelle
Teamspieler
Im Moment ist diese Vorlage alles andere als perfekt. Es hat eine ähnliche Strategie wie Outposts, aber nur 6 Bots sind "in der Luft". Die anderen 2 Bots versorgen sie mit Wänden, wenn sie gestohlen wurden. Bearbeiten: Die Supporter-Bots arbeiten jetzt viel besser.
quelle
Sucher
Ich arbeite noch in Arbeit. Ich habe viele Ideen, aber fast keine von ihnen funktioniert.
Vor allem großes Problem mit fehlgeschlagenen Aktionen.Gelöst!quelle