Introdução
Este é um concurso interativo do tipo rei do monte , onde o controlador está totalmente contido em um snippet de pilha na parte inferior da pergunta. O controlador lê automaticamente as respostas e joga nos jogos. Qualquer pessoa pode executá-lo a qualquer momento, diretamente no navegador.
A mecânica deste concurso é muito parecida com a do Red vs. Blue - Pixel Team Battlebots . Exceto que o jogo que está sendo jogado, embora ainda baseado em grade, seja totalmente diferente. Cada jogo é 1 x 1 e não há equipes. Cada participante está lutando por si e apenas um será o campeão final.
O controlador usa JavaScript e, como o JavaScript é a única linguagem de script do lado do qual a maioria dos navegadores suporta, todas as respostas também devem ser escritas em JavaScript .
Nesta especificação, o texto em itálico é usado para indicar o termo formal para uma mecânica ou propriedade do jogo. Esses termos são usados para ajudar a manter uma maneira clara e coesa de se referir às diferentes partes do jogo.
Jogabilidade
Noções básicas
Toda resposta a esta pergunta representa um jogador . Um jogo é uma competição entre dois jogadores, P1 e P2 . Cada jogador controla um bando de 8 bots , de 0 a 7. Os jogos acontecem na grade , uma arena de células de 128 × 64 cujas 8 linhas inferiores começam como paredes (os 'blocos') e outras linhas começam como ar . As células fora dos limites da grade são consideradas ar.
A coordenada x da grade varia de 0 à esquerda a 127 à direita e y varia de 0 na parte superior a 63 na parte inferior.
Grade inicial de amostra:
Os robôs sempre ficam alinhados às células da grade e vários robôs podem ocupar a mesma célula. Bots podem ocupar apenas células do ar. Os bots de P1 sempre começam na linha 0-7, à esquerda da linha acima das paredes, e os bots de P2 sempre começam na linha 7-0, à direita.
Os vizinhos de um bot ou célula são as 8 células diretamente ortogonais e diagonais para ele.
O campo de visão ( FOV ) de um bot é o quadrado de célula 13 × 13 centrado em um bot. Diz-se que um bot de célula ou inimigo está no FOV de um jogador se estiver no FOV de pelo menos um dos bots do jogador.
Movimentos e ações
Durante um jogo, cada jogador começa a se mover 1000 vezes. P1 se move primeiro, depois P2, depois P1 e assim por diante até 2000 movimentos totais terem sido feitos, momento em que o jogo termina.
Durante uma jogada, cada jogador recebe informações sobre o estado do jogo, as células da grade e os robôs inimigos no FOV e o usa para decidir sobre uma ação a ser executada por cada um dos robôs.
A ação padrão é não fazer nada , onde o bot não se move ou interage com a grade.
As outras ações são mover , agarrar e colocar :
Um bot pode mover - se para uma de suas células vizinhas C se:
- C não está fora dos limites,
- C é ar (ou seja, não é uma parede),
- e pelo menos um dos vizinhos de C é um muro.
Se for bem-sucedido, o bot passará para C.
Um bot pode pegar uma de suas células vizinhas C se:
- C não está fora dos limites,
- C é uma parede,
- e o bot ainda não está carregando uma parede.
Se for bem-sucedido, C se tornará ar e o bot estará carregando uma parede.
Um bot pode colocar em uma de suas células vizinhas C se:
- C não está fora dos limites,
- C é ar,
- nenhum robô de qualquer jogador ocupa C,
- e o bot está carregando uma parede.
Se for bem-sucedido, C se tornará uma parede e o bot não estará mais carregando uma parede.
Ações malsucedidas resultam em não fazer nada.
Uma célula ocupada por pelo menos um bot que carrega uma parede possui um pequeno quadrado colorido na parede. Bots começam sem paredes.
Memória
Durante uma jogada, um jogador pode acessar e alterar sua memória , uma sequência inicialmente vazia que dura ao longo do jogo e pode ser usada para armazenar dados estratégicos.
Objetivo
A célula na mira amarela é o objetivo , que começa em uma posição aleatória. Cada jogador tem uma pontuação que começa em 0. Quando o bot de um jogador se move para o gol, a pontuação do jogador aumenta em 1 e o gol é reposicionado aleatoriamente antes do próximo turno. O jogador com a maior pontuação no final de um jogo vence. É um empate se a pontuação for igual.
Se vários bots forem para o objetivo durante um movimento, o jogador ainda ganha apenas um ponto.
Se o objetivo estiver no mesmo local por 500 jogadas, ele será reposicionado aleatoriamente novamente. Sempre que o objetivo for posicionado aleatoriamente, é garantido que ele não será colocado em uma célula ocupada por um bot.
O que programar
Escreva um corpo para esta função:
function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
//body goes here
}
Ele será chamado uma vez toda vez que seu jogador se mover e precisar retornar as ações que você deseja que cada um de seus bots realize durante esse movimento.
Você pode usar o código de linha de base como ponto de partida.
Parâmetros
p1
é um bool que étrue
se você é P1 efalse
se você é P2id
é um número inteiro que é o ID da resposta.
- Você pode encontrar o ID de uma resposta clicando no link 'compartilhar' abaixo e procurando o número logo
a/
em seguida no URL.- O ID da entrada de teste é -1.
eid
é um número inteiro que é o ID da resposta do seu inimigo.move
é um número inteiro de 1 a 1000 que indica em que movimento você está.goal
é um objeto comx
ey
propriedades. Essas são as coordenadas do objetivo. Eles são dados mesmo que o objetivo esteja fora do seu FOV.grid
é uma função que recebe argumentos x e y, por exemplogrid(x,y)
. Retorna:
-1
para 'desconhecido' se os argumentos não forem dois números inteiros ou sex,y
não estiver no seu FOV.0
para 'ar' sex,y
estiver fora dos limites ou se a célulax,y
estiver no ar.1
para 'parede' se a célula emx,y
for uma parede.
bots
é uma matriz dos seus 8 bots. Seus elementos são objetos com propriedadesx
,y
ehasWall
:
x
ey
são as coordenadas do bot.hasWall
étrue
se o bot está carregando uma parede efalse
se não estiver.
bots
sempre é ordenado normalmente, o enésimo índice corresponde ao número de bot N.ebots
é uma disposição de objectos comx
,y
ehasWall
as propriedades tal comobots
. Apenas os bots inimigos no seu FOV estãoebots
. Portanto, teria comprimento 0 se não houvesse bots inimigos no seu FOV. É ordenado aleatoriamente.getMem
é uma função sem argumentos que retorna sua memória.setMem
é uma função que recebe um argumento M. Se M é uma sequência de 256 caracteres ou menos, sua memória é atualizada para M, caso contrário, nada acontece.
O console
objeto do navegador está disponível apenas para a Entrada de Teste.
Valor de retorno
Sua função precisa retornar uma matriz de exatamente 8 números inteiros, cada um variando de 0 a 24. O valor no índice N é a ação que o número de bot N executará.
Todos os seus bots não farão nada se sua função:
- Lança um erro de qualquer tipo. ( erro )
- Leva mais de 20 milissegundos para executar. ( tempo limite )
- Não retorna uma matriz de 8 números inteiros, variando de 0 a 24. ( malformado )
Por conveniência, o número de erros, tempos limite e ações malformadas são exibidos quando um jogo termina.
Cada um dos números de 0 a 24 corresponde a uma ação específica do bot:
- 0 é para não fazer nada.
- 1-8 são para mover.
- 9-16 são para agarrar.
- 17-24 são para colocação.
Cada um dos 8 valores para mover, agarrar e colocar corresponde a uma das células vizinhas do bot, como mostrado aqui:
Então, por exemplo, 15
é a ação de pegar a célula abaixo do bot.
As ações de bot são tratadas na ordem bot 0 a bot 7. Por exemplo, se durante um movimento o bot 0 é instruído a colocar uma parede na mesma célula aérea o bot 1 foi instruído a mover para, a célula aérea se tornará uma parede antes do bot A ação de 1 é manipulada e o bot 1 não terá êxito.
Ações malsucedidas tornam-se nada e dizem ter falhado . Contadores de ações com falha também são exibidos quando o jogo termina.
Regras
Posso desqualificar temporariamente ou permanentemente usuários ou respostas que não sigam essas regras. Entradas desqualificadas não são elegíveis para ganhar.
Ao declarar variáveis ou funções, você deve usar a
var
palavra - chave
por exemplo,var x = 10
ouvar sum = function(a, b){ return a + b }
Coisas declaradas semvar
se tornarem globais e podem interferir no controlador. Foram tomadas medidas para que essa interferência seja impossível, mas faça isso para garantir.Seu código não deve ser executado lentamente ou perder tempo.
É impossível interromper as funções JavaScript no meio da execução, para que o código de cada jogador seja executado até a conclusão. Se o seu código demorar muito para ser executado, todos os usuários do seu player perceberão e ficarão irritados. Idealmente, as entradas sempre funcionarão bem dentro do limite de 20ms.- Você deve usar um código compatível com o ECMAScript 5 na versão mais recente do Firefox, pois é onde eu o executarei. Não use os recursos do ECMAScript 6, pois ele ainda não é suportado em muitos navegadores.
- Você pode responder até três vezes, mas apenas se cada uma de suas estratégias for consideravelmente diferente. Você pode editar as respostas quantas vezes desejar.
- Você não pode tentar ter nenhum tipo de memória, exceto com o uso de
getMem
esetMem
. - Você não pode tentar acessar ou modificar o controlador, o código de outro jogador ou recursos externos.
- Você não pode tentar modificar nada incorporado ao JavaScript.
- As respostas não precisam ser determinísticas. Você pode usar
Math.random
.
Formato da resposta
#EntryName
Notes, etc.
<!-- language: lang-js -->
//function body
//probably on multiple lines
More notes, etc.
O primeiro bloco de código multilinha deve conter seu corpo da função.
O nome da entrada é limitado a 20 caracteres.
Sua entrada será exibida no controlador com o título EntryName - Username [answer ID]
, mais [DQ]
se for desqualificada.
Ganhando
Quando a pergunta terminar há pelo menos três semanas e quando a resposta for resolvida, coroarei o campeão.
Vou usar o recurso de execução automática do controlador . Em uma rodada de execução automática, todo jogador não desqualificado joga dois jogos entre si, um como P1, um como P2 (rodízio duplo).
Vou executar o máximo de rodadas que puder no período de algumas horas. Isso dependerá de quantas submissões houver e de quanto tempo serão intensas. Mas tenha certeza, estou comprometido em obter uma classificação final precisa. O jogador com mais vitórias é o campeão e sua resposta será aceita.
Vou usar o Firefox em um laptop com Windows 8.1 de 64 bits, 4 GB de RAM e um processador quad-core de 1,6 GHz.
Prêmio
Escreverei e publicarei um desafio PPCG especificamente dedicado ao campeão. De alguma forma, envolverá seu nome de usuário ou avatar ou algo sobre eles. Eu vou decidir em particular sobre o que será o desafio quando este concurso terminar. Escreverei da melhor maneira possível e tentarei garantir que se torne uma Pergunta de Rede Quente.
Controlador
Execute esse trecho ou acesse este JSFiddle para usar o controlador. Começa com jogadores aleatórios selecionados. Eu só o testei completamente no Firefox e 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>
Esta questão tem sua própria sala de chat. Vou postar placares lá todos os dias.
fonte
Respostas:
Cavaleiro Negro
O nome do bot vem de um plano inicial para que ele possa se mover como um cavaleiro de xadrez: mais de dois, um para cima, etc., o que seria mais rápido em alguns casos.
Explicação
A determinação de qual movimento fazer para cada bot pode ser dividida em duas tarefas principais: descobrir para onde ir e como chegar lá.
Onde ir
A tarefa básica de descobrir para onde ir é fácil: vá em direção à meta, se você estiver mais próximo, ou tente se posicionar o mais distante possível dos colegas de equipe. Primeiro ele passa por cada bot e determina se está preso (ou seja, não possui blocos ao redor e não está segurando uma parede, ou está cercado por paredes e segura uma parede). Em seguida, ele percorre os robôs novamente para encontrar o bot não preso mais próximo do objetivo. Todos os outros robôs avançam no sentido de serem espaçados, com a linha inferior na superfície dos blocos (
y=55
) e a linha superior emy=27
. Uma vez que ele sabe para onde ir, ele o entrega àmoveTo
função.Como chegar lá
Decidir como chegar ao destino é muito mais difícil, porque os robôs devem estar sempre adjacentes a uma parede para se moverem. Primeiro, ele descobre o código de direção (1 a 8) do destino em relação à sua posição atual. Por exemplo, se um bot estivesse no canto inferior esquerdo e quisesse ir para o canto superior direito, usaria o código de direção 3. Para cada direção, codifiquei uma lista de movimentos, sendo o primeiro o ideal, o topo movimento de prioridade, e o último sendo o último recurso. Isso é separado se o bot tem ou não uma parede, porque você não pode usar um movimento de lugar sem uma parede ou usar um movimento de agarrar enquanto já possui uma parede.
Obviamente, usar a jogada ideal nem sempre funciona e resultaria em muitas ações com falha. É aqui que
checkMove
entra. Essa função verifica o movimento potencial em relação a todos os requisitos, para impedir que o bot se mova para fora dos limites ou para uma parede, por exemplo. Se um bot inimigo próximo puder ser preso (ele tem apenas uma parede adjacente que pode ser tomada pelo bot), isso se torna sua prioridade; portanto, a função retornaráfalse
para um movimento legítimo, de modo que possa pular para os movimentos de agarrar e retirar o inimigo. A função evita vários outros movimentos estúpidos, como colocar uma parede no gol ou outro bot.A sequência de memória
Às vezes, o bot não fica realmente preso, mas continua tentando o mesmo movimento e não acaba se movendo (geralmente pegando uma parede e largando-a, pegando-a e largando-a etc.). Para evitar isso, ele usa a sequência de memória para lembrar seus dois últimos movimentos, sua última posição xey, e quantas vezes ficou parada. Cada dado é codificado como um único caractere para facilitar a divisão. (A cadeia deve ter 256 caracteres , não bytes, portanto, o uso de caracteres Unicode multibyte não é um problema, como ocorre com os desafios típicos do golfe.)
Por exemplo, digamos que um bot agarrou a parede à esquerda (código
12
) nesta curva, substituiu-a à esquerda (código20
) na curva anterior e esteve nas coordenadas (107
,3
) nas16
curvas anteriores . A sequência de memória para esta instância seria codificada da seguinte maneira:ck
: Os dois códigos de ação mais recentes são convertidos em base36 para transformar os números de dois dígitos em uma única letra.@
: O número de vezes que ainda está parado é representado como o caractere ASCII com esse código + 48 para pular caracteres não imprimíveis e, portanto, as nove primeiras vezes ainda mostram o número real (String.fromCharCode(0 + 48)
→0
).Ħ¾
: As coordenadas x e y também são representadas como o caractere com esse valor, dessa vez compensado pelo valor algo arbitrário de 187 para evitar caracteres problemáticos.Uma sequência de memória típica durante o jogo pode ser
53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬò
, com um grupo de cinco caracteres para cada um dos oito bots.fonte
Outposts
Os 8 bots pegam um quadrado de 32 por 32 e correm para o centro (eu desloco os centros levemente, caso contrário, eles acabam pareando e viajando verticalmente com um bloco de parede entre eles, para que um deles fique preso).
Cada bot permanecerá no centro de seu quadrado, a menos que a meta esteja dentro de 32 células de seu respectivo centro; nesse caso, ela será executada na meta e depois retornada ao seu centro.
Ele ainda usa o método Linha de base para atingir seu objetivo (objetivo ou centro), para que não se mova na diagonal. Apenas um ponto de partida ...
fonte
Linha de base
Este é o controlador bot flock mais simples e de funcionamento consistente que eu consegui pensar. Será minha única resposta não desqualificada e servirá de base para julgar outras respostas. Está tecnicamente em disputa para vencer o concurso, mas vencê-lo não deve ser difícil.
Qualquer código aqui pode ser copiado e usado em outra resposta, sem necessidade de atribuição.
Cada um dos 8 bots segue independentemente o mesmo método básico. Eles tendem a se agrupar por causa disso, a menos que sejam separados por algo externo. Os robôs nunca se importam com o local onde estão os companheiros ou os inimigos, apenas tentam avançar em direção à meta. Eles apenas se movem ortogonalmente, primeiro combinando seu x com o objetivo x, depois seu y. Nunca se mover na diagonal significa que eles perdem muito tempo em viagens.
O algoritmo de movimento de cada bot é o seguinte:
fonte
Teamplayer
No momento, essa submissão está longe de ser perfeita. Ele tem uma estratégia semelhante ao Outposts, mas apenas 6 bots estão "no ar". Os outros 2 bots fornecem paredes se forem roubados. Edit: Os bots apoiadores têm um desempenho muito melhor agora.
fonte
Buscadores
Ainda trabalho em andamento. Eu tenho muitas idéias, mas quase nenhuma delas funciona.
Acima de tudo, grande problema com ações com falha.Resolvido!fonte