Intro
Ceci est un roi de la colline interactive concours lequel le contrôleur est entièrement contenu dans un extrait de pile au bas de la question. Le contrôleur lit automatiquement les réponses et joue aux jeux. Tout le monde peut le lancer à tout moment, directement dans son navigateur.
Les mécanismes de ce concours sont très similaires à ceux de Red vs. Blue - Pixel Team Battlebots . Sauf que le jeu en cours, bien que toujours basé sur une grille, est totalement différent. Chaque jeu est 1 contre 1 et il n'y a pas d'équipes. Chaque inscription se bat pour elle-même et un seul sera le champion final.
Le contrôleur utilise JavaScript. JavaScript étant le seul langage de script côté client pris en charge par la plupart des navigateurs, toutes les réponses doivent également être écrites en JavaScript .
Dans cette spécification, le texte en italique est utilisé pour indiquer le terme formel pour un mécanisme de jeu ou une propriété. Ces termes sont utilisés dans l’ensemble pour aider à maintenir une manière cohérente et claire de faire référence aux différentes parties du jeu.
Gameplay
Les bases
Chaque réponse à cette question représente un joueur . Un jeu est une compétition entre deux joueurs, P1 et P2 . Chaque joueur contrôle un troupeau de 8 bots , numérotés de 0 à 7. Jeux a lieu dans la grille , un 128 × 64 cellules arène dont 8 lignes de fond commencent comme des murs (les « blocs ») et d' autres lignes de départ comme l' air . Les cellules situées en dehors des limites de la grille sont considérées comme de l'air.
La coordonnée x de la grille va de 0 à gauche à 127 à droite et y de 0 en haut à 63 en bas.
Exemple de grille de départ:
Les robots restent toujours alignés sur les cellules de la grille et plusieurs robots peuvent occuper la même cellule. Les robots ne peuvent occuper que des cellules d'air. Les robots de P1 commencent toujours dans une ligne 0-7 à l'extrême gauche de la rangée au-dessus des murs et les robots de P2 commencent toujours dans une ligne 7-0 à l'extrême droite.
Les voisins d'un bot ou d'une cellule sont les 8 cellules directement orthogonales et diagonales.
Le champ de vision ( FOV ) d'un robot est le carré de la cellule de 13 x 13 centrée sur un robot. Une cellule ou un bot ennemi est dit être dans le FOV d'un joueur s'il est dans le FOV d'au moins un des robots du joueur.
Mouvements & Actions
Au cours d'une partie, chaque joueur peut se déplacer 1000 fois. P1 se déplace en premier, puis P2, puis P1 et ainsi de suite jusqu’à ce que 2 000 mouvements aient été effectués. La partie se termine.
Au cours d'un mouvement, chaque joueur reçoit des informations sur l'état du jeu, les cellules de la grille et les robots ennemis de son champ de vision, et l'utilise pour décider d'une action à entreprendre pour chacun de ses robots.
L'action par défaut est ne rien faire , le bot ne se déplaçant pas et n'interagissant pas avec la grille.
Les autres actions sont déplacer , saisir et placer :
Un bot peut se déplacer vers l’une des cellules voisines C si:
- C n'est pas hors limites,
- C est l'air (c'est-à-dire pas un mur),
- et au moins un des voisins de C est un mur.
En cas de succès, le bot passera à C.
Un bot peut saisir l' une de ses cellules voisines C si:
- C n'est pas hors limites,
- C est un mur,
- et le bot ne porte pas déjà un mur.
En cas de succès, C deviendra de l’air et le bot aura maintenant un mur.
Un bot peut placer une de ses cellules voisines C si:
- C n'est pas hors limites,
- C est l'air,
- aucun des deux joueurs n'occupe C,
- et le bot porte un mur.
En cas de succès, C deviendra un mur et le bot ne portera plus de mur.
Les actions infructueuses aboutissent à ne rien faire.
Une cellule occupée par au moins un bot porteur de mur porte un petit carré de couleur. Les robots commencent sans murs.
Mémoire
Au cours d'un déplacement, un joueur peut accéder et modifier sa mémoire , une chaîne initialement vide qui dure tout au long du jeu et peut être utilisée pour stocker des données stratégiques.
Objectif
La cellule dans le réticule jaune est le but , qui commence dans une position aléatoire. Chaque joueur a un score qui commence à 0. Lorsque le bot d'un joueur se déplace vers le but, le score de ce joueur augmente de 1 et le but est repositionné au hasard avant le tour suivant. Le joueur avec le score le plus élevé à la fin d'une partie gagne. Il y a égalité si les scores sont égaux.
Si plusieurs robots se rendent au but pendant un coup, le joueur ne gagne toujours qu'un point.
Si le but est au même endroit depuis 500 coups, il est repositionné de manière aléatoire. Chaque fois que l'objectif est positionné de manière aléatoire, il est garanti qu'il ne sera pas placé sur une cellule occupée par un bot.
Que programmer
Ecrivez un corps pour cette fonction:
function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
//body goes here
}
Il sera appelé une fois à chaque fois que votre joueur bouge et doit rendre les actions que vous souhaitez que chacun de vos robots effectue pendant ce mouvement.
Vous pouvez utiliser le code de base comme point de départ.
Paramètres
p1
est un bool qui esttrue
si vous êtes P1 etfalse
si vous êtes P2id
est un entier qui correspond à l'ID de réponse de votre réponse.
- Vous pouvez trouver l'ID d'une réponse en cliquant sur le lien "Partager" situé en dessous et en recherchant le numéro immédiatement après
a/
dans l'URL.- L'ID de l'entrée de test est -1.
eid
est un entier qui est l'ID de réponse de la réponse de votre ennemi.move
est un entier de 1 à 1000 indiquant le mouvement sur lequel vous vous trouvez.goal
est un objet avecx
ety
propriétés. Ce sont les coordonnées du but. Ils sont donnés même si le but est hors de votre champ de vision.grid
est une fonction qui prend en arguments x et y, par exemplegrid(x,y)
. Il retourne:
-1
pour 'inconnu' si les arguments ne sont pas deux entiers ou six,y
n'est pas dans votre champ de vision.0
pour 'air' six,y
est hors limites ou si la cellule àx,y
est est air.1
pour 'mur' si la cellule àx,y
est un mur.
bots
est un tableau de vos 8 robots. Ses éléments sont des objets avec des propriétésx
,y
ethasWall
:
x
ety
sont les coordonnées du bot.hasWall
esttrue
si le bot porte un mur etfalse
sinon.
bots
est toujours ordonné normalement, le Nième index correspond au numéro de bot N.ebots
est un tableau d'objets avecx
,y
et deshasWall
propriétés tout commebots
. Seuls les robots ennemis de votre champ de vision sont présentsebots
. Donc, il aurait une longueur de 0 s'il n'y avait pas de robots ennemis dans votre FOV. C'est ordonné au hasard.getMem
est une fonction sans argument qui retourne votre mémoire.setMem
est une fonction qui prend un argument M. Si M est une chaîne de 256 caractères ou moins, votre mémoire est mise à jour en M, sinon rien ne se produit.
L' console
objet de navigateur est disponible uniquement pour l'entrée de test.
Valeur de retour
Votre fonction doit renvoyer un tableau d’exactement 8 entiers, chacun compris entre 0 et 24. La valeur à l’indice N est l’action que le nombre de bot N va entreprendre.
Tous vos robots ne feront rien si votre fonction:
- Lance une erreur de toute nature. ( erreur )
- Prend plus de 20 millisecondes à exécuter. ( timeout )
- Ne retourne pas un tableau de 8 nombres entiers allant de 0 à 24. ( mal formé )
Pour plus de commodité, le nombre d'erreurs, de délais d'expiration et d'actions malformées est affiché à la fin d'un jeu.
Chacun des nombres de 0 à 24 correspond à une action de bot particulière:
- 0 est pour ne rien faire.
- 1-8 sont pour le déménagement.
- 9-16 sont pour saisir.
- 17-24 sont pour le placement.
Chacune des 8 valeurs pour déplacer, saisir et placer correspond à l'une des cellules voisines du bot, comme indiqué ci-dessous:
Ainsi, par exemple, 15
l'action consiste à saisir la cellule sous le bot.
Les actions de bot sont traitées dans l'ordre bot 0 à bot 7. Par exemple, si lors d'un mouvement, on dit à bot 0 de placer un mur dans la même cellule à air, il est dit à bot 1 de s'y déplacer, la cellule à air deviendra un mur avant L'action de 1 est gérée et le bot 1 échouera.
Les actions infructueuses deviennent des ratés et sont réputées avoir échoué . Les compteurs d'actions ayant échoué sont également affichés à la fin du jeu.
Règles
Je peux disqualifier temporairement ou définitivement des utilisateurs ou des réponses qui ne suivent pas ces règles. Les participations disqualifiées ne sont pas admissibles à gagner.
Lors de la déclaration de variables ou de fonctions, vous devez utiliser le
var
mot clé.
Par exemple,var x = 10
ouvar sum = function(a, b){ return a + b }
Les choses déclarées sansvar
devenir globales et pourraient interférer avec le contrôleur. Des mesures ont été prises pour que cette ingérence soit impossible, mais faites-le pour vous en assurer.Votre code ne doit pas être lent ni perdre du temps.
Il est impossible d'arrêter les fonctions JavaScript en cours d'exécution, le code de chaque joueur est donc exécuté à fond. Si votre code prend beaucoup de temps à s'exécuter, tous ceux qui exécutent votre lecteur le remarqueront et en seront agacés. Idéalement, les entrées fonctionneront toujours dans la limite de 20 ms.- Vous devez utiliser un code compatible avec ECMAScript 5 dans la dernière version de Firefox, car c'est ici que je l'exécuterai. N'utilisez pas les fonctionnalités d'ECMAScript 6 car elles ne sont pas encore prises en charge par de nombreux navigateurs.
- Vous pouvez répondre jusqu'à 3 fois, mais seulement si chacune de vos stratégies est très différente. Vous pouvez modifier les réponses autant que vous le souhaitez.
- Vous ne pouvez pas essayer d’avoir une quelconque mémoire sauf avec
getMem
etsetMem
. - Vous ne pouvez pas tenter d'accéder ou de modifier le contrôleur, le code d'un autre joueur ou des ressources externes.
- Vous ne pouvez pas tenter de modifier quoi que ce soit intégré à JavaScript.
- Les réponses ne doivent pas nécessairement être déterministes. Vous pouvez utiliser
Math.random
.
Format de réponse
#EntryName
Notes, etc.
<!-- language: lang-js -->
//function body
//probably on multiple lines
More notes, etc.
Le premier bloc de code multiligne doit contenir votre corps de fonction.
Le nom de l'entrée est limité à 20 caractères.
Votre entrée apparaîtra dans le contrôleur avec le titre EntryName - Username [answer ID]
, plus [DQ]
si elle est disqualifiée.
Gagnant
Quand la question sera posée pendant au moins 3 semaines et que la réponse sera réglée, je couronnerai le champion.
Je vais utiliser la fonction d' exécution automatique du contrôleur . Dans un tour en autorun, chaque joueur non disqualifié joue deux parties avec une autre, une en tant que P1, une en tant que P2 (un double round robin).
Je vais autoruner autant de tours que possible en l'espace de quelques heures. Cela dépendra du nombre de soumissions et de leur temps fastidieux. Mais rassurez-vous, je suis déterminé à obtenir un classement final précis. Le joueur avec le plus de victoires est le champion et sa réponse sera acceptée.
J'utiliserai Firefox sur un ordinateur portable avec Windows 8.1 64 bits, 4 Go de RAM et un processeur quad-core à 1,6 GHz.
Prix
Je vais écrire et poster un challenge PPCG spécifiquement dédié au champion. Cela impliquera en quelque sorte leur nom d'utilisateur ou leur avatar ou quelque chose à leur sujet. Je déciderai en privé de ce que sera le défi lorsque ce concours sera terminé. Je vais l'écrire au mieux de mes capacités et essayer de faire en sorte que cela devienne une question de réseau à chaud.
Manette
Exécutez cet extrait ou accédez à ce JSFiddle pour utiliser le contrôleur. Cela commence avec des joueurs choisis au hasard. Je ne l'ai que soigneusement testé dans Firefox et Chrome.
<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>
Cette question a sa propre salle de discussion. J'y posterai des classements tous les quelques jours.
la source
Réponses:
Chevalier noir
Le nom du bot vient d'un plan initial lui permettant de se déplacer comme un chevalier d'échecs: plus de deux, plus un, etc., ce qui serait plus rapide dans certains cas.
Explication
La détermination du mouvement à effectuer pour chaque bot peut être divisée en deux tâches principales: déterminer où aller et comment s'y rendre.
Où aller
La tâche de base qui consiste à déterminer où aller est simple: visez l'objectif si vous êtes le plus proche ou tentez de vous positionner aussi loin que possible de vos coéquipiers. Il passe d’abord par chaque bot et détermine s’il est bloqué (c’est-à-dire qu’il n’est pas entouré de blocs et qu’il ne tient pas un mur, ou qu’il est entouré de murs et qu’il détient un mur). Il parcourt ensuite à nouveau les robots pour trouver le robot non bloqué le plus proche du but. Tous les autres robots s'efforcent d'être espacés, avec la rangée inférieure à la surface des blocs (
y=55
) et la rangée supérieure eny=27
. Une fois qu'il sait où aller, il passe à lamoveTo
fonction.Comment aller là
Décider comment se rendre à la destination est beaucoup plus difficile car les robots doivent toujours être adjacents à un mur pour se déplacer. Il commence par comprendre le code de direction (1–8) de la destination par rapport à sa position actuelle. Par exemple, si un bot se trouvait dans le coin inférieur gauche et souhaitait aller en haut à droite, il utiliserait le code de direction 3. Pour chaque direction, j’ai codé en dur une liste de mouvements, le premier étant l’idéal, en haut. mouvement prioritaire, et le dernier étant le dernier recours. Ceci est séparé par le fait que le bot ait ou non un mur, parce que vous ne pouvez pas utiliser un déplacement de lieu sans mur ni utiliser un déplacement instantané alors que vous avez déjà un mur.
Bien sûr, utiliser le mouvement idéal ne fonctionne pas toujours et entraînerait de nombreuses actions infructueuses. C’est là
checkMove
qu’intervient. Cette fonction vérifie le potentiel de déplacement par rapport à chaque exigence, afin d’empêcher le bot de sortir des limites du terrain ou de s’approcher d’un mur, par exemple. Si un bot ennemi proche peut être bloqué (il n'a qu'un mur adjacent qui peut être pris par le bot), cela devient sa priorité, de sorte que la fonction reviendrafalse
pour un mouvement par ailleurs légitime afin de pouvoir passer aux mouvements de saisie et sortir. l'ennemi. La fonction empêche plusieurs autres mouvements stupides, comme de placer un mur dans le but ou un autre bot.La chaîne de mémoire
Parfois, le bot ne sera pas réellement bloqué, mais continuera d'essayer le même mouvement et ne finira pas par bouger (habituellement, relever un mur et le poser, le ramasser et le poser, etc.). Pour éviter cela, il utilise la chaîne de mémoire pour mémoriser ses deux derniers déplacements, ses dernières positions x et y et combien de fois il est resté immobile. Chaque donnée est codée comme un seul caractère pour faciliter la division. (La chaîne doit être composée de 256 caractères et non d'octets. L'utilisation de caractères Unicode multi-octets ne pose donc pas de problème, contrairement à ce qui se passe avec les défis de golf classiques.)
Par exemple, disons qu'un bot a saisi le mur à sa gauche (code
12
) ce tour, l'a remplacé à sa gauche (code20
) lors de son tour précédent et qu'il a été aux coordonnées (107
,3
) pour les16
tours précédents . La chaîne de mémoire pour cette instance serait codée comme suit:ck
: Les deux derniers codes d'action sont convertis en base36 pour que les nombres à deux chiffres forment une seule lettre.@
: Le nombre de fois où il a toujours été représenté est représenté par le caractère ASCII avec le code + 48 pour ignorer les caractères non imprimables. Les neuf premières fois affichent toujours le nombre réel (String.fromCharCode(0 + 48)
→0
).Ħ¾
: Les coordonnées x et y sont également représentées sous la forme du caractère avec cette valeur, cette fois compensée par la valeur quelque peu arbitraire de 187 pour éviter les caractères problématiques.Une chaîne de mémoire typique pendant le jeu pourrait être
53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬò
, avec un groupe de cinq personnages pour chacun des huit robots.la source
Avant-postes
Les 8 robots prennent chacun un carré de 32 sur 32 et courent au centre (je décale légèrement les centres, sinon ils finissent par s’apparier et se déplacent verticalement avec un bloc de mur entre eux, de sorte que l’un d’eux reste bloqué).
Chaque bot restera au centre de sa case, à moins que le but ne se trouve dans un rayon de 32 cellules de son centre respectif. Dans ce cas, il courra jusqu'au but puis reviendra au centre.
Ceci utilise toujours la méthode Baseline pour atteindre sa cible (objectif ou centre) et ne bouge donc pas en diagonale. Juste un point de départ ...
la source
De base
C’est le contrôleur de troupeau de bots le plus simple et le plus simple que je puisse imaginer. Ce sera ma seule réponse non disqualifiée et servira de base pour juger d’autres réponses. Il est techniquement possible de remporter le concours, mais le battre ne devrait pas être difficile.
Tout le code ici peut être copié et utilisé dans une autre réponse, aucune attribution requise.
Chacun des 8 robots suit indépendamment la même méthode de base. Ils ont tendance à se regrouper pour cette raison, à moins qu'ils ne soient séparés par quelque chose d'extérieur. Les robots ne se soucient jamais de savoir où se trouvent leurs coéquipiers ou leurs ennemis, ils tentent seulement de se diriger vers le but. Ils ne se déplacent qu'orthogonalement, en faisant d'abord correspondre leur x au but x, puis leur y. Ne jamais bouger en diagonale signifie qu'ils perdent beaucoup de temps en déplacement.
L'algorithme de mouvement de chaque bot est le suivant:
la source
Joueur d'équipe
Pour le moment, cette soumission est loin d'être parfaite. Sa stratégie est semblable à celle des avant-postes, mais seuls 6 robots sont "dans les airs". Les 2 autres robots leur fournissent des murs s'ils se font voler. Edit: Les robots de support fonctionnent beaucoup mieux maintenant.
la source
Demandeurs
Tous travaux en cours. J'ai beaucoup d'idées, mais presque aucune ne fonctionne.
Surtout, gros problème avec les actions ratées.Résolu!la source