Битва за блюдо Петрі


32

У цьому виклику ви повинні створити види одноклітинних організмів для боротьби зі смертю на арені «Петрі». Арена представлена ​​у вигляді прямокутної сітки, де кожна комірка займає один простір:

.....x....
...x...o..
...x.c..o.
.......o..

Атрибути

Кожна клітина має три атрибути. Вказуючи свій вид клітин на початку гри, ви виділяєте 12 балів серед цих атрибутів.

  • Хіт-бали (HP): Якщо HP клітинки падає до нуля, вона гине. Нові клітини мають повний HP.
    • Коли клітина гине, вона залишає після себе труп, який можуть з'їдати інші клітини для отримання енергії.
    • Клітина не може відновити втрачену HP, але вона може створити нову клітинку з повним HP шляхом поділу.
  • Енергія : Більшість дій, які може здійснити клітина, вимагають енергії. Активно відпочиваючи, клітина може повернути втрачену енергію до максимуму її видів.
    • Клітинний вид з меншою енергією менше 5, ймовірно, вийде з ладу, тому що він не може поділитися для створення нових клітин.
    • Клітина не може відновити енергію, що перевищує максимальну величину свого виду.
    • Новостворена клітина має початкову енергетичну цінність, скопійовану з батьківського (та максимальне значення, продиктоване специфікацією її видів).
  • Кислотність : Якщо клітина вирішила вибухнути, рівень кислотності клітини використовується для обчислення шкоди сусідніх клітин.

Дії

Кожен поворот, кожна комірка може здійснити одну дію:

  • Переміщення: осередок переміщує один простір у будь-якому напрямку (N / S / E / W / NE / NW / SE / SW) вартістю 1 енергія.

    • Клітина не може переміститися на простір, займаний іншою живою клітиною.
    • Клітина не може відійти від сітки.
    • Переміщення на клітинний труп руйнує труп.
  • Атака: Клітина атакує сусідню клітину, завдаючи від 1 до 3 ушкоджень, витрачаючи 1 - 3 енергетичні точки.

    • Клітина може атакувати в будь-якому напрямку (N / S / E / W / NE / NW / SE / SW).
    • Законно атакувати дружні осередки.
  • Поділ: комірка ділиться і створює нову комірку на сусідньому просторі, вартістю 5 енергії.

    • Клітина може ділитися в будь-якому напрямку (N / S / E / W / NE / NW / SE / SW).
    • Нова клітинка має повний HP відповідно до оригінальних специфікацій комірок.
    • Нова клітина має стільки ж енергії, скільки її материнська клітина після віднімання вартості поділу. (Наприклад, материнська клітина з початковими 8 енергетичними точками буде зменшена до 3 енергії і виробить дочірню клітинку з 3 енергією).
    • Нова комірка не може діяти до наступного кроку.
    • Клітина не може поділитися на простір, зайнятий живою клітиною, але вона може поділитися на простір, зайнятий трупом мертвої клітини (це знищує труп).
  • Їжте: клітина поїдає сусідній труп клітини, отримуючи 4 енергії.

    • Клітина може їсти в будь-якому напрямку (N / S / E / W / NE / NW / SE / SW).
  • Відпочинок: Клітина нічого не робить за один оборот, повертаючи 2 енергії.

  • Вибухнути: Коли клітина має 3 або менше HP і більше енергії, ніж HP, вона може вирішити вибухнути, завдаючи шкоди всім восьми сусіднім клітинам.

    • Пошкодження кожної сусідньої комірки є (exploding cell HP) + (explodng cell acidity)
    • Вибухає клітина гине і залишає після себе труп, як і будь-які клітини, загиблі під час вибуху.

Протокол

Налаштування

Ваша програма буде працювати з рядком, BEGINнаданим на stdin. Ваша програма повинна записати в стандартний висновок , розділених пробілами список 3 невід'ємних цілих чисел, що представляють HP, енергію і кислотність для вашого виду клітин: наприклад, 5 6 1. Цифри повинні дорівнювати 12. При бажанні може бути кислотність 0. (Інші атрибути також можуть бути нульовими, але це функціонально втрачає гру!)

Ви починаєте з однієї клітини, у північно-західному чи південно-східному куті, на відстані одного простору від будь-якого краю. Початкова клітина має повний HP і енергію.

Кожна клітина діє

Щоразу, ваша програма буде викликана один раз для кожної осередку, що живе у вашій команді (за винятком клітинок, щойно створили цю чергу), щоб клітина могла діяти. Вашій програмі надаються дані про stdin, що включає стан чашки Петрі та інформацію про цю конкретну клітинку:

10 4
..........
..xx.c....
...c...o..
......o...

6 3 5 7

Перші два числа вказують на ширину та висоту арени: тут є арена 10 на 4.

  • Ці oклітини є вашими; що xклітини ваші вороги. (Це завжди правда; кожен гравець завжди бачить власні клітини як o.)
  • У .просторах порожньо.
  • У cпросторах є їстівні трупи клітин.

Числа після порожнього рядка представляють інформацію про цю клітинку:

  • Перші два числа - це x,yкоординати, індексовані 0,0вгорі ліворуч (тому 6 3тут йдеться про південнішу oклітинку).
  • Третє число - HP клітини; четверте число - це енергія клітини.

Ваша програма повинна виводити (для викреслювання) дії. У наведених нижче прикладах ми використовуватимемо Nяк приклад, але це може бути будь-який напрямок, законний для цієї дії ( N/ S/ E/ W/ NE/ NW/ SE/ SW). Весь вихід програми нечутливий до регістру, але для прикладів буде використана велика літера. Будь-яка вихідна дія, яка є недійсною (або тому, що вона має недійсний синтаксис, або намагається незаконну дію), ігнорується та призводить до отримання клітинки REST(і, таким чином, отримує 2 енергії).

  • MOVE N
  • DIVIDE N
  • EAT N
  • ATTACK N 2 - число представляє силу атаки (1 - 3)
  • REST
  • EXPLODE

Черга вашої команди складається з того, щоб всі ваші клітини отримали можливість діяти по черзі. Усі ваші клітини діють до того, як діятиме будь-яка клітина противника. Як тільки всі ваші клітини діють, ваша черга закінчується, і починається черга вашого опонента. Як тільки всі клітини вашого опонента діють, ваша черга починається знову. У свою чергу, кожній клітці надається пріоритет, щоб діяти залежно від її віку: найстаріші клітини вашої команди діють першими перед молодшими.

Приклад

Ось як може поводитися програма. Введення від stdin позначається тут провідними >стрілками (відокремленими від фактичного вводу прояснювальним пробілом), а вихід на stdout має <стрілки.

> BEGIN
< 5 6 1

Потім програма знову викликається:

> 10 4
> ..........
> .o........
> ........x.
> ..........
>
> 1 1 5 6
< DIVIDE SE

Після черги опонента (який вирішив зробити DIVIDE Wз однієї стартової комірки) ваша програма викликається двічі, один раз для кожної комірки:

> 10 4
> ..........
> .o........
> ..o....xx.
> ..........
>
> 1 1 5 1
< MOVE E

Для другого виклику на черзі:

> 10 4
> ..........
> ..o.......
> ..o....xx.
> ..........
>
> 2 2 5 1
< MOVE SE

Зверніть увагу, що ця друга комірка бачить оновлений стан плати на основі руху іншої комірки раніше, у свою чергу. Також зауважте, що ця клітина була створена з 1 енергією, оскільки материнська клітина мала 6 енергії, коли вона виконувала ділення останнього обороту (тому початкові 6, мінус 5-енергетичні витрати на поділ, створили дочірню клітинку з 1 енергією).

Тепер ваша черга закінчилася і починається черга вашого опонента. Дві протилежні осередки отримають шанс діяти, і тоді розпочнеться ваш наступний хід.

Перемога

Ви можете виграти:

  • Знищення всіх протилежних клітин, або
  • Маючи більше клітин, ніж ваш опонент, після того, як кожен гравець виконав 150 обертів

Підрахунок балів буде залежати від кількості виграшів у 100 іграх проти подання один одного. У половині моделювання вашій програмі буде дозволено перейти першим.

Ігри з краватками (тобто рівно стільки ж комірок після 150 оборотів, або єдині осередки, що залишилися вбиті разом у результаті вибуху), не враховуються в суми виграшу жодного гравця.

Інша інформація

  • Ваша програма не повинна намагатися підтримувати стан (крім використання стану чашки Петрі): одноклітинні організми не мають дуже гарної пам’яті та реагують на світ мить за моментом. Зокрема, запис у файл (або інший сховище даних), спілкування з віддаленим сервером або встановлення змінних середовища явно заборонено.
  • Подання будуть запущені / зібрані на Ubuntu 12.04.4.
  • Специфіка 100 ігор, поки не підтверджена, але, ймовірно, передбачає кілька розмірів арени (наприклад, 50 пробіжок на малій арені та 50 пробіжок на більшій арені). На більшій арені я можу збільшити максимальну кількість поворотів, щоб забезпечити належний бій.

Ресурси

Ось код драйвера, який виконує моделювання, написаний для Node.js, що викликається node petri.js 'first program' 'second program'. Наприклад, розміщення комірки, написаної на Python, у клітинку, написану на Java, може виглядати так node petri.js 'python some_cell.py' 'java SomeCellClass'.

