Intro
Ini adalah kontes interaktif king-of-the-hill di mana controller sepenuhnya terkandung dalam Stack Snippet di bagian bawah pertanyaan. Pengontrol secara otomatis membaca jawaban dan bermain melalui permainan. Siapa pun dapat menjalankannya kapan saja di browser mereka.
Mekanik dari kontes ini sangat mirip dengan yang ada di Red vs Blue - Pixel Team Battlebots . Kecuali permainan yang dimainkan, sementara masih berbasis grid, sama sekali berbeda. Setiap pertandingan adalah 1 lawan 1 dan tidak ada tim. Setiap entri berjuang untuk dirinya sendiri dan hanya satu yang akan menjadi juara akhir.
Pengontrol menggunakan JavaScript, dan karena JavaScript adalah satu-satunya bahasa skrip sisi-klien yang didukung sebagian besar browser, semua jawaban harus ditulis dalam JavaScript juga .
Dalam spesifikasi ini, teks yang dicetak miring digunakan untuk menunjukkan istilah formal untuk mekanik game atau properti. Istilah-istilah ini digunakan sepanjang untuk membantu mempertahankan cara yang kohesif dan jelas merujuk ke berbagai bagian permainan.
Gameplay
Dasar-dasar
Setiap jawaban untuk pertanyaan ini mewakili pemain . Sebuah permainan adalah kompetisi antara dua pemain, P1 dan P2 . Setiap pemain mengontrol kawanan 8 bot , bernomor 0 hingga 7. Permainan berlangsung di grid , arena sel 128 × 64 yang 8 baris bawahnya dimulai sebagai dinding ('blok') dan baris lainnya dimulai sebagai udara . Sel di luar batas grid dianggap udara.
Koordinat x grid berkisar dari 0 di sebelah kiri hingga 127 di sebelah kanan, dan y berkisar dari 0 di atas hingga 63 di bawah.
Contoh grid awal:
Bot selalu tetap sejajar dengan sel kisi dan beberapa bot dapat menempati sel yang sama. Bot hanya bisa menempati sel udara. Bot P1 selalu dimulai pada garis 0-7 di paling kiri dari baris di atas dinding dan bot P2 selalu dimulai pada garis 7-0 di paling kanan.
The tetangga dari bot atau sel adalah 8 sel secara langsung orthogonal dan diagonal untuk itu.
Bidang pandang ( FOV ) dari sebuah bot adalah sel 13x13 yang berpusat pada sebuah bot. Bot sel atau musuh dikatakan berada di FOV pemain jika berada di FOV setidaknya satu bot pemain.
Gerakan & Tindakan
Selama pertandingan, setiap pemain bisa bergerak 1000 kali. P1 bergerak terlebih dahulu, lalu P2, lalu P1 dan seterusnya hingga 2000 total gerakan telah dibuat, pada titik mana permainan berakhir.
Selama bergerak, setiap pemain menerima informasi tentang keadaan permainan dan sel-sel kisi dan bot musuh dalam FOV mereka, dan menggunakannya untuk memutuskan tindakan yang akan diambil masing-masing bot mereka.
Tindakan default adalah tidak melakukan apa-apa , di mana bot tidak bergerak atau berinteraksi dengan kisi.
Tindakan lain adalah bergerak , ambil , dan tempat :
Bot dapat pindah ke salah satu sel tetangganya C jika:
- C tidak keluar batas,
- C adalah udara (yaitu bukan dinding),
- dan setidaknya salah satu tetangga C adalah tembok.
Jika berhasil, bot akan pindah ke C.
Bot dapat mengambil salah satu sel tetangganya C jika:
- C tidak keluar batas,
- C adalah dinding,
- dan bot belum membawa dinding.
Jika berhasil, C akan menjadi udara dan bot sekarang akan membawa dinding.
Bot dapat menempatkan ke salah satu sel tetangganya C jika:
- C tidak keluar batas,
- C adalah udara,
- tidak ada bot dari salah satu pemain yang menempati C,
- dan bot itu membawa dinding.
Jika berhasil, C akan menjadi dinding dan bot tidak akan lagi membawa dinding.
Tindakan yang gagal menghasilkan do do nothing.
Sel yang ditempati oleh setidaknya satu bot pembawa dinding memiliki kotak kecil berwarna dinding yang digambar di atasnya. Bot mulai tanpa dinding.
Ingatan
Selama bergerak, pemain dapat mengakses dan mengubah ingatan mereka , string yang awalnya kosong yang berlangsung sepanjang permainan dan dapat digunakan untuk menyimpan data strategis.
Tujuan
Sel dalam crosshair kuning adalah tujuannya , yang dimulai dalam posisi acak. Setiap pemain memiliki skor yang dimulai dari 0. Ketika bot pemain bergerak ke gawang, skor pemain itu meningkat sebesar 1 dan gawang secara acak diposisikan ulang sebelum belokan berikutnya. Pemain dengan skor tertinggi di akhir pertandingan menang. Ini seri jika nilainya sama.
Jika beberapa bot bergerak ke gawang selama bergerak, pemain hanya mendapatkan satu poin.
Jika gawang telah berada di tempat yang sama untuk 500 gerakan, itu secara acak diposisikan lagi. Setiap kali tujuan diposisikan secara acak, dijamin tidak akan ditempatkan pada sel yang ditempati oleh bot.
Apa yang Diprogram?
Tulis badan untuk fungsi ini:
function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
//body goes here
}
Ini akan dipanggil sekali setiap kali pemain Anda bergerak dan perlu mengembalikan tindakan yang Anda inginkan dari masing-masing bot Anda selama gerakan itu.
Anda dapat menggunakan kode Baseline sebagai titik awal.
Parameter
p1
adalah bool itutrue
jika Anda P1 danfalse
jika Anda P2id
adalah bilangan bulat yang merupakan ID jawaban dari jawaban Anda.
- Anda dapat menemukan ID jawaban dengan mengeklik tautan 'bagikan' di bawahnya dan mencari nomornya
a/
di URL.- ID Entri Tes adalah -1.
eid
adalah bilangan bulat yang merupakan ID jawaban dari jawaban musuh Anda.move
adalah bilangan bulat dari 1 hingga 1000 yang menyatakan gerakan Anda saat ini.goal
adalah objek denganx
dany
properti. Ini adalah koordinat sasaran. Mereka diberikan bahkan jika tujuannya keluar dari FOV Anda.grid
adalah fungsi yang mengambil argumen x dan y, misgrid(x,y)
. Ia mengembalikan:
-1
untuk 'tidak dikenal' jika argumennya bukan dua bilangan bulat atau jikax,y
tidak ada dalam FOV Anda.0
untuk 'udara' jika dix,y
luar batas atau jika sel dix,y
udara.1
untuk 'dinding' jika sel dix,y
adalah dinding.
bots
adalah array dari 8 bot Anda. Unsur-unsurnya adalah obyek dengan sifatx
,y
danhasWall
:
x
dany
merupakan koordinat bot.hasWall
adalahtrue
jika bot membawa dinding danfalse
jika tidak.
bots
selalu dipesan secara normal, indeks Nth sesuai dengan nomor bot N.ebots
adalah array dari objek denganx
,y
, danhasWall
sifat sepertibots
. Hanya bot musuh di FOV Anda yang masukebots
. Jadi itu akan memiliki panjang 0 jika jika tidak ada bot musuh di FOV Anda. Ini dipesan secara acak.getMem
adalah fungsi tanpa argumen yang mengembalikan memori Anda.setMem
adalah fungsi yang mengambil satu argumen M. Jika M adalah string 256 karakter atau kurang, memori Anda diperbarui ke M, jika tidak, tidak ada yang terjadi.
console
Objek browser tersedia untuk Tes Masuk saja.
Nilai Pengembalian
Fungsi Anda perlu mengembalikan array yang persis 8 bilangan bulat, masing-masing berkisar dari 0 hingga 24. Nilai pada indeks N adalah tindakan yang akan diambil oleh nomor bot N.
Semua bot Anda tidak akan melakukan apa pun jika fungsi Anda:
- Melempar kesalahan apa pun. ( kesalahan )
- Perlu waktu lebih dari 20 milidetik untuk dieksekusi. ( batas waktu )
- Tidak mengembalikan array 8 bilangan bulat mulai dari 0 hingga 24. ( salah format )
Untuk kenyamanan, jumlah kesalahan, batas waktu, dan tindakan salah ditampilkan saat permainan berakhir.
Masing-masing angka dari 0 hingga 24 sesuai dengan tindakan bot tertentu:
- 0 untuk tidak melakukan apa pun.
- 1-8 untuk bergerak.
- 9-16 untuk diraih.
- 17-24 untuk penempatan.
Masing-masing dari 8 nilai untuk memindahkan, meraih, dan menempatkan berkorespondensi dengan salah satu sel tetangga bot, seperti yang ditunjukkan di sini:
Jadi, misalnya, 15
adalah tindakan untuk meraih sel di bawah bot.
Tindakan bot ditangani dalam urutan bot 0 ke bot 7. Misalnya, jika selama satu gerakan bot 0 disuruh menempatkan dinding di sel udara yang sama bot 1 disuruh pindah, sel udara akan menjadi dinding sebelum bot Tindakan 1 ditangani dan bot 1 tidak akan berhasil.
Tindakan yang gagal menjadi tidak berguna dan dikatakan gagal . Penghitung tindakan yang gagal juga ditampilkan saat permainan berakhir.
Aturan
Saya mungkin untuk sementara waktu atau secara permanen mendiskualifikasi pengguna atau jawaban yang tidak mengikuti aturan ini. Entri yang didiskualifikasi tidak memenuhi syarat untuk menang.
Saat mendeklarasikan variabel atau fungsi, Anda harus menggunakan
var
kata kunci.
egvar x = 10
atauvar sum = function(a, b){ return a + b }
Hal dinyatakan tanpavar
menjadi global dan dapat mengganggu pengontrol. Langkah-langkah telah diambil sehingga gangguan ini seharusnya tidak mungkin, tetapi lakukan ini untuk memastikan.Kode Anda seharusnya tidak berjalan lambat atau membuang waktu.
Tidak mungkin untuk menghentikan fungsi JavaScript saat eksekusi, sehingga kode setiap pemain dijalankan hingga selesai. Jika kode Anda membutuhkan waktu lama untuk dijalankan, semua orang yang menjalankan pemutar Anda akan memperhatikan dan merasa terganggu. Idealnya, entri akan selalu berjalan dengan baik dalam batas 20 ms.- Anda harus menggunakan kode yang kompatibel dengan ECMAScript 5 di versi terbaru Firefox karena di sinilah saya akan menjalankannya. Jangan gunakan fitur dari ECMAScript 6 karena belum didukung di banyak browser.
- Anda dapat menjawab hingga 3 kali, tetapi hanya jika masing-masing strategi Anda sangat berbeda. Anda dapat mengedit jawaban sebanyak yang diinginkan.
- Anda tidak boleh mencoba memiliki memori apa pun kecuali melalui penggunaan
getMem
dansetMem
. - Anda tidak boleh mencoba mengakses atau memodifikasi controller, kode pemain lain, atau sumber daya eksternal.
- Anda tidak boleh mencoba mengubah apa pun yang ada di dalam JavaScript.
- Jawaban tidak harus bersifat deterministik. Anda bisa menggunakan
Math.random
.
Format Jawaban
#EntryName
Notes, etc.
<!-- language: lang-js -->
//function body
//probably on multiple lines
More notes, etc.
Blok kode multiline pertama harus berisi fungsi tubuh Anda.
Nama entri dibatasi hingga 20 karakter.
Entri Anda akan muncul di controller dengan judul EntryName - Username [answer ID]
, ditambah [DQ]
jika itu didiskualifikasi.
Kemenangan
Ketika pertanyaan telah muncul setidaknya selama 3 minggu dan setelah jawaban telah selesai, saya akan memahkotai juara.
Saya akan menggunakan fitur autorun controller . Dalam satu ronde autorun, setiap pemain yang tidak didiskualifikasi memainkan dua game satu sama lain, satu sebagai P1, satu sebagai P2 (sebuah round-robin ganda).
Saya akan autorun sebanyak mungkin putaran dalam jangka waktu beberapa jam. Ini akan tergantung pada berapa banyak pengiriman yang ada dan seberapa intensif waktu mereka. Tapi yakinlah, saya berkomitmen untuk mendapatkan leaderboard akhir yang akurat. Pemain dengan kemenangan terbanyak adalah sang juara dan jawaban mereka akan diterima.
Saya akan menggunakan Firefox pada laptop dengan Windows 8.1 64-bit, ram 4 GB, dan prosesor quad-core 1.6GHz.
Hadiah
Saya akan menulis dan memposting tantangan PPCG yang didedikasikan khusus untuk sang juara. Entah bagaimana itu akan melibatkan nama pengguna atau avatar mereka atau sesuatu tentang mereka. Saya akan memutuskan secara pribadi apa tantangannya ketika kontes ini selesai. Saya akan menuliskannya dengan kemampuan terbaik saya dan mencoba memastikan itu menjadi Pertanyaan Jaringan Panas.
Pengendali
Jalankan cuplikan ini atau buka JSFiddle ini untuk menggunakan pengontrol. Dimulai dengan pemain acak yang dipilih. Saya hanya mengujinya secara menyeluruh di Firefox dan 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>
Pertanyaan ini memiliki ruang obrolan sendiri. Saya akan memposting papan peringkat di sana setiap beberapa hari.
sumber
Jawaban:
Ksatria hitam
Nama bot berasal dari rencana awal untuk membuatnya bisa bergerak seperti ksatria catur: lebih dari dua, satu, dll, yang akan lebih cepat dalam beberapa kasus.
Penjelasan
Menentukan langkah yang harus dilakukan untuk setiap bot dapat dibagi menjadi dua tugas utama: menentukan ke mana harus pergi dan bagaimana menuju ke sana.
Ke mana harus pergi
Tugas dasar untuk menentukan ke mana harus pergi adalah mudah: pergi ke tujuan jika Anda terdekat, atau coba posisikan diri Anda sejauh mungkin dari rekan setim. Pertama kali melewati setiap bot dan menentukan apakah itu terdampar (yaitu ia tidak memiliki blok di sekitarnya dan tidak memegang dinding, atau dikelilingi oleh dinding dan memegang dinding). Kemudian loop melalui bot lagi untuk menemukan bot non-untai yang paling dekat dengan tujuan. Semua bot lainnya membuat jalan menuju spasi, dengan baris bawah di permukaan blok (
y=55
) dan baris atas diy=27
. Setelah tahu ke mana harus pergi, ia menyerahkannya kemoveTo
fungsi.Bagaimana menuju ke sana
Memutuskan cara mencapai tujuan jauh lebih sulit karena bot harus selalu bersebelahan dengan dinding untuk bergerak. Ini pertama-tama mencari tahu kode arah (1–8) tujuan relatif terhadap posisi saat ini. Sebagai contoh, jika sebuah bot berada di sudut kiri bawah dan ia ingin pergi ke kanan atas, itu akan menggunakan kode arah 3. Untuk setiap arah, saya membuat hardcode daftar gerakan, dengan yang pertama adalah yang ideal, atas Langkah -prioritas, dan yang terakhir menjadi pilihan terakhir. Ini dipisahkan oleh apakah bot memiliki dinding atau tidak, karena Anda tidak dapat menggunakan gerakan tempat tanpa dinding atau menggunakan gerakan ambil saat sudah memiliki dinding.
Tentu saja, menggunakan gerakan ideal tidak selalu berhasil, dan itu akan menghasilkan banyak tindakan yang gagal. Di sinilah
checkMove
masuk. Fungsi ini memeriksa gerakan potensial terhadap setiap persyaratan, untuk mencegah bot bergerak keluar dari batas atau ke dinding, misalnya. Jika bot musuh terdekat dapat terdampar (hanya memiliki satu dinding yang berdekatan yang dapat diambil oleh bot), itu menjadi prioritasnya, sehingga fungsi tersebut akan kembalifalse
untuk gerakan yang sah sehingga dapat melompat ke gerakan ambil dan mengambil musuh. Fungsi ini mencegah beberapa gerakan bodoh lainnya seperti menempatkan dinding di gawang atau bot lain.String memori
Kadang-kadang bot tidak akan benar-benar terdampar tetapi akan terus mencoba gerakan yang sama dan tidak berakhir bergerak (biasanya mengambil dinding dan meletakkannya, mengambilnya dan meletakkannya, dll). Untuk mencegah hal ini, ia menggunakan string memori untuk mengingat dua gerakan terakhirnya, posisi x dan y terakhirnya, dan berapa kali masih diam. Setiap datum dikodekan sebagai satu karakter untuk memudahkan pemisahan. (String harus 256 karakter , bukan byte, jadi menggunakan karakter Unicode multibyte bukanlah masalah, seperti halnya dengan tantangan golf pada umumnya.)
Sebagai contoh, misalkan bot meraih dinding di sebelah kirinya (kode
12
) belokan ini, menggantinya ke kiri (kode20
) di belokan sebelumnya, dan telah berada di koordinat (107
,3
) untuk16
belokan terakhir . String memori untuk instance ini akan dikodekan sebagai berikut:ck
: Dua kode tindakan terbaru dikonversi ke base36 untuk membuat angka dua digit menjadi satu huruf.@
: Jumlah berapa kali itu masih direpresentasikan sebagai karakter ASCII dengan kode itu + 48 untuk melompati karakter yang tidak dapat dicetak sehingga sembilan kali pertama masih menunjukkan angka yang sebenarnya (String.fromCharCode(0 + 48)
→0
).Ħ¾
: Koordinat x dan y juga direpresentasikan sebagai karakter dengan nilai itu, kali ini diimbangi oleh nilai agak arbitrer dari 187 untuk menghindari karakter yang bermasalah.String memori yang khas selama permainan mungkin
53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬò
, dengan sekelompok lima karakter untuk masing-masing dari delapan bot.sumber
Pos terdepan
8 bot masing-masing mengambil 32 dengan 32 persegi dan lari ke pusatnya (saya mengimbangi pusat sedikit jika mereka akhirnya berpasangan dan bepergian secara vertikal dengan satu blok dinding di antara mereka, sehingga salah satu dari mereka terdampar).
Setiap bot akan tetap berada di tengah alun-alun kecuali jika tujuannya berada dalam 32 sel dari pusatnya masing-masing, dalam hal ini ia akan berlari ke tujuan dan kemudian kembali ke pusatnya.
Ini masih menggunakan metode Baseline untuk mencapai targetnya (sasaran atau pusat) sehingga tidak bergerak secara diagonal. Hanya titik awal ...
sumber
Baseline
Ini adalah pengontrol kawanan bot yang berfungsi paling sederhana dan konsisten yang bisa saya pikirkan. Itu akan menjadi satu-satunya jawaban saya yang tidak didiskualifikasi dan akan menjadi dasar untuk menilai jawaban lain. Secara teknis dalam menjalankan untuk memenangkan kontes, tetapi mengalahkan itu seharusnya tidak sulit.
Salah satu kode di sini dapat disalin dan digunakan dalam jawaban lain, tidak diperlukan atribusi.
Masing-masing 8 bot secara independen mengikuti metode dasar yang sama. Mereka cenderung berkumpul bersama karena hal ini, kecuali mereka terpisah oleh sesuatu yang eksternal. Bot tidak pernah peduli di mana rekan satu tim atau musuh, mereka hanya berusaha untuk bergerak ke arah gawang. Mereka hanya bergerak secara orthogonal, pertama-tama mencocokkan x mereka dengan tujuan x, kemudian y mereka. Tidak pernah bergerak secara diagonal berarti mereka menghabiskan banyak waktu dalam perjalanan.
Algoritma pergerakan setiap bot adalah sebagai berikut:
sumber
Pemain tim
Saat ini, pengajuan ini masih jauh dari sempurna. Ini memiliki strategi yang sama seperti Outposts, tetapi hanya 6 bot "di udara". 2 bot lainnya memasok mereka dengan dinding jika mereka dicuri. Sunting: Bot pendukung tampil lebih baik sekarang.
sumber
Pencari
Semua masih dalam proses. Saya punya banyak ide, tetapi hampir tidak ada yang berhasil.
Di atas segalanya, masalah besar dengan tindakan yang gagal.Terpecahkan!sumber