вступление
Это интерактивное состязание " царь горы", в котором контроллер полностью содержится в фрагменте стека в нижней части вопроса. Контроллер автоматически читает ответы и играет в игры. Любой может запустить его в любое время прямо в своем браузере.
Механика этого конкурса очень похожа на тех, что у Red vs. Blue - Pixel Team Battlebots . За исключением того, что игра, в которую все еще играют, хотя и на основе сетки, полностью отличается. Каждая игра 1 против 1 и нет команд. Каждая запись борется за себя, и только один будет финальным чемпионом.
Контроллер использует JavaScript, и поскольку JavaScript является единственным языком сценариев на стороне клиента, который поддерживается большинством браузеров, все ответы должны быть написаны также на JavaScript .
В этой спецификации выделенный курсивом текст используется для обозначения формального термина игровой механики или свойства. Эти термины используются повсеместно, чтобы помочь поддерживать целостный и ясный способ обращения к различным частям игры.
Игровой процесс
основы
Каждый ответ на этот вопрос представляет игрока . Игра является конкуренция между двумя игроками, P1 и P2 . Каждый игрок контролирует стаю из 8 ботов , пронумерованных от 0 до 7. Игры проходят в сетке , клеточной арене 128 × 64 , нижние 8 рядов которой начинаются как стены («блоки»), а другие ряды начинаются как воздух . Клетки за пределами сетки считаются воздушными.
Координата x сетки находится в диапазоне от 0 слева до 127 справа, а y - от 0 вверху до 63 внизу.
Пример стартовой сетки:
Боты всегда остаются выровненными по ячейкам сетки, и несколько ботов могут занимать одну и ту же ячейку. Боты могут занимать только воздушные клетки. Боты P1 всегда начинаются с линии 0-7 слева от ряда над стенами, а боты P2 всегда начинаются с линии 7-0 справа.
В соседи из бота или ячеек являются 8 ячеек непосредственно ортогональна и по диагонали к нему.
Поле зрения ( FOV ) бота - это квадрат ячейки 13 × 13 с центром в боте. Сотовый или вражеский бот находится в поле зрения игрока, если он находится в поле зрения хотя бы одного из ботов игрока.
Ходы и действия
Во время игры каждый игрок может двигаться 1000 раз. Сначала ходы P1, затем P2, затем P1 и так далее, пока не будет сделано 2000 ходов, после чего игра заканчивается.
Во время хода каждый игрок получает информацию о состоянии игры, ячейках сетки и вражеских ботах в своем поле зрения и использует ее для определения действий, которые должен выполнить каждый из своих ботов.
Действие по умолчанию - ничего не делать , когда бот не двигается и не взаимодействует с сеткой.
Другие действия - перемещение , захват и размещение. :
Бот может переместиться в одну из соседних ячеек C, если:
- С не за пределами,
- С - воздух (т.е. не стена),
- и по крайней мере один из соседей С является стеной.
В случае успеха бот переместится на C.
Бот может захватить одну из своих соседних ячеек C, если:
- С не за пределами,
- C это стена,
- и бот уже не несет стену.
В случае успеха C станет воздушным, а бот будет теперь нести стену.
Бот может разместить в одной из своих соседних ячеек C, если:
- С не за пределами,
- С является воздухом,
- никакие боты ни одного из игроков не занимают C,
- и бот несет стену.
В случае успеха C станет стеной, и бот больше не будет нести стену.
Неудачные действия приводят к тому, что ничего не делается.
Ячейка, занятая, по крайней мере, одним ботом, несущим стену, имеет маленький квадрат цвета стены, нарисованный над ней. Боты запускаются без стен.
объем памяти
Во время хода игрок может получить доступ к своей памяти и изменить ее - изначально пустая строка, которая сохраняется на протяжении всей игры и может использоваться для хранения стратегических данных.
Цель
Клетка в желтом перекрестии - это цель , которая начинается в случайном положении. Каждый игрок имеет счет который начинается с 0. Когда бот игрока перемещается к цели, счет этого игрока увеличивается на 1, и цель случайным образом перемещается перед следующим ходом. Игрок с наибольшим количеством очков в конце игры побеждает. Это ничья, если очки равны.
Если несколько ботов движутся к цели во время хода, игрок все равно получает только одно очко.
Если цель была в том же месте в течение 500 ходов, она снова случайно перемещается. Каждый раз, когда цель размещается случайным образом, она гарантированно не будет помещена в ячейку, занятую ботом.
Что программировать
Напишите тело для этой функции:
function myMove(p1, id, eid, move, goal, grid, bots, ebots, getMem, setMem) {
//body goes here
}
Он будет вызываться один раз каждый раз, когда ваш игрок двигается, и ему нужно вернуть действия, которые вы хотите, чтобы каждый из ваших ботов предпринял в течение этого хода.
Вы можете использовать базовый код в качестве отправной точки.
параметры
p1
это бул, этоtrue
если вы P1 иfalse
если вы P2id
целое число, которое является идентификатором ответа вашего ответа.
- Вы можете найти идентификатор ответа, щелкнув ссылку «Поделиться» под ним и выполнив поиск номера сразу после ссылки
a/
.- Идентификатор тестовой записи -1.
eid
целое число, которое является идентификатором ответа вашего врага.move
целое число от 1 до 1000, которое говорит о том, что вы ходите.goal
это объект сx
иy
свойствами. Это координаты цели. Они даются, даже если цель вне вашего поля зрения.grid
это функция, которая принимает аргументы x и y, напримерgrid(x,y)
. Возвращает:
-1
для «неизвестно», если аргументы не являются двумя целыми числами или еслиx,y
нет в вашем поле зрения.0
для «воздуха», еслиx,y
он выходит за пределы или если клеткаx,y
находится в воздухе.1
для «стены», если клетка вx,y
является стеной.
bots
это массив ваших 8 ботов. Ее элементы являются объектами , обладающими свойствамиx
,y
иhasWall
:
x
иy
координаты бота.hasWall
этоtrue
если бот несет стену и ,false
если нет.
bots
всегда упорядочен нормально, N-й индекс соответствует номеру бота N.ebots
представляет собой массив объектовx
,y
иhasWall
свойства так же , какbots
. Только вражеские боты в вашем поле зренияebots
. Так что он будет иметь длину 0, если в вашем поле зрения нет ботов противника. Это заказано в случайном порядке.getMem
это функция без аргументов, которая возвращает вашу памятьsetMem
является функцией, которая принимает один аргумент M. Если M является строкой из 256 символов или менее, ваша память обновляется до M, в противном случае ничего не происходит.
Объект браузера console
доступен только для входа в тест.
Возвращаемое значение
Ваша функция должна вернуть массив из 8 целых чисел, каждое из которых находится в диапазоне от 0 до 24. Значение в индексе N - это действие, которое предпримет номер бота N.
Все ваши боты ничего не сделают, если ваша функция:
- Выдает ошибку любого рода. ( ошибка )
- Выполнение занимает более 20 миллисекунд . ( тайм-аут )
- Не возвращает массив из 8 целых чисел в диапазоне от 0 до 24. ( искажено )
Для удобства количество ошибок, тайм-аутов и некорректных действий отображается по окончании игры.
Каждое из чисел от 0 до 24 соответствует определенному действию бота:
- 0 - ничего не делать.
- 1-8 для перемещения.
- 9-16 для захвата.
- 17-24 для размещения.
Каждое из 8 значений для перемещения, захвата и размещения соответствует одной из соседних ячеек бота, как показано здесь:
Так, например, 15
это действие по захвату клетки под ботом.
Действия с ботами выполняются в порядке от бота 0 до бота 7. Например, если во время одного хода боту 0 говорят разместить стенку в той же самой воздушной ячейке, боту 1 было приказано перейти к, воздушная ячейка станет стеной до того, как бот Действие 1 обрабатывается, и бот 1 будет неудачным.
Неудачные действия становятся ничем и, как говорят, провалились . Счетчики неудачных действий также отображаются по окончании игры.
правила
Я могу временно или навсегда дисквалифицировать пользователей или ответы, которые не соответствуют этим правилам. Дисквалифицированные записи не имеют права на победу.
При объявлении переменных или функций вы должны использовать
var
ключевое слово.
Например,var x = 10
илиvar sum = function(a, b){ return a + b }
Вещи, объявленные без,var
становятся глобальными и могут мешать контроллеру. Были предприняты шаги, чтобы это вмешательство было невозможным, но сделайте это, чтобы убедиться.Ваш код не должен работать медленно или тратить время.
Невозможно остановить функции JavaScript в середине выполнения, поэтому код каждого игрока выполняется до конца. Если запуск вашего кода занимает много времени, все работающие с вашим плеером заметят это и будут раздражены. В идеале, записи всегда будут работать в пределах 20 мс.- Вы должны использовать код, совместимый с ECMAScript 5 в последней версии Firefox, так как именно здесь я буду его запускать. Не используйте функции из ECMAScript 6, так как он еще не поддерживается во многих браузерах.
- Вы можете ответить до 3 раз, но только если каждая из ваших стратегий значительно отличается. Вы можете редактировать ответы по своему усмотрению.
- Вы не можете пытаться иметь какой-либо вид памяти, кроме как с помощью
getMem
иsetMem
. - Вы не можете пытаться получить доступ или изменить контроллер, код другого игрока или внешние ресурсы.
- Вы не можете пытаться изменить что-либо встроенное в JavaScript.
- Ответы не должны быть детерминированными. Вы можете использовать
Math.random
.
Формат ответа
#EntryName
Notes, etc.
<!-- language: lang-js -->
//function body
//probably on multiple lines
More notes, etc.
Первый блок многострочного кода должен содержать ваше тело функции.
Имя записи ограничено 20 символами.
Ваша заявка будет отображаться в контроллере с заголовком EntryName - Username [answer ID]
, плюс, [DQ]
если она будет дисквалифицирована.
выигрыш
Когда вопрос будет решен в течение по крайней мере 3 недель и после того, как ответ будет исчерпан, я буду победителем.
Я буду использовать функцию автозапуска контроллера . В раунде автозапуска каждый недисквалифицированный игрок играет две игры друг с другом, одну как P1, одну как P2 (двойной раунд).
Я буду запускать как можно больше раундов в течение нескольких часов. Это будет зависеть от того, сколько заявок и сколько времени они требуют. Но будьте уверены, я полон решимости получить точный итоговый список лидеров. Игрок с наибольшим количеством побед становится чемпионом, и его ответ будет принят.
Я буду использовать Firefox на ноутбуке с 64-битной Windows 8.1, 4 ГБ оперативной памяти и четырехъядерным процессором 1,6 ГГц.
приз
Я напишу и опубликую вызов PPCG, специально посвященный чемпиону. Это будет как-то включать их имя пользователя или аватар или что-то о них. Я лично приму решение о том, что вызов будет, когда этот конкурс закончится. Я напишу это в меру своих возможностей и постараюсь сделать так, чтобы это стало Горячим Сетевым Вопросом.
контроллер
Запустите этот фрагмент или перейдите к этому JSFiddle, чтобы использовать контроллер. Все начинается с выбранных случайных игроков. Я только тщательно проверил это в Firefox и 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>
Этот вопрос имеет свой чат. Я буду публиковать там списки лидеров каждые несколько дней.
источник
Ответы:
Черный рыцарь
Название бота происходит от раннего плана, чтобы он мог двигаться как шахматный рыцарь: больше двух, один выше и т. Д., Что было бы быстрее в некоторых случаях.
объяснение
Определение того, какой ход сделать для каждого бота, можно разделить на две основные задачи: выяснить, куда идти и как туда добраться.
Куда идти
Основная задача выяснить, куда идти, очень проста: идти к цели, если вы ближе всего, или пытаться позиционировать себя как можно дальше от товарищей по команде. Сначала он проходит через каждого бота и определяет, находится ли он на мели (то есть он не имеет блоков, окружающих его и не держит стену, или он окружен стенами и держит стену). Затем он снова проходит через ботов, чтобы найти ближайшего к цели нетронутого бота. Все остальные боты направляются в сторону, где нижний ряд на поверхности блоков (
y=55
) и верхний ряд наy=27
. Как только он знает, куда идти, он передает егоmoveTo
функции.Как туда добраться
Решить, как добраться до места назначения, гораздо сложнее, потому что боты должны всегда находиться рядом со стеной, чтобы двигаться. Сначала он определяет код направления (1–8) пункта назначения относительно его текущей позиции. Например, если бот находится в нижнем левом углу, и он хочет перейти в верхний правый угол, он будет использовать код направления 3. Для каждого направления я жестко закодировал список движений, причем первое было идеальным, верхнее -приоритетный ход, и последнее, являющееся последним средством. Это зависит от того, есть ли у бота стена или нет, потому что вы не можете использовать перемещение места без стены или использовать захват, когда у вас уже есть стена.
Конечно, использование идеального движения не всегда работает, и это приведет к множеству неудачных действий. Это то, где
checkMove
приходит. Эта функция проверяет потенциальное движение против любого требования, например, для предотвращения выхода бота за пределы или в стену. Если ближайший вражеский бот может оказаться на мели (у него есть только одна смежная стена, которую бот может взять), это становится его приоритетом, поэтому функция вернетсяfalse
к другому легитимному ходу, чтобы можно было переходить к захватным ходам и вынимать враг. Функция предотвращает несколько других глупых движений, таких как установка стены в цель или другого бота.Строка памяти
Иногда бот не будет на самом деле застрял, но будет продолжать делать то же самое движение и не заканчивать тем, что двигался (обычно поднимая стену и опуская ее, поднимая ее и опуская, и т.д.). Чтобы предотвратить это, он использует строку памяти для запоминания последних двух ходов, последних позиций x и y и сколько раз он был неподвижен. Каждый элемент данных закодирован как один символ для легкого разделения. (Строка должна состоять из 256 символов , а не байтов, поэтому использование многобайтовых символов Юникода не является проблемой, как в типичных задачах игры в гольф.)
Например, скажем, бот захватил стену слева (код
12
) в этом ходу, заменил ее слева (код20
) в предыдущем ходу и находился в координатах (107
,3
) в прошлых16
поворотах. Строка памяти для этого экземпляра будет закодирована следующим образом:ck
: Два последних кода действий преобразуются в base36, чтобы сделать двузначные числа одной буквой.@
: Количество раз, которое оно все еще было представлено как символ ASCII с этим кодом + 48, чтобы пропустить непечатаемые символы, и поэтому первые девять раз все еще показывает фактическое число (String.fromCharCode(0 + 48)
→0
).Ħ¾
: Координаты x и y также представлены в виде символа с этим значением, на этот раз смещенным на несколько произвольное значение 187, чтобы избежать проблемных символов.Типичная строка памяти во время игры может быть
53äÇØb7¼ðÌ00ßĉÖ7m±ĪÚ00ĝÌò00Ĝìò00ĖČò00ĈĬò
с группой из пяти символов для каждого из восьми ботов.источник
Заставы
Каждый из 8 ботов берет квадрат 32 на 32 и бежит к его центру (я немного смещаю центры, иначе они заканчивают спариванием и перемещаются вертикально с одним стенным блоком между ними, так что один из них оказывается на мели).
Каждый бот будет оставаться в центре своего квадрата, если цель не находится в пределах 32 клеток от его соответствующего центра, в этом случае он добежит до цели, а затем вернется в свой центр.
При этом по-прежнему используется базовый метод достижения цели (цель или центр), поэтому он не перемещается по диагонали. Просто отправная точка ...
источник
базисный
Это самый простой и стабильно работающий контроллер бота, который я когда-либо мог придумать. Это будет мой единственный недисквалифицированный ответ, который послужит отправной точкой для оценки других ответов. Технически он побеждает, чтобы выиграть конкурс, но победить его не должно быть сложно.
Любой код здесь может быть скопирован и использован в другом ответе, без указания авторства.
Каждый из 8 ботов независимо использует один и тот же базовый метод. Из-за этого они, как правило, собираются вместе, если только они не отделены чем-то внешним. Боты никогда не заботятся о том, где находятся товарищи по команде или враги, они только пытаются продвинуться к цели. Они двигаются только ортогонально, сначала сопоставляя их х с целью х, а затем их у. Отсутствие движения по диагонали означает, что они тратят много времени на путешествия.
Алгоритм движения каждого бота выглядит следующим образом:
источник
Командный игрок
На данный момент это представление далеко от совершенства. У него такая же стратегия, как у Outposts, но только 6 ботов «в воздухе». Другие 2 бота снабжают их стенами, если их украли. Редактировать: боты-сторонники теперь работают намного лучше.
источник
Ищут
Stll работа в процессе. У меня много идей, но почти ни одна из них не работает.
Прежде всего, большая проблема с неудачными действиями.Решено!источник