Крім того, я розумію, що читання та аналіз декількох рядків на stdin може бути величезним болем, тому я склав декілька повних зразків осередків на різних мовах, які ви можете побудувати, повністю переглядати або повністю ігнорувати.

Звичайно, ви вільні писати клітинку іншою мовою; це просто три мови, для яких я вирішив написати код котла як економію часу.

Якщо у вас виникли проблеми із керуванням драйвером, не соромтесь пінговати мене в кімнаті чату, яку я створив для цього виклику . Якщо у вас немає достатньої репутації для чату, просто залиште коментар.


1
@Ryan Вам потрібно буде вказати повністю запущену команду, як 'node c:/cell/cell_template.js'для кожного аргументу, так само, як вам потрібно було вказати 'java CellTemplate'код Java. Я зрозумію це в тексті виклику. Якщо у вас виникли проблеми, ми (і будь-хто інший із технічними питаннями) можемо продовжити цю дискусію в кімнаті чату, яку я щойно створив .
апспіллери

1
@Moogie Лише 2 супротивники за гру.
apsillers

3
Людина, приклади чудові!
CommonGuy

3
@apsillers ми попросили вас у чаті, але забули вас пінг, щоб ви, можливо, не помітили: нам було цікаво, коли ви плануєте запускати гру?
планнапус

2
@Manu Нарешті, так! Прошу вибачення за дуже довгу затримку. У мене налаштований код для підбору матчів / підбірки, і я зараз усуваю будь-які проблеми з поданнями в моїй спробі змусити запустити код кожного. Після цього я дозволю йому працювати на моєму сервері протягом дня або близько того, щоб завершити раунди.
apsillers

Відповіді:


3

Ось мій порівняно простий бот, який я запрограмував у Ruby. В основному, він надає пріоритет першим поділам та намаганням ділитися на ворогів, щоб отримати контроль над полем. Другий її пріоритет - їжа, а третій - атака. Це легко перемогти зразок клітини Python.

def surroundingCells(x, y)
  result = Hash.new
  if x >= 1
    if y >= 1
      # northwest
      result["NW"] = $petriDish[x - 1][y - 1]
    end
    if y < ($sizeY - 1) # $sizeY - 1 is the farthest south square
      # southwest
      result["SW"] = $petriDish[x - 1][y + 1]
    end
      # west
      result["W"] = $petriDish[x - 1][y]
  end
  if x < ($sizeX - 1)
    if y >= 1
      # northeast
      result["NE"] = $petriDish[x + 1][y - 1]
    end
    if y < ($sizeY - 1)
      # southeast
      result["SE"] = $petriDish[x + 1][y + 1]
    end
    # east
    result["E"] = $petriDish[x + 1][y]
  end
  # north
  result["N"] = $petriDish[x][y - 1] if y >= 1
  # south
  result["S"] = $petriDish[x][y + 1] if y < ($sizeY - 1)
  return result
end

def directionTowardsEnemies(locX, locY)
  totalXDirections = 0
  totalYDirections = 0
  totalTargetsFound = 0 # enemies or corpses
  optimalDirections = []
  for x in 0..($petriDish.length - 1)
    for y in 0..($petriDish[0].length - 1)
      if $petriDish[x][y] == 'c' or $petriDish[x][y] == 'x'
        totalXDirections += (x - locX)
        totalYDirections += (y - locY)
        totalTargetsFound += 1
      end
    end
  end
  if totalXDirections == 0
    if totalYDirections > 0
      optimalDirections << "S" << "SE" << "SW"
    else
      optimalDirections << "N" << "NE" << "NW"
    end
    return optimalDirections
  end
  if totalYDirections == 0
    if totalXDirections > 0
      optimalDirections << "E" << "NE" << "SE"
    else
      optimalDirections << "W" << "NW" << "SW"
    end
    return optimalDirections
  end
  if totalXDirections > 0
    if totalYDirections > 0
      optimalDirections << "SE"
      if totalYDirections > totalXDirections
        optimalDirections << "S" << "E"
      else
        optimalDirections << "E" << "S"
      end
    else
      optimalDirections << "NE"
      if -totalYDirections > totalXDirections
        optimalDirections << "N" << "E"
      else
        optimalDirections << "E" << "N"
      end
    end
    return optimalDirections
  end
  if totalXDirections < 0
    if totalYDirections > 0
      optimalDirections << "SW"
      if totalYDirections > -totalXDirections
        optimalDirections << "S" << "W"
      else
        optimalDirections << "W" << "S"
      end
    else
      optimalDirections << "NW"
      if -totalYDirections > -totalXDirections
        optimalDirections << "N" << "W"
      else
        optimalDirections << "W" << "N"
      end
    end
  end
  return optimalDirections
end

firstLine = gets
if firstLine == "BEGIN"
  puts "5 7 0"
  exit 0
end
$sizeX, $sizeY = firstLine.split(' ')[0].to_i, firstLine.split(' ')[1].to_i
$petriDish = Array.new($sizeX) { Array.new($sizeY) }
for y in 0..($sizeY - 1)
  line = gets
  chars = line.split('').reverse.drop(1).reverse # this gets every character but     the last
  for x in 0..(chars.length - 1)
    $petriDish[x][y] = chars[x]
  end
end
gets # blank line
info = gets
locX = info.split(' ')[0].to_i
locY = info.split(' ')[1].to_i
hp = info.split(' ')[2].to_i
energy = info.split(' ')[3].to_i

# dividing is our first priority
if(energy >= 5)
  # try to divide towards enemies
  dirs = directionTowardsEnemies(locX, locY)
  directions = { "N" => [0, -1], "NE" => [1, -1], "E" => [1, 0],
    "SE" => [1, 1], "S" => [0, 1], "SW" => [-1, 1],
    "W" => [-1, 0], "NW" => [-1, -1] }
  for dir in dirs
    potentialNewX = locX + directions[dir][0]
    potentialNewY = locY + directions[dir][1]
    if $petriDish[potentialNewX][potentialNewY] == '.'
      puts "DIVIDE #{dir}"
      exit 0
    end
  end
  # otherwise, just divide somewhere.
  surroundingCells(locX, locY).each do |k, v|
    if v == '.'
      puts "DIVIDE #{k}"
      exit 0
    end
  end
end

# next, eating
surroundingCells(locX, locY).each do |k, v|
  if v == 'c'
    puts "EAT #{k}"
    exit 0
  end
end

# next, attacking
surroundingCells(locX, locY).each do |k, v|
  attackStrength = 0
  if (energy > 5) then # we want to save energy for dividing
    attackStrength = [(energy - 5), 3].min
  else
    attackStrength = [energy, 3].min
  end
  if v == 'x'
    puts "ATTACK #{k} #{attackStrength}"
    exit 0
  end
end

# otherwise, rest
puts "REST"

Я не програміст Ruby, тому мені цікаво, чому деякі змінні є нормальними, а деякі починаються з a $.
seequ

$використовується для позначення глобальної змінної. Так, вони злі, але в цій невеликій програмі це не має великого значення.
Олексій

Глобальні змінні є злі лише у виробничому коді. Хто їх пам’ятає у таких сценаріях?
seequ

Чи справді є моя клітина, чия здатність поширюватись не на 4-8-0?
Алекс

Це найкращий претендент на мої CoordinatedBacteria поки що! Я будував свою стратегію на основі результатів тесту на вашому одноклітинному організмі. =)
justhalf

3

Амеба

Спочатку розбивається на чотири, а потім намагається дістатися до середини, щоб обмежити простір реплікації опонентів. Потім починає реплікацію. Під час переміщення чи реплікації знаходить оптимальний шлях до найближчого ворога і рухається або ділиться на нього, щоб спробувати відрізати наявний простір противника.

Якщо противник знаходиться поруч або знаходиться на одному просторі, він завжди буде атакувати або рухатися до нього, дозволяючи ряду позаду нічого не робити, щоб заповнити порожні пробіли.

Я не перевіряв цього на будь-якому іншому матеріалі, тому не маю уявлення, наскільки це буде добре.

var MAX_HP = 2;
var MAX_ENERGY = 10;
var ACIDITY = 0;

function PathfindingNode(_x, _y, _prevNode, _distance, _adjacentEnemies) {
    this.x = _x;
    this.y = _y;
    this.prevNode = _prevNode;
    this.distance = _distance;
    this.adjacentEnemies = _adjacentEnemies;
}

PathfindingNode.prototype.GetDistance = function()
{
    return this.distance;
}

var evaluatedNodes = {};
var initialNode = {};
var firstEval = true;

function evaluateNode(x, y, arena)
{
    //get surrounding reachable nodes that havent already been checked
    var adjacentEmpties = arena.getAdjacentMatches(arena.get(x, y), [".", "c"]);

    //if this node is adjacent to the start node - special case because the start node isnt an empty
    if (firstEval)
        adjacentEmpties.push({ 'x': initialNode.x, 'y': initialNode.y });

    //find the optimal node to reach this one
    var prevNode = null;
    for (var i in adjacentEmpties)
    {
        if(evaluatedNodes[adjacentEmpties[i].x + "," + adjacentEmpties[i].y])
        {
            var currentNode = evaluatedNodes[adjacentEmpties[i].x + "," + adjacentEmpties[i].y];

            if (!prevNode) {
                prevNode = currentNode;
            }
            else {
                if(currentNode.GetDistance() < prevNode.GetDistance())
                {
                    prevNode = currentNode;
                }
            }
        }
    }

    var adjacentEnemies = arena.getAdjacentMatches(arena.get(x, y), ["x"]);
    var newNode = new PathfindingNode(x, y, prevNode, prevNode.GetDistance() + 1, adjacentEnemies.length);
    evaluatedNodes[x + "," + y] = newNode;
}

