Wprowadzenie
Jest to interaktywny konkurs króla wzgórza, w którym kontroler jest w pełni zawarty we fragmencie stosu u dołu pytania. Kontroler automatycznie odczytuje odpowiedzi i gra w gry. Każdy może uruchomić go w dowolnym momencie bezpośrednio w przeglądarce.
Mechanika tego konkursu jest bardzo podobna do mechaniki Red vs. Blue - Pixel Team Battlebots . Tyle, że gra, w którą gra się, choć wciąż oparta na siatce, jest zupełnie inna. Każda gra to 1 na 1 i nie ma drużyn. Każde zgłoszenie walczy o siebie i tylko jeden będzie ostatecznym mistrzem.
Kontroler używa JavaScript, a ponieważ JavaScript jest jedynym językiem skryptowym po stronie klienta, który obsługuje większość przeglądarek, wszystkie odpowiedzi muszą być również napisane w JavaScript .
W tej specyfikacji kursywą używany jest termin formalny dla mechaniki gry lub właściwości. Terminy te są używane w całym tekście, aby zachować spójny i jasny sposób odwoływania się do różnych części gry.
Rozgrywka
Podstawy
Każda odpowiedź na to pytanie reprezentuje gracza . Gry jest rywalizacja pomiędzy dwoma graczami, P1 i P2 . Każdy gracz kontroluje stado 8 botów , ponumerowanych od 0 do 7. Gry odbywają się na planszy , arenie komórkowej 128 × 64 , której dolne 8 rzędów zaczyna się od ścian („bloków”), a pozostałe rzędy zaczynają się od powietrza . Komórki poza granicami siatki są uważane za powietrze.
Współrzędna x siatki waha się od 0 po lewej stronie do 127 po prawej stronie, a y wynosi od 0 u góry do 63 na dole.
Przykładowa siatka początkowa:
Boty zawsze pozostają wyrównane do komórek siatki, a wiele botów może zajmować tę samą komórkę. Boty mogą zajmować tylko komórki powietrzne. Boty P1 zawsze zaczynają się w linii 0-7 po lewej stronie rzędu nad ścianami, a boty P2 zawsze zaczynają się w linii 7-0 po prawej stronie.
W sąsiedzi z bot lub komórki są komórkami 8 bezpośrednio prostopadła i skośnie do niej.
Pole widzenia ( FOV ) bota to kwadrat komórek 13 × 13 wyśrodkowany na bocie. Mówi się, że komórka lub wrogi bot znajduje się w polu widzenia gracza, jeśli znajduje się w polu widzenia co najmniej jednego z botów gracza.
Ruchy i akcje
Podczas gry każdy gracz może się poruszyć 1000 razy. P1 najpierw się porusza, potem P2, potem P1 i tak dalej, aż do wykonania 2000 ruchów ogółem, w którym to momencie gra się kończy.
Podczas ruchu każdy gracz otrzymuje informacje o stanie gry oraz komórkach siatki i botach wroga w swoim polu widzenia i wykorzystuje je do podjęcia decyzji o działaniu każdego z nich.
Domyślna akcja to nic nie rób , gdy bot nie porusza się ani nie wchodzi w interakcje z siatką.
Inne działania to ruch , chwytanie i umieszczanie :
Bot może przenieść się do jednej z sąsiednich komórek C, jeśli:
- C nie jest poza zakresem,
- C to powietrze (tj. Nie ściana),
- a przynajmniej jeden z sąsiadów C jest ścianą.
Jeśli się powiedzie, bot przejdzie do C.
Bot może złapać jedną z sąsiednich komórek C, jeśli:
- C nie jest poza zakresem,
- C to ściana,
- a bot nie nosi już ściany.
Jeśli się powiedzie, C zamieni się w powietrze, a bot będzie niósł ścianę.
Bot może umieścić w jednej z sąsiednich komórek C, jeśli:
- C nie jest poza zakresem,
- C to powietrze,
- żaden bot żadnego z graczy nie zajmuje C,
- a bot niesie ścianę.
Jeśli się powiedzie, C stanie się ścianą, a bot nie będzie już dźwigał ściany.
Nieudane działania skutkują brakiem działania.
Komórka zajmowana przez co najmniej jednego robota niosącego ścianę ma narysowany na niej mały kwadrat w kolorze ściany. Boty zaczynają się bez ścian.
Pamięć
Podczas ruchu gracz może uzyskać dostęp i zmienić swoją pamięć - początkowo pusty ciąg znaków, który trwa przez całą grę i może służyć do przechowywania danych strategicznych.
Cel
Komórka w żółtym celowniku to cel , który zaczyna się w losowej pozycji. Każdy gracz ma wynik, który zaczyna się od 0. Gdy bot gracza zbliża się do bramki, wynik tego gracza wzrasta o 1, a cel jest losowo zmieniany przed następną turą. Gracz z najwyższym wynikiem na koniec gry wygrywa. Remis, jeśli wyniki są równe.
Jeśli podczas ruchu wiele botów zbliży się do celu, gracz nadal otrzymuje tylko jeden punkt.
Jeśli cel był w tym samym miejscu na 500 ruchów, jest losowo ponownie ustawiany. Za każdym razem, gdy cel jest losowo ustawiony, na pewno nie zostanie on umieszczony na komórce zajmowanej przez bota.
Co programować
Napisz treść dla tej funkcji:
function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
//body goes here
}
Będzie on wywoływany raz za każdym razem, gdy Twój gracz się poruszy i musi zwrócić działania, które chcesz, aby każdy z twoich botów wykonał podczas tego ruchu.
Możesz użyć kodu linii bazowej jako punktu początkowego.
Parametry
p1
to bool,true
jeśli jesteś P1 ifalse
jeśli jesteś P2id
jest liczbą całkowitą będącą identyfikatorem odpowiedzi.
- Możesz znaleźć identyfikator odpowiedzi, klikając poniższy link „udostępnij” i szukając numeru bezpośrednio
a/
w adresie URL.- Identyfikator wpisu testowego to -1.
eid
jest liczbą całkowitą będącą identyfikatorem odpowiedzi twojego wroga.move
jest liczbą całkowitą od 1 do 1000, która mówi, jaki ruch wykonujesz.goal
jest obiektem o właściwościachx
iy
. Są to współrzędne celu. Są one przyznawane, nawet jeśli cel nie pochodzi z pola widzenia.grid
jest funkcją, która przyjmuje argumenty xiy, npgrid(x,y)
. Zwraca:
-1
dla „nieznane”, jeśli argumentami nie są dwie liczby całkowite lub jeślix,y
nie ma go w FOV.0
dla „powietrza”, jeślix,y
jest poza zakresem lub jeśli komórka wx,y
jest powietrzem.1
dla „ściany”, jeśli komórka wx,y
jest ścianą.
bots
to tablica twoich 8 botów. Jej elementy są obiektami o właściwościachx
,y
orazhasWall
:
x
iy
są współrzędnymi bota.hasWall
jest,true
jeśli bot niesie ścianę, afalse
jeśli nie.
bots
jest zawsze zamawiany normalnie, N-ty indeks odpowiada numerowi bota N.ebots
to szereg obiektów zx
,y
ihasWall
właściwości jakbots
. W grze znajdują się tylko wrogie boty w twoim FOVebots
. Miałby więc długość 0, jeśli w twoim FOV nie ma żadnych wrogich botów. Jest zamawiany losowo.getMem
to funkcja bez argumentów zwracająca pamięć.setMem
jest funkcją, która przyjmuje jeden argument M. Jeśli M jest łańcuchem o długości 256 znaków lub mniejszym, twoja pamięć jest aktualizowana do M, w przeciwnym razie nic się nie dzieje.
Obiekt przeglądarki console
jest dostępny tylko dla wpisu testowego.
Zwracana wartość
Twoja funkcja musi zwrócić tablicę dokładnie 8 liczb całkowitych, każda w zakresie od 0 do 24. Wartość o indeksie N to czynność, którą podejmie bot numer N.
Wszystkie twoje boty nic nie zrobią, jeśli twoja funkcja:
- Zgłasza błąd dowolnego rodzaju. ( błąd )
- Wykonanie trwa dłużej niż 20 milisekund . ( limit czasu )
- Nie zwraca tablicy 8 liczb całkowitych od 0 do 24. ( zniekształcony )
Dla wygody liczba błędów, przekroczenia limitu czasu i zniekształcone działania są wyświetlane po zakończeniu gry.
Każda liczba od 0 do 24 odpowiada konkretnej akcji bota:
- 0 oznacza nic nie robienie.
- 1-8 są do przeprowadzki.
- 9-16 jest do zgarnięcia.
- 17–24 przeznaczone są do umieszczenia.
Każda z 8 wartości ruchu, chwytania i umieszczania odpowiada jednej z sąsiednich komórek bota, jak pokazano tutaj:
Na przykład 15
jest akcja polegająca na złapaniu komórki poniżej bota.
Działania bota są obsługiwane w kolejności bot 0 do bota 7. Na przykład, jeśli podczas jednego ruchu bot 0 zostanie umieszczony na ścianie w tej samej komórce powietrznej, bot 1 powinien przejść, komórka powietrzna stanie się ścianą przed botem Akcja 1 jest obsługiwana, a bot 1 zakończy się niepowodzeniem.
Nieudane działania stają się niczym i mówi się, że zawiodły . Liczniki nieudanych akcji są również wyświetlane po zakończeniu gry.
Zasady
Mogę tymczasowo lub na stałe zdyskwalifikować użytkowników lub odpowiedzi, które nie przestrzegają tych zasad. Dyskwalifikowane zgłoszenia nie kwalifikują się do wygrania.
Deklarując zmienne lub funkcje, musisz użyć
var
słowa kluczowego.
npvar x = 10
lubvar sum = function(a, b){ return a + b }
miejsca zadeklarował bezvar
stać globalny i może kolidować z kontrolerem. Podjęto kroki, aby ingerencja była niemożliwa, ale zrób to, aby się upewnić.Twój kod nie powinien działać wolno ani tracić czasu.
Nie można zatrzymać funkcji JavaScript w trakcie wykonywania, więc kod każdego gracza jest uruchamiany do końca. Jeśli uruchomienie kodu zajmie dużo czasu, wszyscy korzystający z odtwarzacza zauważą i będą zirytowani. Idealnie, wpisy zawsze będą działały dobrze w granicach 20ms.- Musisz użyć kodu zgodnego z ECMAScript 5 w najnowszej wersji Firefoksa, ponieważ tutaj go uruchomię. Nie używaj funkcji z ECMAScript 6, ponieważ nie jest on jeszcze obsługiwany w wielu przeglądarkach.
- Możesz odpowiedzieć do 3 razy, ale tylko wtedy, gdy każda z twoich strategii jest znacząco inna. Możesz edytować odpowiedzi w dowolny sposób.
- Nie możesz próbować mieć pamięci, chyba że używasz
getMem
isetMem
. - Nie wolno próbować uzyskiwać dostępu do kontrolera, kodu innego gracza ani zasobów zewnętrznych ani modyfikować ich.
- Nie możesz próbować modyfikować niczego wbudowanego w JavaScript.
- Odpowiedzi nie muszą być deterministyczne. Możesz użyć
Math.random
.
Format odpowiedzi
#EntryName
Notes, etc.
<!-- language: lang-js -->
//function body
//probably on multiple lines
More notes, etc.
Pierwszy blok kodu wielowierszowego musi zawierać treść funkcji.
Nazwa wpisu jest ograniczona do 20 znaków.
Twój wpis pojawi się w kontrolerze z tytułem EntryName - Username [answer ID]
, a także, [DQ]
jeśli zostanie zdyskwalifikowany.
Zwycięski
Kiedy pytanie będzie trwać przez co najmniej 3 tygodnie, a po udzieleniu odpowiedzi ułożę mistrza.
Użyję funkcji autorun kontrolera . W rundzie autouruchamiania każdy niedyskwalifikowany gracz rozgrywa dwie gry ze sobą, jedną jako P1, drugą jako P2 (podwójne rundy).
W ciągu kilku godzin wykonam jak najwięcej rund. Będzie to zależeć od liczby zgłoszeń i ich czasuochłonności. Ale zapewniam cię, że staram się uzyskać dokładną finałową tabelę wyników. Gracz z największą liczbą wygranych jest mistrzem, a ich odpowiedź zostanie zaakceptowana.
Będę używać Firefoksa na laptopie z 64-bitowym systemem Windows 8.1, 4 GB pamięci RAM i czterordzeniowym procesorem 1,6 GHz.
Nagroda
Napiszę i opublikuję wyzwanie PPCG specjalnie przeznaczone dla mistrza. Będzie to w jakiś sposób wiązało się z ich nazwą użytkownika lub awatarem lub czymś o nich. Będę sam decydować o tym, jakie będzie wyzwanie po zakończeniu tego konkursu. Napiszę to najlepiej jak potrafię i postaram się, aby stało się to Hot Network Question.
Kontroler
Uruchom ten fragment kodu lub przejdź do tego JSFiddle, aby użyć kontrolera. Zaczyna się od wybranych losowych graczy. Testowałem go dokładnie tylko w Firefox i 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>
To pytanie ma swój własny czat. Zamieszczam tam tabele wyników co kilka dni.
źródło
Odpowiedzi:
Czarny Rycerz
Nazwa bota pochodzi od wczesnego planu, aby móc poruszać się jak rycerz szachowy: ponad dwa, jeden itd., Co w niektórych przypadkach byłoby szybsze.
Wyjaśnienie
Określenie, który ruch wykonać dla każdego bota, można podzielić na dwa główne zadania: ustalenie, gdzie się udać i jak się tam dostać.
Gdzie iść
Podstawowe zadanie ustalenia, gdzie się udać, jest łatwe: idź do celu, jeśli jesteś najbliżej, lub w inny sposób postaraj się ustawić jak najdalej od członków drużyny. Najpierw przechodzi przez każdego bota i określa, czy jest osierocony (tzn. Nie ma wokół niego żadnych bloków i nie trzyma ściany lub jest otoczony ścianami i trzyma ścianę). Następnie ponownie zapętla boty, aby znaleźć nietkniętego bota najbliższego celu. Wszystkie inne boty zmierzają w kierunku rozstawienia, z dolnym rzędem na powierzchni bloków (
y=55
) i górnym rzędem wy=27
. Gdy wie, gdzie iść, przekazuje ją domoveTo
funkcji.Jak się tam dostać
Decyzja o dotarciu do celu jest znacznie trudniejsza, ponieważ boty muszą zawsze znajdować się przy ścianie, aby się poruszać. Najpierw oblicza kod kierunku (1–8) miejsca docelowego w stosunku do jego aktualnej pozycji. Na przykład, jeśli bot znajdowałby się w lewym dolnym rogu i chciałby przejść do prawego górnego rogu, użyłby kodu kierunkowego 3. Dla każdego kierunku zapisałem na stałe listę ruchów, przy czym pierwszy byłby idealny, górny -priorytet ruch, a ostatnia jest ostatecznością. Jest to oddzielone od tego, czy bot ma ścianę, ponieważ nie możesz użyć ruchu miejsca bez ściany lub użyć ruchu chwytu, mając już ścianę.
Oczywiście użycie idealnego ruchu nie zawsze działa i spowodowałoby wiele nieudanych działań. W
checkMove
tym momencie pojawia się funkcja. Ta funkcja sprawdza potencjalny ruch pod kątem każdego wymagania, aby na przykład zapobiec wypadnięciu bota poza granice lub do ściany. Jeśli bot znajdujący się w pobliżu wroga może zostać osierocony (ma on tylko jedną przylegającą ścianę, którą może przejąć bot), staje się to priorytetem, więc funkcja wrócifalse
do innego legalnego ruchu, aby mógł przejść do ruchów chwytania i wyjąć wróg. Ta funkcja zapobiega kilku innym głupim ruchom, takim jak umieszczenie ściany w bramce lub innego bota.Ciąg pamięci
Czasami bot nie będzie właściwie osierocony, ale będzie próbował wykonać ten sam ruch i nie skończy się poruszaniem (zwykle podnosząc ścianę i kładąc ją, podnosząc ją i kładąc itp.). Aby temu zapobiec, używa ciągu pamięci do zapamiętania dwóch ostatnich ruchów, ostatniej pozycji xiy oraz tego, ile razy był jeszcze nieruchomy. Każdy układ odniesienia jest zakodowany jako pojedynczy znak dla łatwego podziału. (Ciąg musi składać się z 256 znaków , a nie bajtów, więc używanie wielobajtowych znaków Unicode nie stanowi problemu, podobnie jak w przypadku typowych wyzwań golfowych.)
Na przykład, powiedzmy, że bot złapał ścianę po lewej (kod
12
) w tym zakręcie, zastąpił ją w lewo (kod20
) w poprzednim zakręcie i był na współrzędnych (107
,3
) dla poprzednich16
zakrętów. Ciąg pamięci dla tego wystąpienia będzie kodowany w następujący sposób:ck
: Dwa ostatnie kody akcji są konwertowane na base36, aby cyfry dwucyfrowe były pojedynczą literą.@
: Liczba wyświetleń jest reprezentowana jako znak ASCII z tym kodem + 48, aby pominąć znaki niedrukowalne, więc pierwsze dziewięć razy pokazuje rzeczywistą liczbę (String.fromCharCode(0 + 48)
→0
).Ħ¾
: Współrzędne xiy są również przedstawiane jako znak o tej wartości, tym razem przesunięty o nieco dowolną wartość 187, aby uniknąć problematycznych znaków.Typowy ciąg pamięci podczas gry może składać się
53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬò
z grupy pięciu znaków dla każdego z ośmiu botów.źródło
Placówki
Każdy z 8 botów bierze kwadrat 32 na 32 i biegnie do jego środka (nieznacznie przesunęłem centra, w przeciwnym razie kończą się parowaniem i podróżowaniem pionowo z jednym blokiem ściany między nimi, więc jeden z nich zostaje unieruchomiony).
Każdy bot pozostanie na środku swojego kwadratu, chyba że cel znajdzie się w odległości 32 komórek od jego odpowiedniego środka, w którym to przypadku wybiegnie do celu, a następnie z powrotem do swojego środka.
Nadal korzysta z podstawowej metody osiągania celu (celu lub środka), więc nie porusza się po przekątnej. Tylko punkt wyjścia ...
źródło
Linia bazowa
Jest to najprostszy konsekwentnie działający kontroler stada botów, jaki mogłem wymyślić. Będzie to moja jedyna niedyskwalifikowana odpowiedź i posłuży jako podstawa do oceny innych odpowiedzi. Aby wygrać konkurs, technicznie jest on w biegu, ale pokonanie go nie powinno być trudne.
Każdy kod tutaj może zostać skopiowany i użyty w innej odpowiedzi, bez konieczności podawania źródła.
Każdy z 8 botów niezależnie stosuje tę samą podstawową metodę. Z tego powodu mają tendencję do zlepiania się, chyba że zostaną rozdzieleni przez coś zewnętrznego. Boty nigdy nie dbają o to, gdzie są członkowie drużyny lub wrogowie, starają się jedynie zbliżyć do celu. Poruszają się tylko prostopadle, najpierw dopasowując swoje x do celu x, a następnie y. Nigdy nie poruszanie się po przekątnej oznacza, że marnują dużo czasu w podróży.
Algorytm ruchu każdego bota jest następujący:
źródło
Gracz zespołowy
W tej chwili przedłożenie to jest dalekie od ideału. Ma podobną strategię jak Outposts, ale tylko 6 botów jest „w powietrzu”. Pozostałe 2 boty dostarczają im ściany, jeśli zostaną skradzione. Edycja: Boty wspierające działają teraz znacznie lepiej.
źródło
Poszukiwacze
Stll pracuje w toku. Mam wiele pomysłów, ale prawie żaden z nich nie działa.
Przede wszystkim duży problem z nieudanymi działaniami.Rozwiązany!źródło