function evaluateNeighbours(arena) {
    //breadth first search all reachable cells
    var nodesToEvaluate = [];
    for (var i in evaluatedNodes) {
        var emptyNodes = arena.getAdjacentMatches(arena.get(evaluatedNodes[i].x, evaluatedNodes[i].y), [".", "c"]);
        //only ones that havent already been eval'd
        for (var j in emptyNodes)
            if (!evaluatedNodes[emptyNodes[j].x + "," + emptyNodes[j].y])
                nodesToEvaluate.push(emptyNodes[j])
    }

    //have all available nodes been evaluated
    if (nodesToEvaluate.length === 0)
        return false;

    for (var i in nodesToEvaluate)
    {
        evaluateNode(parseInt(nodesToEvaluate[i].x), parseInt(nodesToEvaluate[i].y), arena)
    }

    firstEval = false;
    return true;
}

function getAllReachableNodes(arena, cell) {
    //return a list of all reachable cells, with distance and optimal path
    evaluatedNodes = {};

    //add the first node to get started
    var adjacentEnemies = arena.getAdjacentMatches(arena.get(cell.x, cell.y), ["x"]);
    var newNode = new PathfindingNode(parseInt(cell.x), parseInt(cell.y), null, 0, adjacentEnemies.length);
    evaluatedNodes[cell.x + "," + cell.y] = newNode;
    initialNode.x = parseInt(cell.x);
    initialNode.y = parseInt(cell.y);
    firstEval = true;

    while (evaluateNeighbours(arena))
        ;

    return evaluatedNodes;
}

function passedMiddleGround(arena)
{
    for (var i = (parseInt(arena.width) / 2) - 1; i < parseInt(arena.width); i++)
    {
        for(var j = 0; j < parseInt(arena.height); j++)
        {
            if (arena.get(i, j).symbol == "o")
                return true;
        }
    }
    return false;
}

function decide(arena, cell, outputCallback) {

    var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]);
    var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]);
    var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]);

    if (nearbyEnemies.length > 4 && cell.energy >= cell.hp && cell.hp <= 3) {
        outputCallback("EXPLODE");
        return;
    }

    //attack whenever we get the chance. leave the replication to the cells doing nothing
    if (cell.energy > 0 && nearbyEnemies.length > 0){
        outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length * Math.random()) | 0]) + " " + Math.min(cell.energy, 3));
        return;
    }

    //if we are close to an enemy, move towards it. let the back line fill the new space
    if (cell.energy > 2) {
        for (var i = 0; i < nearbyEmpties.length; ++i) {
            var space = nearbyEmpties[i];
            if (arena.getAdjacentMatches(space, ["x"]).length) {
                outputCallback("MOVE " + arena.getDirection(cell, space));
                return;
            }
        }
    }

    //yum
    if (nearbyCorpses.length > 0) {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length * Math.random()) | 0]));
        return;
    }

    //until we pass the middle ground, just keep moving into tactical position. afterwards we can start replication
    if (passedMiddleGround(arena) && cell.energy < 5 && nearbyEmpties.length > 0)
    {
        outputCallback("REST");
        return;
    }

    //try to block the opponent cells - interrupt their replication
    //if we have enough energy to move, choose the best spot
    if (nearbyEmpties.length > 0 && ((cell.energy >= 2 && nearbyEnemies.length == 0) || cell.energy >= 5)) {

        var nextMove = null;

        if (nearbyEmpties.length === 1) {
            nextMove = nearbyEmpties[0];
        }
        else {
            var reachableNodes = getAllReachableNodes(arena, cell);

            //select nodes that have an adjacent enemy
            var enemyAdjacentNodes = {};
            var enemyNodesReachable = false;
            for (var node in reachableNodes) {
                if (reachableNodes.hasOwnProperty(node) && reachableNodes[node].adjacentEnemies > 0) {
                    enemyAdjacentNodes[node] = reachableNodes[node];
                    enemyNodesReachable = true;
                }
            }

            if (enemyNodesReachable)
            {
                //if there are any then select the closest one
                var closest = null;
                for (var node in enemyAdjacentNodes) {
                    if(!closest)
                    {
                        closest = enemyAdjacentNodes[node];
                    }
                    else{
                        if(enemyAdjacentNodes[node].GetDistance() < closest.GetDistance())
                        {
                            closest = enemyAdjacentNodes[node];
                        }
                    }

                }

                //select the first move of the nodes path
                //trace the selected node back to the first node to select the first move towards the cell.
                while (closest.prevNode != null && closest.prevNode.prevNode != null)
                {
                    closest = closest.prevNode;
                }
                nextMove = arena.get(closest.x, closest.y);
            }
        }

        //a path to the enemy was found
        if(nextMove)
        {
            //do this until we get half way across the board, then we just replicate
            if (!passedMiddleGround(arena)) {
                if (cell.energy >= 5) {
                    outputCallback("DIVIDE " + arena.getDirection(cell, nextMove));
                    return;
                }

                outputCallback("MOVE " + arena.getDirection(cell, nextMove));
                return;
            }
            else {
                outputCallback("DIVIDE " + arena.getDirection(cell, nextMove));
                return;
            }

        }

    }

    //if theres no path to an enemy available, just divide anywhere
    if (cell.energy >= 5 && nearbyEmpties.length > 0) {
        outputCallback("DIVIDE " + arena.getDirection(cell, nearbyEmpties[(nearbyEmpties.length * Math.random()) | 0]));
        return;
    }

    outputCallback("REST");
    return;
}

var input = "";
// quiet stdin EPIPE errors
process.stdin.on("error", function(err) {
    log("slight error: " + err);
});
process.stdin.on("data", function(data) {
    input += data;
});
process.stdin.on("end", function() {
    if(input == "BEGIN") {
        // output space-separated attributes
        process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" "));
        clearLog();
    } else {
        // read in arena and decide on an action
        var arena = new Arena();
        var lines = input.split("\n");
        var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); });
        arena.width = dimensions[0];
        arena.height = dimensions[1];
        for(var y=1; y<=dimensions[1]; ++y) {
            for(var x=0; x<lines[y].length; ++x) {
                arena.set(x, y-1, lines[y][x]);
            }
        }

        var stats = lines[dimensions[1]+2].split(" ");
        var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) };

        // decide on an action and write the action to stdout
        decide(arena, cell, function(output) { process.stdout.write(output); })
    }
});

var Arena = function() {
    this.dict = {};
};
Arena.prototype = {
    // get Point object
    get: function(x,y) {
        if(!this.dict[x+","+y])
            return 'w';
        return this.dict[x+","+y];
    },

    // store Point object
    set: function(x,y,d) {
        this.dict[x+","+y] = new Point(x,y,d);
    },

    // get an array of all Points adjacent to this one whose symbol is contained in matchList
    // if matchList is omitted, return all Points
    getAdjacentMatches: function(point, matchList) {
        var result = [];
        for(var i=-1; i<=1; ++i) {
            for(var j=-1; j<=1; ++j) {
                var inspectedPoint = this.get(point.x+i, point.y+j);
                if(inspectedPoint && 
                   (i!=0 || j!=0) &&
                   (!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) {
                    result.push(inspectedPoint);
                }
            }
        }
        return result;
    },

    // return the direction from point1 to point2
    getDirection: function(point1, point2) {
        var dx = point2.x - point1.x;
        var dy = point2.y - point1.y;
        dx = Math.abs(dx) / (dx || 1);
        dy = Math.abs(dy) / (dy || 1);

        c2d = { "0,0":"-",
                "0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W",
                "-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" };

        return c2d[dx + "," + dy];
    }
}

var Point = function(x,y,d) {
    this.x = x;
    this.y = y;
    this.symbol = d;
}
Point.prototype.toString = function() {
    return "(" + this.x + ", " + this.y + ")";
}

Це насправді досить хороша стратегія, якщо тільки ви зменшите ризик, враховуючи кількість сусідніх друзів перед тим, як рухатися, інакше інші рушники можуть легко пробити вашу тонку лінію захисту в ранній грі (і, отже, застосовні лише у невеликих дошках)
justhalf

До речі, це, здається, не працює так, як задумано, якщо це гравець 2.
justhalf

3

Простий осередок, виконаний в node.js . Випробуваний він отримує приклади клітини вузла і проти Костронора він їх б'є.

Оновлення

Все ще досить просто, спробуйте рухатися до ворога або розділитись.

// used in defining cell spec
var MAX_HP = 4;
var MAX_ENERGY = 8;
var ACIDITY = 0;

function decide(arena, cell, outputCallback) {

    var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]);
    var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]);
    var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]);
    var nearbyFriends = arena.getAdjacentMatches(cell.point, ["o"]);

    if (nearbyFriends.length >= 8) {
        outputCallback("REST");
        return;
    }

    if (nearbyFriends.length >= 7 && nearbyEnemies.length < 0 && nearbyCorpses.length > 0 && energy < MAX_ENERGY) {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0]));
        return;
    }

    // if you have two or more nearby enemies, explode if possible
    if(nearbyEnemies.length >= 1
        && cell.energy >= cell.hp 
        && cell.hp <= 1 
        && nearbyEnemies.length > nearbyFriends.length) {
        outputCallback("EXPLODE");
        return;
    }

    // if you have two or more nearby enemies, explode if possible
    if(nearbyEnemies.length >= 3 && cell.energy >= cell.hp && nearbyEnemies.length > nearbyFriends.length) {
        outputCallback("EXPLODE");
        return;
    }

    // if you have the energy and space to divide, do it
    if(cell.energy >= 5 && nearbyEmpties.length > 0) {
        var ed = arena.getEnemyDirection(cell);
        if (nearbyEmpties.indexOf(ed) >= 0 && Math.random() < 0.5){
            outputCallback("DIVIDE " + ed);
        } else{
            outputCallback("DIVIDE " + arena.getDirection(cell, nearbyEmpties[(nearbyEmpties.length*Math.random())|0]));
        }
        return;
    }

    // if at least one adjacent enemy, attack if possible
    if(cell.energy > 0 && nearbyEnemies.length > 0) {
        outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length*Math.random())|0]) + " " + Math.min(cell.energy, 3));
        return;
    }

    if (Math.random() < 0.5) {
        for(var i=0; i<nearbyEmpties.length; ++i) {
            outputCallback("MOVE " + arena.getEnemyDirection(cell));
            return;
        }
    } 

    if (nearbyEmpties.length > 0 && nearbyEnemies.length <= 6) {
        outputCallback("REST"); // because next turn is divide time
        return;
    }

    // if there's a nearby corpse, eat it if your energy is below max
    if(nearbyCorpses.length > 0) {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0]));
        return;
    }

    outputCallback("REST");
    return;
}

var input = "";
// quiet stdin EPIPE errors
process.stdin.on("error", function(err) {
    console.log("slight error: " + err);
});
process.stdin.on("data", function(data) {
    input += data;
});
process.stdin.on("end", function() {
    if(input == "BEGIN") {
        // output space-separated attributes
        process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" "));
    } else {
        // read in arena and decide on an action
        var arena = new Arena();
        var lines = input.split("\n");
        var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); });
        arena.width = dimensions[0];
        arena.height = dimensions[1];
        for(var y=1; y<=dimensions[1]; ++y) {
            for(var x=0; x<lines[y].length; ++x) {
                arena.set(x, y-1, lines[y][x]);
            }
        }

        var stats = lines[dimensions[1]+2].split(" ");
        var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) };

        // decide on an action and write the action to stdout
        decide(arena, cell, function(output) { process.stdout.write(output); })
    }
});

var Arena = function() {
    this.dict = {};
};
Arena.prototype = {
    // get Point object
    get: function(x,y) {
        return this.dict[x+","+y];
    },

    // store Point object
    set: function(x,y,d) {
        this.dict[x+","+y] = new Point(x,y,d);
    },

    // get an array of all Points adjacent to this one whose symbol is contained in matchList
    // if matchList is omitted, return all Points
    getAdjacentMatches: function(point, matchList) {
        var result = [];
        for(var i=-1; i<=1; ++i) {
            for(var j=-1; j<=1; ++j) {
                var inspectedPoint = this.get(point.x+i, point.y+j);
                if(inspectedPoint && 
                   (i!=0 || j!=0) &&
                   (!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) {
                    result.push(inspectedPoint);
                }
            }
        }
        return result;
    },

    // return the direction from point1 to point2
    getDirection: function(point1, point2) {
        var dx = point2.x - point1.x;
        var dy = point2.y - point1.y;
        dx = Math.abs(dx) / (dx || 1);
        dy = Math.abs(dy) / (dy || 1);

        c2d = { "0,0":"-",
                "0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W",
                "-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" };

        return c2d[dx + "," + dy];
    },

    getEnemyDirection: function(p) {
        for (var i = 0; i < this.width; i++) {
            for (var j = 0; j < this.height; j++) {
                var found = this.get(i,j);
                if (found != null && found.symbol == "x") {
                    return this.getDirection(p, found);
                }
            }
        }
        return "N"; //should never happen
    }
}

var Point = function(x,y,d) {
    this.x = x;
    this.y = y;
    this.symbol = d;
}
Point.prototype.toString = function() {
    return "(" + this.x + ", " + this.y + ")";
}

justhalf виявив декілька серйозних помилок у драйверній програмі (MOVE був безцінним і EXPLODE не враховував кислотність). Якщо ви зацікавлені в повторному протестуванні оновленого коду драйвера та оновлення подання, будь ласка, повідомте мене про це. Якщо ні, то це теж чудово.
апспіллери

2

Еволюція

Це уявлення еволюціонувало і вже не є простим організмом синглетних клітин! Він намагається атакувати / вибухати, коли це можливо, в іншому випадку він розділяє або рухається в бік ворога. Переміщення повинно вирішити проблему клітини, оточеної дружніми клітинами з максимальною енергією, нездатними зробити щось корисне.
Після переміщення завжди залишається 3 енергії, щоб якомога сильніше вразити противника.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

public class Evolution {
    public static final int MAX_HP = 4;
    public static final int MAX_ENERGY = 8;
    public static final int ACIDITY = 0;

    // given arena state and cell stats, return an action string (e.g., "ATTACK NW 2", "DIVIDE S")
    public static String decide(Arena arena, Point cell, int hp, int energy) {
        ArrayList<Point> nearbyEmpty = arena.getAdjacentMatches(cell, ".");
        ArrayList<Point> nearbyEnemies = arena.getAdjacentMatches(cell, "x");
        ArrayList<Point> nearbyCorpses = arena.getAdjacentMatches(cell, "c");
        ArrayList<Point> nearbyFriends = arena.getAdjacentMatches(cell, "o");

        // more than 1 enemy around => explode if possible and worth it
        if(nearbyEnemies.size() > 1 && energy > hp && hp <= 3 && nearbyEnemies.size() > nearbyFriends.size()) {
            return "EXPLODE";
        }

        // enemies around => always attack with max strength
        if(energy > 0 && nearbyEnemies.size() > 0) {
            int attackStrength = Math.min(energy, 3);
            Point enemy = nearbyEnemies.get(0);
            return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
        }       

        // safe spot => divide if possible
        if(energy >= 5 && nearbyEmpty.size() > 0) {
            Point randomEmpty = nearbyEmpty.get((int)Math.floor(nearbyEmpty.size()*Math.random()));
            return "DIVIDE " + arena.getDirection(cell, randomEmpty);
        }

        // nearby corpse and missing energy => eat
        if(nearbyCorpses.size() > 0 && energy < MAX_ENERGY) {
            Point corpse = nearbyCorpses.get(0);
            return "EAT " + arena.getDirection(cell, corpse);
        }

        // move towards enemy => constant flow of attacks
        if(energy == 4) {
            return "MOVE " + arena.getEnemyDirection(cell);
        }

        return "REST";
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br =
            new BufferedReader(new InputStreamReader(System.in));

        String firstLine;

        firstLine = br.readLine();
        if(firstLine.equals("BEGIN")) {
            System.out.println(MAX_HP + " " + MAX_ENERGY + " " + ACIDITY);
        } else {
            String[] dimensions = firstLine.split(" ");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            Point[][] arena = new Point[height][];
            String input;
            int lineno = 0;

            while(!(input=br.readLine()).equals("")) {
                String[] charList = input.substring(1).split("");
                arena[lineno] = new Point[width];
                for(int i=0; i<charList.length; ++i) {
                    arena[lineno][i] = new Point(i, lineno, charList[i]);
                }
                lineno++;
            }

            String[] stats = br.readLine().split(" ");
            int x = Integer.parseInt(stats[0]);
            int y = Integer.parseInt(stats[1]);
            int hp = Integer.parseInt(stats[2]);
            int energy = Integer.parseInt(stats[3]);

            Arena arenaObj = new Arena(arena, width, height);
            System.out.print(decide(arenaObj, arenaObj.get(x,y), hp, energy));
        }
    }

    public static class Arena {
        public Point[][] array;
        public HashMap<String, String> c2d;
        public int height;
        public int width;

        public Arena(Point[][] array, int width, int height) {
            this.array = array;
            this.width = width;
            this.height = height;

            this.c2d = new HashMap<String, String>();
            this.c2d.put("0,0", "-");
            this.c2d.put("0,-1", "N");
            this.c2d.put("0,1", "S");
            this.c2d.put("1,0", "E");
            this.c2d.put("-1,0", "W");
            this.c2d.put("-1,-1", "NW");
            this.c2d.put("1,-1", "NE");
            this.c2d.put("-1,1", "SW");
            this.c2d.put("1,1", "SE");
        }

        // get the character at x,y
        // or return empty string if out of bounds
        public Point get(int x, int y) {
            if(y < 0 || y >= this.array.length){
                return null;
            }

            Point[] row = this.array[y];

            if(x < 0 || x >= row.length) {
                return null;
            }

            return row[x];
        }

        // get arraylist of Points for each adjacent space that matches the target string
        public ArrayList<Point> getAdjacentMatches(Point p, String match) {
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if((i!=0 || j!=0) && found != null && found.symbol.equals(match)) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        // get the direction string from point 1 to point 2
        public String getDirection(Point p1, Point p2) {
            int dx = p2.x - p1.x;
            int dy = p2.y - p1.y;
            dx = Math.abs(dx) / (dx==0?1:dx);
            dy = Math.abs(dy) / (dy==0?1:dy);

            return this.c2d.get(dx + "," + dy);
        }

        public String getEnemyDirection(Point p) {
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    Point found = this.get(x,y);
                    if (found != null && found.symbol.equals("x")) {
                        return getDirection(p, found);
                    }
                }
            }
            return "N"; //should never happen
        }
    }

    public static class Point {
        int x, y;
        String symbol;

        public Point(int x, int y, String sym) {
            this.x=x;
            this.y=y;
            this.symbol=sym;
        }
    }
}

justhalf виявив декілька серйозних помилок у драйверній програмі (MOVE був безцінним і EXPLODE не враховував кислотність). Якщо ви зацікавлені в повторному протестуванні оновленого коду драйвера та оновлення подання, будь ласка, повідомте мене про це. Якщо ні, то це прекрасно.
апспіллери

2

Берсеркер

Оскільки я використовував Clojure, який має деякі обмеження, в основному величезний час запуску, я взяв трохи печінки. Коли програма дається, BEGINвона виводить 4 6 2 LOOP, що вказує, що вона не зупиняється. Потім він приймає входи як безперервний потік і закінчується END. Це не зберігає жодного стану, що стає зрозумілим, не використовуючи жодних глобальних змінних або використовуючи повторні значення. Оскільки реалізація цього циклу дії ще не виконана, я не зміг повністю перевірити код (сподіваюся, що код достатньо зрозумілий).

Клітина отримала свою назву від природи вибуху, коли це можливо (і тим самим маючи кислотність), і пріоритетуючи атаку відразу після поділу.

Я завантажив згенерований jar-файл у свій Dropbox . Бігайте зjava -jar petridish-clojure.jar

Просто для уточнення:

> BEGIN
< 4 6 2 LOOP
> 10 4
> ..........
> ..xx.c....
> ...c...O..
> ......o...
> 
> 3 4 6
< DIVIDE NW
> 10 4
> ..........
> ..xx.c....
> ...c.o.o..
> ......o...
>
> 5 2 4 1
< EAT N
> END
(ns petridish.core
  (:require [clojure.string :as s])
  (:gen-class))

(def ^:const maxhp     4)
(def ^:const maxenergy 6)
(def ^:const acidity   2)

(defn str->int
  [x]
  (if (empty? x)
    0
    (Integer. (re-find #"\d+" x))))

(defn sum-vectors [vec1 vec2]
  (vec (map #(vec (map + % vec2)) vec1)))

(defn find-adjacent [[width height] board pos target]
  (let [cells (sum-vectors [[-1 -1] [0 -1] [1 -1]
                            [-1  0]        [1  0]
                            [-1  1] [0  1] [1  1]]
                           pos)
        directions ["NW" "N" "NE"
                    "W"      "E"
                    "SW" "S" "SE"]]
    (for [cell cells
          :when (and (> width  (cell 0) -1)
                     (> height (cell 1) -1)
                     (= target (get-in board (reverse cell))))]
      (directions (.indexOf cells cell)))))

(defn decide [size board [x y hp energy]]
  (let [friends (find-adjacent size board [x y] \o)
        enemies (find-adjacent size board [x y] \x)
        corpses (find-adjacent size board [x y] \c)
        empty   (find-adjacent size board [x y] \.)]
    (cond
      (and (<= hp 3) (> energy hp) (seq enemies))
        "EXPLODE"
      (and (>= energy 5) (seq empty))
        (str "DIVIDE " (first empty))
      (and (>= energy 3) (seq enemies))
        (str "ATTACK " (first enemies) " " (min 3 energy))
      (and (< energy maxenergy) (seq corpses))
        (str "EAT " (first corpses))
      (or (and (<= 5 energy maxenergy) (not (seq empty))) (< energy 5))
        "REST"
      (seq empty)
        (str "MOVE " (rand-nth empty)))))

(defn read-board [[width height]]
  (let [result (vec (for [i (range height)]
                        (read-line)))]
    (read-line) ; Skip the empty line
    result))

(defn reader []
  (loop []
    (let [firstline (read-line)]
      (when (not= firstline "END")
        (println
          (if (= firstline "BEGIN")
            (str maxhp " " maxenergy " " acidity " LOOP")
            (let [size   (map str->int (s/split firstline #"\s+"))
                  board  (read-board size)
                  status (map str->int (s/split (read-line) #"\s+"))]
              (decide size board status))))
        (recur)))))

(defn -main []
  (reader))

Журнал оновлення

1. Fixed the logic a little and removed redundancies.

Приємне використання кислотності - насправді я думаю, що це єдиний бот, який взагалі використовує кислотність.
Олексій

@Alex Ми побачимо, як це виходить, але я думаю, що це повинно бути в змозі очистити амеби. Що ви думаєте про код? Я так новачок у прихильності.
seequ

У вашому прикладі, як може переміститися щойно породжена клітина? Я думав, що потрібно почекати ще одного ходу?
justhalf

@justhalf Е, клітини не знають, скільки їм років.
seequ

Так, але контролер знає, правда? Це не повинно давати поворот новоствореній клітині.
justhalf

2

Голодний, голодний бот

Ось запис у Р. Я сподіваюся, що я правильно зрозумів, які технічні характеристики щодо спілкування зі своєю програмою. Слід викликати Rscript Hungryhungrybot.R.
Якщо у нього є щонайменше 6 енергії, яку він ділить, переважно в напрямку противника. В іншому випадку він їсть все, що поруч з ним, або все, що можна досягти. Якщо жодна їжа недосяжна, то вона або вибухне, коли навколо буде більше ворогів, ніж сестринські клітини, або буде битися з ворогами, що знаходяться поблизу. Відпочиває лише в тому випадку, якщо енергії 0 і нічого їсти немає.

infile <- file("stdin")
open(infile)
input1 <- readLines(infile,1)
if(input1=="BEGIN"){
    out <- "4 7 1"
    }else{
        nr <- as.integer(strsplit(input1," ")[[1]][2])
        nc <- as.integer(strsplit(input1," ")[[1]][1])
        input2 <- readLines(infile, 2+as.integer(nr))
        arena <- do.call(rbind,strsplit(input2[1:nr],""))
        stats <- strsplit(input2[nr+2]," ")[[1]]
        coords <- as.integer(stats[2:1])+1
        hp <- as.integer(stats[3])
        nrj <- as.integer(stats[4])
        closest <- function(coords,arena,object){
            a <- which(arena==object,arr.ind=TRUE)
            if(length(a)){
                d <- apply(a,1,function(x)max(abs(x-coords)))
                b <- which.min(d)
                f <- a[b,]
                dir <- f-coords
                where <- ""
                if(dir[1]<0)where <- paste(where,"N",sep="")
                if(dir[1]>0)where <- paste(where,"S",sep="")
                if(dir[2]<0)where <- paste(where,"W",sep="")
                if(dir[2]>0)where <- paste(where,"E",sep="")
                dist <- d[b]
                }else{dist <- NA; where <- ""}
            list(dist,where)
            }
        near <- expand.grid((coords[1]-1):(coords[1]+1),(coords[2]-1):(coords[2]+1))
        near <- near[near[,1]<=nr&near[,2]<=nc,]
        adjacent <- t(matrix(apply(near,1,function(x)arena[x[1],x[2]]),nr=3,byrow=TRUE))
        w <- matrix(c('NW','N','NE','W','','E','SW','S','SE'),nr=3,byrow=TRUE)
        if(coords[1]==1) w <- w[-1,]
        if(coords[1]==nr) w <- w[-3,]
        if(coords[2]==1) w <- w[,-1]
        if(coords[2]==nc) w <- w[,-3]
        if(any(arena=="c")){food <- closest(coords,arena,"c")}else{food <- list(nrj+2,"")}
        enemies <- closest(coords,arena,"x")
        if(nrj>=6){
            empties <- w[adjacent=="."]
            if(!length(empties)){
                if(sum(adjacent=="x")>sum(adjacent=="o") & hp<=3 & nrj>=hp){
                    out <- "EXPLODE"
                    }else{out <- "REST"}
                }else if(enemies[[2]]%in%empties & enemies[[1]]!=1){
                out <- paste("DIVIDE", enemies[[2]])
                }else{
                out <- paste("DIVIDE", empties[1])
                }
            }else{
                if(nrj==0 & !any(adjacent=="c")){
                    out <- "REST"
                    }else{
                        if(any(adjacent=="c")){
                            out <- paste("EAT",w[adjacent=="c"][1])
                            }else if(any(arena=="c") & food[[1]]<=(nrj+1)){
                                    out <- paste("MOVE",food[[2]])
                            }else if(sum(adjacent=="x")>sum(adjacent=="o") & hp<=3 & nrj>=hp){
                                out <- "EXPLODE"
                            }else if(any(adjacent=="x")){
                                out <- paste("ATTACK",w[adjacent=="x"][1],max(nrj,3))
                            }else{
                                out <- paste("MOVE", enemies[[2]])
                            }
                    }
            }
        }
cat(out)
flush(stdout())

Я (нарешті) намагаюся виконати виклик, і я продовжую отримувати Error: unexpected 'else' in "else"ваш код. Я боюся, що взагалі не знаю R, тому не можу почати вирішувати цю помилку. Для довідки, я отримую цю помилку як при запуску її в драйвері, так і при просто запуску програми та введенні вручну BEGIN.
апспіллери

@apsillers arf Я додав новий рядок, де я не повинен мати: він повинен працювати зараз.
планнапус

Це виправило цю помилку, щоб ми могли пройти через клітинку init; тепер я отримую ще одну, коли гра починається насправді:Error in if (dir[1] < 0) where <- paste(where, "N", sep = "") : missing value where TRUE/FALSE needed
apsillers

Тепер перша черга працює нормально, але наступні повороти приносять Error: object 'food' not foundкористь (коли стикаються з поданням Алекса Рубі, можливо, інші)
apsillers

Тепер у вас клітина працює чудово, дякую! :) Однак, justhalf виявив декілька серйозних помилок у драйверній програмі (MOVE був безцінним і EXPLODE не враховував кислотність). Якщо ви зацікавлені в повторному протестуванні оновленого коду драйвера та оновлення подання, будь ласка, повідомте мене про це. Якщо ні, то це прекрасно.
апспіллери

2

Координовані бактерії

Я сподіваюся, що я не спізнився.

На моїх тестах перемога проти інших супротивників (і завжди, вбиваючи їх усіх), і битва ніколи не закінчиться, якщо вона зіткнеться сама, що свідчить про сильну стратегію.

Коли ви одноклітинні, ви можете запам'ятати попередній стан, але ви можете використовувати свою власну позицію, щоб поводитись інакше! =)

Це розділить бактерії на дільник і рушій, і, таким чином, збережеться більше корисних бактерій, а не лише передня лінія, зберігаючи оборонну лінію.

Він також координує свої атаки, щоб зосередитись на конкретному ворогу, щоб вороги швидше гинули (це зіткнулося з моєю іншою одноклітиною, яка зосереджена на HP).

У середині гри, яка виявляється за кількістю осередків на дошці, вони намагатимуться залізти на ворожу територію, перегравши їх. Це ключова стратегія виграшу.

Це найвищий темп зростання порівняно з усіма іншими супротивниками в даний час, але він має повільний старт, тому це працює краще на великих аренах.

Запустіть його java CoordinatedBacteria

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

public class CoordinatedBacteria {
    public static final int MAX_HP = 6;
    public static final int MAX_ENERGY = 6;
    public static final int ACIDITY = 0;

    // given arena state and cell stats, return an action string (e.g., "ATTACK NW 2", "DIVIDE S")
    public static String decide(final Arena arena, Point cell, int hp, int energy) {
        // empty and corpses are free for movement and division
        final Point2D enemyCenter = arena.getCenterOf("x");
        final Point2D ourCenter = arena.getCenterOf("o");
        final int moverPos = (enemyCenter.x <= ourCenter.x || enemyCenter.y <= ourCenter.y) ? (arena.width+arena.height+1)%2 : 1;
        final int attackPos = (enemyCenter.x <= ourCenter.x || enemyCenter.y <= ourCenter.y) ? (arena.width+arena.height)%2 : 1;

        int selfCount = arena.count("o");
        boolean isMidWay = selfCount > (arena.width*arena.height/2-1);

        if(!isMidWay){
            if(enemyCenter.x < ourCenter.x){
                enemyCenter.x = 0;
                enemyCenter.y = 0;
                ourCenter.x = arena.width;
                ourCenter.y = arena.height;
            } else {
                enemyCenter.x = arena.width;
                enemyCenter.y = arena.height;
                ourCenter.x = 0;
                ourCenter.y = 0;
            }
        }
        ArrayList<Point> nearbyEmpty = arena.getAdjacentMatches(cell, ".");
        Collections.sort(nearbyEmpty, new Comparator<Point>(){
            @Override
            public int compare(Point o1, Point o2) {
                Double score1 = arena.getAdjacentMatches(o1, ".").size()
                        + arena.getAdjacentMatches(o1, "c").size()
                        + arena.getAdjacentMatches(o1, "x").size()
                        - arena.getAdjacentMatches(o1, "o").size()
                        + distance(o1.x, o1.y, enemyCenter.x, enemyCenter.y)*100;
                Double score2 = arena.getAdjacentMatches(o2, ".").size()
                        + arena.getAdjacentMatches(o2, "c").size()
                        + arena.getAdjacentMatches(o2, "x").size()
                        - arena.getAdjacentMatches(o2, "o").size()
                        + distance(o1.x, o1.y, enemyCenter.x, enemyCenter.y)*100;
                return Double.compare(score2, score1);
            }
        });
        ArrayList<Point> nearbyEnemies = arena.getAdjacentMatches(cell, "x");
        Collections.sort(nearbyEnemies, new Comparator<Point>(){
            @Override
            public int compare(Point o1, Point o2) {
                Integer score1 = (arena.getAdjacentMatches(o1, ".").size()
                        + arena.getAdjacentMatches(o1, "c").size()
                        - arena.getAdjacentMatches(o1, "x").size()
                        + arena.getAdjacentMatches(o1, "o").size())
                        *10
                        + (isAtBoundary(o1, arena)?1000:0)
                        + (o1.x + o1.y + attackPos + 1)%2;
                Integer score2 = (arena.getAdjacentMatches(o2, ".").size()
                        + arena.getAdjacentMatches(o2, "c").size()
                        - arena.getAdjacentMatches(o2, "x").size()
                        + arena.getAdjacentMatches(o2, "o").size())
                        *10
                        + (isAtBoundary(o2, arena)?1000:0)
                        + (o2.x + o2.y + attackPos + 1)%2;
                return Integer.compare(score2, score1);
            }
        });
        ArrayList<Point> nearbyCorpses = arena.getAdjacentMatches(cell, "c");
        Collections.sort(nearbyCorpses, new Comparator<Point>(){
            @Override
            public int compare(Point o1, Point o2) {
                Integer score1 = arena.getAdjacentMatches(o1, "x").size()
                        - arena.getAdjacentMatches(o1, "o").size();
                Integer score2 = arena.getAdjacentMatches(o2, "x").size()
                        - arena.getAdjacentMatches(o2, "o").size();
                return Integer.compare(score1, score2);
            }
        });
        ArrayList<Point> nearbyFriends = arena.getAdjacentMatches(cell, "o");

        for(Point empty: nearbyEmpty){
            if(nearbyFriends.size()>=2 && energy >= 1 && arena.getAdjacentMatches(empty, "x").size()==3 && isAtBoundary(empty, arena)){
                return "MOVE "+arena.getDirection(cell, empty);
            }
        }

        for(Point empty: nearbyCorpses){
            if(nearbyFriends.size()>=2 && energy >= 1 && arena.getAdjacentMatches(empty, "x").size()==3 && isAtBoundary(empty, arena)){
                return "MOVE "+arena.getDirection(cell, empty);
            }
        }

        if ((cell.x+cell.y)%2 == moverPos && energy >= 1 && energy <= 5){
            if(nearbyEmpty.size()>0){
                Point foremost = nearbyEmpty.get(0);
                if(nearbyFriends.size() >= 4){
                    return "MOVE "+arena.getDirection(cell, foremost);
                }
            }
            if(nearbyCorpses.size() > 0) {
                Point corpse = nearbyCorpses.get(0);
                return "EAT " + arena.getDirection(cell, corpse);
            }

            if(energy > 0 && nearbyEnemies.size() > 0) {
                int attackStrength = Math.min(energy, 3);
                Point enemy = nearbyEnemies.get(0);
                return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
            }

            if(nearbyFriends.size() >= 4 && nearbyEmpty.size() > 0){
                Point movePoint = getBestPointToDivide(arena, nearbyEmpty);
                return "MOVE " + arena.getDirection(cell, movePoint);
            }
        }

        if(energy >= 5 && nearbyEmpty.size() > 0) {
            Point divisionPoint = getBestPointToDivide(arena, nearbyEmpty);
            if(energy == MAX_ENERGY && nearbyFriends.size() >= 5
                    && distance(enemyCenter.x, enemyCenter.y, cell.x, cell.y) > distance(enemyCenter.x, enemyCenter.y, divisionPoint.x, divisionPoint.y)){
                return "MOVE " + arena.getDirection(cell, divisionPoint);
            }
            return "DIVIDE " + arena.getDirection(cell, divisionPoint);
        }

        if(nearbyCorpses.size() > 0) {
            Point corpse = nearbyCorpses.get(0);
            if (energy < MAX_ENERGY){
                return "EAT " + arena.getDirection(cell, corpse);
            } else {
                return "DIVIDE " + arena.getDirection(cell, corpse);
            }
        }

        if(energy >= 5 && nearbyCorpses.size() > 0) {
            Point divisionPoint = getBestPointToDivide(arena, nearbyCorpses);
            if(energy == MAX_ENERGY && nearbyFriends.size() >= 5
                    && distance(enemyCenter.x, enemyCenter.y, cell.x, cell.y) < distance(enemyCenter.x, enemyCenter.y, divisionPoint.x, divisionPoint.y)){
                return "MOVE " + arena.getDirection(cell, divisionPoint);
            }
            return "DIVIDE " + arena.getDirection(cell, divisionPoint);
        }

        // if at least one adjacent enemy, attack if possible
        if(energy > 0 && nearbyEnemies.size() > 0) {
            int attackStrength = Math.min(energy, 3);
            Point enemy = nearbyEnemies.get(0);
            return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
        }

        return "REST";

    }

    public static boolean isAtBoundary(Point point, Arena arena){
        return point.x==0 || point.x==arena.width-1 || point.y==0 || point.y==arena.height-1;
    }

    public static double distance(double x1, double y1, double x2, double y2){
        return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
    }

    public static Point getBestPointToDivide(Arena arena, List<Point> nearbyEmpty){
        Point result = null;
        double minDist = 100000;
        List<Point> mostEmpty = new ArrayList<Point>();
        int max = -1000;
        List<Point> neighbor = nearbyEmpty;
        for(Point point: neighbor){
            int emptyNeighborScore = arena.getAdjacentMatches(point, ".").size()
                    + arena.getAdjacentMatches(point, "c").size()
                    + arena.getAdjacentMatches(point, "x").size()
                    - arena.getAdjacentMatches(point, "o").size();
            if(emptyNeighborScore > max){
                mostEmpty = new ArrayList<Point>();
                mostEmpty.add(point);
                max = emptyNeighborScore;
            } else if(emptyNeighborScore == max){
                mostEmpty.add(point);
            }
        }
        for(Point point: mostEmpty){
            Point2D enemyCenter = arena.getCenterOf("x");
            double dist = Math.pow(point.x-enemyCenter.x, 2) + Math.pow(point.y-enemyCenter.y, 2);
            if(dist < minDist){
                minDist = dist;
                result = point;
            }
        }
        return result;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br =
                new BufferedReader(new InputStreamReader(System.in));

        String firstLine;

        firstLine = br.readLine();
        if(firstLine.equals("BEGIN")) {
            System.out.println(MAX_HP + " " + MAX_ENERGY + " " + ACIDITY);
        } else {
            String[] dimensions = firstLine.split(" ");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            Point[][] arena = new Point[height][];
            String input;
            int lineno = 0;

            while(!(input=br.readLine()).equals("")) {
                char[] charList = input.toCharArray();
                arena[lineno] = new Point[width];
                for(int i=0; i<charList.length; ++i) {
                    arena[lineno][i] = new Point(i, lineno, charList[i]);
                }
                lineno++;
            }

            String[] stats = br.readLine().split(" ");
            int x = Integer.parseInt(stats[0]);
            int y = Integer.parseInt(stats[1]);
            int hp = Integer.parseInt(stats[2]);
            int energy = Integer.parseInt(stats[3]);

            Arena arenaObj = new Arena(arena, width, height);
            System.out.print(decide(arenaObj, arenaObj.get(x,y), hp, energy));
        }
    }

    public static class Arena {
        public Point[][] array;
        public HashMap<String, String> c2d;
        public int height;
        public int width;

        public Arena(Point[][] array, int width, int height) {
            this.array = array;
            this.width = width;
            this.height = height;


            this.c2d = new HashMap<String, String>();
            this.c2d.put("0,0", "-");
            this.c2d.put("0,-1", "N");
            this.c2d.put("0,1", "S");
            this.c2d.put("1,0", "E");
            this.c2d.put("-1,0", "W");
            this.c2d.put("-1,-1", "NW");
            this.c2d.put("1,-1", "NE");
            this.c2d.put("-1,1", "SW");
            this.c2d.put("1,1", "SE");
        }

        // get the character at x,y
        // or return empty string if out of bounds
        public Point get(int x, int y) {
            if(y < 0 || y >= this.array.length){
                return null;
            }

            Point[] row = this.array[y];

            if(x < 0 || x >= row.length) {
                return null;
            }

            return row[x];
        }

        // get arraylist of Points for each adjacent space that matches the target string
        public ArrayList<Point> getAdjacentMatches(Point p, String match) {
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if((i!=0 || j!=0) && found != null && found.symbol.equals(match)) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        public ArrayList<Point> getAdjacents(Point p){
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if((i!=0 || j!=0) && found != null) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        public int count(String sym){
            int result = 0;
            for(int y=0; y<array.length; y++){
                for(int x=0; x<array[y].length; x++){
                    Point cur = this.get(x, y);
                    if(cur!=null && cur.symbol.equals(sym)){
                        result++;
                    }
                }
            }
            return result;
        }

        // get the direction string from point 1 to point 2
        public String getDirection(Point p1, Point p2) {
            int dx = p2.x - p1.x;
            int dy = p2.y - p1.y;
            dx = Math.abs(dx) / (dx==0?1:dx);
            dy = Math.abs(dy) / (dy==0?1:dy);

            return this.c2d.get(dx + "," + dy);
        }

        public Point2D getCenterOf(String sym){
            Point2D result = new Point2D(0,0);
            int count = 0;
            for(int y=0; y<array.length; y++){
                for(int x=0; x<array[y].length; x++){
                    if(this.get(x,y).symbol.equals(sym)){
                        result.x += x;
                        result.y += y;
                        count++;
                    }
                }
            }
            result.x /= count;
            result.y /= count;
            return result;
        }

    }

    public static class Point {
        int x, y;
        String symbol;

        public Point(int x, int y, String sym) {
            this.x=x;
            this.y=y;
            this.symbol=sym;
        }

        public Point(int x, int y, char sym){
            this(x, y, ""+sym);
        }
    }

    public static class Point2D{
        double x,y;
        public Point2D(double x, double y){
            this.x = x;
            this.y = y;
        }
    }
}

1

Я думаю, що я опублікую своє подання, оскільки ви настільки щедрі, щоб додати логіку котла ...

У вашій логіці виникла проблема, коли дієта видавала б ATTACK замість EAT і витрачала б труп.

Я змінив вашу суть так само, щоб мати робоче рішення, яке повинно працювати досить добре. Він починається з 4 к.с. та 8 енергії, тож після розколу та відпочинку обидві клітини можуть знову розколотися. Вона намагатиметься розмножуватися, атакувати ворогів, їсти трупи та відпочивати, в такому порядку. Таким чином, внутрішні клітини будуть зберігати свої 8 енергетичних точок, щоб швидко замінити вбиті зовнішні клітини і залишити їм 3 енергетичні точки, щоб здійснити 3-кратну атаку або розмножитися після одного повороту. 4 к.с. повинні вижити принаймні однією атакою повних сил.

кислота, здається, є марною витратою балів для мене, тому я стримав її ...

Я не перевіряв подання, оскільки це було 2 хвилини;)

ось мій код:

/*
 Sample code for a "Battle for the Petri Dish" cell

 Released under the terms of the WTF Public License
 No warranty express or implied is granted, etc, etc.

 I just hacked this together very quickly; improvements are welcome, so please fork the Gist if you like.

 used this code for a submission @kostronor

 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

public class SlimeCell {
    public static final int MAX_HP = 4;
    public static final int MAX_ENERGY = 8;
    public static final int ACIDITY = 0;

    // given arena state and cell stats, return an action string (e.g., "ATTACK NW 2", "DIVIDE S")
    public static String decide(final Arena arena, final Point cell, final int hp, final int energy) {
        // empty and corpses are free for movement and division
        ArrayList<Point> nearbyEmpty = arena.getAdjacentMatches(cell, ".");
        nearbyEmpty.addAll(arena.getAdjacentMatches(cell, "c"));

        ArrayList<Point> nearbyEnemies = arena.getAdjacentMatches(cell, "x");
        ArrayList<Point> nearbyCorpses = arena.getAdjacentMatches(cell, "c");
        ArrayList<Point> nearbyFriends = arena.getAdjacentMatches(cell, "o");

        // if you have energy and space to divide, divide into a random space
        if((energy >= 5) && (nearbyEmpty.size() > 0)) {
            Point randomEmpty = nearbyEmpty.get((int)Math.floor(nearbyEmpty.size()*Math.random()));
            return "DIVIDE " + arena.getDirection(cell, randomEmpty);
        }

        // if at least one adjacent enemy, attack if possible
        if((energy > 0) && (nearbyEnemies.size() > 1)) {
            int attackStrength = Math.min(energy, 3);
            Point enemy = nearbyEnemies.get((int)Math.floor(nearbyEnemies.size()*Math.random()));
            return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
        }

        // if there's a nearby corpse, eat it if your energy is below max
        if(nearbyCorpses.size() > 0) {
            Point corpse = nearbyCorpses.get((int)Math.floor(nearbyCorpses.size()*Math.random()));
            return "EAT " + arena.getDirection(cell, corpse);
        }

        return "REST";

    }

    public static void main(final String[] args) throws IOException {
        BufferedReader br =
                new BufferedReader(new InputStreamReader(System.in));

        String firstLine;

        firstLine = br.readLine();
        if(firstLine.equals("BEGIN")) {
            System.out.println(MAX_HP + " " + MAX_ENERGY + " " + ACIDITY);
        } else {
            String[] dimensions = firstLine.split(" ");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            Point[][] arena = new Point[height][];
            String input;
            int lineno = 0;

            while(!(input=br.readLine()).equals("")) {
                String[] charList = input.substring(1).split("");
                arena[lineno] = new Point[width];
                for(int i=0; i<charList.length; ++i) {
                    arena[lineno][i] = new Point(i, lineno, charList[i]);
                }
                lineno++;
            }

            String[] stats = br.readLine().split(" ");
            int x = Integer.parseInt(stats[0]);
            int y = Integer.parseInt(stats[1]);
            int hp = Integer.parseInt(stats[2]);
            int energy = Integer.parseInt(stats[3]);

            Arena arenaObj = new Arena(arena, width, height);
            System.out.print(decide(arenaObj, arenaObj.get(x,y), hp, energy));
        }
    }

    public static class Arena {
        public Point[][] array;
        public HashMap<String, String> c2d;
        public int height;
        public int width;

        public Arena(final Point[][] array, final int width, final int height) {
            this.array = array;
            this.width = width;
            this.height = height;

            this.c2d = new HashMap<String, String>();
            this.c2d.put("0,0", "-");
            this.c2d.put("0,-1", "N");
            this.c2d.put("0,1", "S");
            this.c2d.put("1,0", "E");
            this.c2d.put("-1,0", "W");
            this.c2d.put("-1,-1", "NW");
            this.c2d.put("1,-1", "NE");
            this.c2d.put("-1,1", "SW");
            this.c2d.put("1,1", "SE");
        }

        // get the character at x,y
        // or return empty string if out of bounds
        public Point get(final int x, final int y) {
            if((y < 0) || (y >= this.array.length)){
                return null;
            }

            Point[] row = this.array[y];

            if((x < 0) || (x >= row.length)) {
                return null;
            }

            return row[x];
        }

        // get arraylist of Points for each adjacent space that matches the target string
        public ArrayList<Point> getAdjacentMatches(final Point p, final String match) {
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if(((i!=0) || (j!=0)) && (found != null) && found.symbol.equals(match)) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        // get the direction string from point 1 to point 2
        public String getDirection(final Point p1, final Point p2) {
            int dx = p2.x - p1.x;
            int dy = p2.y - p1.y;
            dx = Math.abs(dx) / (dx==0?1:dx);
            dy = Math.abs(dy) / (dy==0?1:dy);

            return this.c2d.get(dx + "," + dy);
        }

    }

    public static class Point {
        int x, y;
        String symbol;

        public Point(final int x, final int y, final String sym) {
            this.x=x;
            this.y=y;
            this.symbol=sym;
        }
    }
}

1

Тонко розкиданий бомбардувальник

Оскільки ви так люб'язно надали кодовий код, я вирішив зробити власну просту клітинку; Ця клітина має 4 кислотності, всього 1 к.с. і 7 енергії. Він намагається вийти з кола дружніх, а потім чекає там (або їсть, якщо це можливо), поки не отримає шанс підірвати чи повторити. Атаки, лише якщо це єдиний варіант.

Це досить м'яча стратегія і, ймовірно, погано працює, але мені цікаво подивитися, як це відбувається. Я протестую його і вдосконалю пізніше сьогодні, можливо.

/*
 Sample code for a "Battle for the Petri Dish" cell

 Released under the terms of the WTF Public License,
 No warranty express or implied is granted, etc, etc.

 I just hacked this together very quickly; improvements are welcome, so please fork the Gist if you like.
*/

// used in defining cell spec
var MAX_HP = 1;
var MAX_ENERGY = 7;
var ACIDITY = 4;

/*
   The decide function takes an Arena object (see below for prototype methods), a cell object,
   and an outputCallback, which accepts a command string to output
*/
function decide(arena, cell, outputCallback) {
    var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]);
    var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]);
    var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]);
    var nearbyFriendlies = arena.getAdjacentMatches(cell.point, ["o"]);

    //attempt to move away from friendlies if possible
    if(nearbyFriendlies.length>1 && cell.energy>0)
    {
        for(var i=0; i<nearbyEmpties.length; ++i)
        {
            var space = nearbyEmpties[i];
            if(arena.getAdjacentMatches(space, ["o"]).length == 1)
            {
                outputCallback("MOVE " + arena.getDirection(cell,space));
                return;
            }
        }
    }

    // Explode if there are two more adjacent enemies than friendlies or enemies and no friendlies.
    if((nearbyEnemies.length - nearbyFriendlies.length > 1 || (nearbyEnemies.length>0 && nearbyFriendlies.length == 0)) 
        && cell.energy >= cell.hp && cell.hp <= 3)
    {
        outputCallback("EXPLODE");
        return;
    }

    // if you have the energy and space to divide, and there's a way for the child to get away from friendlies, do it.
    if(cell.energy >= 5 && nearbyEmpties.length > 0)
    {
        for(var i=0; i<nearbyEmpties.length; ++i)
        {
            var space = nearbyEmpties[i];
            var possiblePositions = arena.getAdjacentMatches(space, ["o"]);
            for(var i=0; i<possiblePositions.length; ++i)
            {
                if(arena.getAdjacentMatches(possiblePositions[i], ["o"]).length == 0)
                {
                    outputCallback("DIVIDE " + arena.getDirection(cell,space));
                    return;
                }
            }
        }
    }

    // if at least one adjacent enemy, attack if possible
    if(cell.energy > 0 && nearbyEnemies.length > 0)
    {
        outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length*Math.random())|0]) + " " + Math.min(cell.energy, 3));
        return;
    }

    // if there's a nearby corpse, eat it if your energy is below max
    if(nearbyCorpses.length > 0)
    {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0]));
        return;
    }

    outputCallback("REST");
    return;
}

var input = "";
// quiet stdin EPIPE errors
process.stdin.on("error", function(err) {
    //console.log("slight error: " + err);
});
process.stdin.on("data", function(data) {
    input += data;
});
process.stdin.on("end", function() {
    if(input == "BEGIN") {
        // output space-separated attributes
        process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" "));
    } else {
        // read in arena and decide on an action
        var arena = new Arena();
        var lines = input.split("\n");
        var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); });
        arena.width = dimensions[0];
        arena.height = dimensions[1];
        for(var y=1; y<=dimensions[1]; ++y) {
            for(var x=0; x<lines[y].length; ++x) {
                arena.set(x, y-1, lines[y][x]);
            }
        }

        var stats = lines[dimensions[1]+2].split(" ");
        var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) };

        // decide on an action and write the action to stdout
        decide(arena, cell, function(output) { process.stdout.write(output); })
    }
});

var Arena = function() {
    this.dict = {};
};
Arena.prototype = {
    // get Point object
    get: function(x,y) {
        return this.dict[x+","+y];
    },

    // store Point object
    set: function(x,y,d) {
        this.dict[x+","+y] = new Point(x,y,d);
    },

    // get an array of all Points adjacent to this one whose symbol is contained in matchList
    // if matchList is omitted, return all Points
    getAdjacentMatches: function(point, matchList) {
        var result = [];
        for(var i=-1; i<=1; ++i) {
            for(var j=-1; j<=1; ++j) {
                var inspectedPoint = this.get(point.x+i, point.y+j);
                if(inspectedPoint && 
                   (i!=0 || j!=0) &&
                   (!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) {
                    result.push(inspectedPoint);
                }
            }
        }
        return result;
    },

    // return the direction from point1 to point2
    getDirection: function(point1, point2) {
        var dx = point2.x - point1.x;
        var dy = point2.y - point1.y;
        dx = Math.abs(dx) / (dx || 1);
        dy = Math.abs(dy) / (dy || 1);

        c2d = { "0,0":"-",
                "0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W",
                "-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" };

        return c2d[dx + "," + dy];
    }
}

var Point = function(x,y,d) {
    this.x = x;
    this.y = y;
    this.symbol = d;
}
Point.prototype.toString = function() {
    return "(" + this.x + ", " + this.y + ")";
}

Я намагаюся перевірити його, але я не змушую його працювати. Я встановив node.js спробував командний рядок node c:/cells/petri.js 'node c:/cells/bomber.js' 'node c:/cells/sample.js. Коли я набираю це на консолі програми вузла, я отримую лише три крапки, коли я намагаюся запустити його у вікні cmd, я отримую: «вузол» не розпізнається як внутрішня чи зовнішня команда, функціонуюча програма чи пакетний файл. Я зберегла всі файли як .js файли у правильній папці. Якась допомога для нооб? Я б пішов у чат або прокоментував десь ще, але мій представник занадто низький.
суперактор

Оскільки я не можу протестувати, зараз здорово, якщо хтось міг би сказати мені, як мої клітини роблять проти своїх. Я вдруге здогадуюсь про свою тактику або, принаймні, думаю, що вона потребує доопрацювання.
суперактор

Здається, у вас є тип лінії if((nearbyEnemies.length - nearbyFriendlies.length > 1 ¦¦ - вони ¦¦, здається, не є дійсним оператором, і у вас є невідповідні дужки. Я думаю, можливо, форматування коду зіпсувалося, коли ви його розмістили?
apsillers

За моїми тестами, це дуже погано. У вас багато завдань ( =), коли ви хочете, це порівняння рівності ( ==).
justhalf

Ой, чорт. Я здебільшого програмував мовою, де (=) - це призначення, коли я писав це, чи працює це краще? Я ніколи не очікував, що це буде чудово, хоча.
суперактор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.