페트리 접시 전투


32

이 도전에서, 당신은 페트리 접시 경기장에서 죽음에 맞서 싸울 단일 세포 유기체 종을 설계해야합니다. 경기장은 직사각형 격자로 표시되며 각 셀은 하나의 공간을 차지합니다.

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

속성

모든 셀에는 세 가지 속성이 있습니다. 게임을 시작할 때 세포 종을 지정할 때이 속성들 사이에 12 점을 할당합니다.

  • 적중 점수 (HP) : 셀의 HP가 0으로 떨어지면 죽습니다. 새로운 세포에는 완전한 HP가 있습니다.
    • 세포가 죽으면 다른 세포가 에너지를 위해 먹을 수있는 시체 뒤에 남습니다.
    • 셀은 잃어버린 HP를 되 찾을 수 없지만 나눠서 HP가 가득 찬 새 셀을 만들 수 있습니다.
  • 에너지 : 세포가 취할 수있는 대부분의 행동에는 에너지가 필요합니다. 활동적으로 휴식을 취함으로써, 세포는 잃어버린 에너지를 최대 종까지 회복 할 수 있습니다.
    • 5 개 미만의 에너지를 갖는 세포 종은 새로운 세포를 생성하기 위해 분열 될 수 없기 때문에 실패 할 가능성이있다.
    • 세포는 종의 최대 값 이상으로 에너지를 되 찾을 수 없습니다.
    • 새로 생성 된 셀은 상위에서 복사 된 초기 에너지 값 (및 종 사양에 의해 지정된 최대 값)을 갖습니다.
  • 산도 : 세포가 폭발하기로 선택하면, 세포의 산도 수준은 인접한 세포에 대한 손상을 계산하는 데 사용됩니다.

행위

차례마다, 모든 세포는 하나의 행동을 취할 수 있습니다 :

  • 이동 : 셀은 1 에너지 비용으로 임의의 방향 (N / S / E / W / NE / NW / SE / SW)으로 한 공간 이동합니다.

    • 세포는 다른 살아있는 세포가 차지하는 공간으로 이동할 수 없습니다.
    • 셀은 격자 밖으로 이동할 수 없습니다.
    • 세포 시체로 옮기면 시체가 파괴됩니다.
  • 공격 : 한 셀이 인접한 셀을 공격하여 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 에너지를 되 찾습니다.

  • 폭발 : 세포에 HP보다 3 이하의 HP가 있고 더 많은 에너지가있을 경우, 폭발하여 인접한 8 개의 세포 모두에 피해를 줄 수 있습니다.

    • 인접한 각 셀의 손상은 (exploding cell HP) + (explodng cell acidity)
    • 폭발 한 세포는 죽어서 시체 뒤에 남습니다. 폭발에서 죽은 세포도 마찬가지입니다.

실험 계획안

설정

프로그램은 BEGINstdin에 제공된 문자열로 실행됩니다 . 프로그램은 세포 종에 대한 HP, 에너지 및 산도를 나타내는 공백이 아닌 3 개의 음이 아닌 정수 목록을 작성해야합니다. 예 : 5 6 1. 숫자의 합은 12 여야합니다 0. 원하는 경우 산도가 일 수 있습니다 . (다른 속성들도 0 일 수 있지만, 그렇게하면 기능상 게임을 상실합니다!)

북서쪽 또는 남동쪽 모서리에서 하나의 셀로 시작합니다. 시작 세포는 완전한 HP와 에너지를 가지고 있습니다.

모든 세포가 작용

매 턴마다, 당신의 팀에 살아있는 모든 세포 (이 턴에서 방금 생성 된 세포는 제외)에 대해 프로그램이 한 번 호출되어 세포가 행동 할 수 있습니다. 프로그램에는 페트리 접시 상태 및이 특정 셀에 대한 정보가 포함 된 stdin에 대한 데이터가 제공됩니다.

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

6 3 5 7

처음 두 숫자는 경기장 너비와 높이를 나타냅니다. 여기에는 10x4 경기장이 있습니다.

  • 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

이 두 번째 셀은 자신의 차례에서 다른 셀의 움직임에 따라 업데이트 된 보드 상태를 봅니다. 또한이 셀은 마지막 회전을 수행 할 때 모 셀에 6 개의 에너지가 있었기 때문에 1 개의 에너지로 생성되었습니다 (원래 6에서 5 에너지 분할 비용을 뺀 값은 1 개의 에너지를 가진 자식 셀을 생성 함).

이제 당신 차례가 끝나고 상대 차례가 시작됩니다. 두 개의 반대쪽 칸에 행동 할 기회가 주어지고 다음 차례가 시작됩니다.

승리

다음 중 하나를 통해 이길 수 있습니다.

  • 반대되는 모든 세포를 파괴하거나
  • 각 플레이어가 150 턴을 완료 한 후 상대방보다 많은 세포를 보유

득점은 서로의 제출에 대한 100 게임의 승리 횟수를 기반으로합니다. 시뮬레이션의 절반에서 프로그램이 먼저 진행됩니다.

동점 게임 (즉, 150 턴 후 정확히 같은 수의 셀 또는 폭발로 남아있는 유일한 셀이 함께 죽임)은 두 플레이어의 승리 합계에 포함되지 않습니다.

기타 정보

  • 프로그램은 상태를 유지하려고 시도해서는 안됩니다 (페트리 접시 상태를 사용하는 것 이상) : 단세포 유기체는 기억력이 우수하지 않으며 순간적으로 세계에 반응합니다. 특히 파일 (또는 다른 데이터 저장소)에 쓰거나 원격 서버와 통신하거나 환경 변수를 설정하는 것은 명시 적으로 허용되지 않습니다.
  • 제출은 Ubuntu 12.04.4에서 실행 / 컴파일됩니다.
  • 100 점수 게임의 세부 사항은 아직 확정되지 않았지만 여러 경기장 크기와 관련이있을 수 있습니다 (예 : 작은 경기장에서 50 회, 더 큰 경기장에서 50 회). 더 큰 경기장의 경우, 올바른 전투가 이루어질 수 있도록 최대 회전 수를 늘릴 수 있습니다.

자원

Node.js 용으로 작성된 시뮬레이션을 실행 하는 드라이버 코드는 다음과 같습니다node petri.js 'first program' 'second program' . 예를 들어, Java로 작성된 셀에 대해 Python으로 작성된 셀을 pitting하면 다음과 같습니다 node petri.js 'python some_cell.py' 'java SomeCellClass'.

또한 stdin에서 여러 줄을 읽고 구문 분석하는 것은 큰 고통이 될 수 있음을 이해하므로 자유롭게 빌드하거나 완전히 분해하거나 완전히 무시할 수있는 다른 언어로 몇 가지 완전한 샘플 셀을 작성했습니다.

물론 다른 언어로 된 셀을 자유롭게 작성할 수 있습니다. 이것들은 시간 절약을 위해 상용구 코드를 작성하기로 결정한 세 가지 언어입니다.

드라이버를 실행하는 데 문제가있는 경우이 챌린지를 위해 만든 대화방 에서 자유롭게 핑을 보내십시오 . 채팅 평판이 충분하지 않은 경우 댓글을 남기십시오.


1
@Ryan Java 코드에 대해 'node c:/cell/cell_template.js'지정해야하는 것처럼 각 인수에 대해 완전히 실행 가능한 명령 을 지정해야 'java CellTemplate'합니다. 챌린지 텍스트에서 더 명확하게 설명하겠습니다. 여전히 문제가있는 경우 Google (및 기술적 인 문제가있는 사람) 은 방금 만든 대화방 에서이 토론 계속할 수 있습니다 .
apillers

1
@Moogie 게임당 2 명의 상대 만.
apillers

3
사람, 모범은 훌륭합니다!
CommonGuy

3
@apsillers 우리는 채팅에서 당신에게 물었지만 당신은 핑을 잊어 버렸습니다.
plannapus

2
@Manu 마지막으로 그렇습니다! 매우 긴 지연에 대해 사과드립니다. 매치 메이킹 / 점수 코드를 설정했으며 이제 모든 사람의 코드를 실행하려는 시도에서 제출 관련 문제를 해결하고 있습니다. 그런 다음 라운드를 완료하기 위해 하루 정도 서버에서 실행하도록하겠습니다.
apillers

답변:


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 프로그래머가 아니기 때문에 일부 변수가 왜 정상이고 어떤 변수가로 시작하는지 궁금합니다 $.
seequ

$전역 변수를 나타내는 데 사용됩니다. 예, 그들은 사악하지만이 작은 프로그램에서는 그다지 중요하지 않습니다.
Alex

전역 변수는 프로덕션 코드에서만 악의적입니다. 누가 이런 식으로 대본을 떠올리나요?
seequ

능력이 4-8-0이 아닌 유일한 세포입니까?
Alex

이것은 지금까지 내 CoordinatedBacteria에 가장 적합한 경쟁자입니다! 단일 세포 유기체에 대한 테스트 결과를 바탕으로 전략을 세웠습니다. =)
justhalf

3

아메바

먼저 4 개로 분할 한 다음 상대 복제 공간을 제한하기 위해 중간 지점으로 이동합니다. 그런 다음 복제를 시작합니다. 이동하거나 복제 할 때 가장 가까운 적에게 최적의 경로를 찾은 후 적을 향해 이동하거나 나눠서 사용 가능한 공간을 차단합니다.

적이 인접하거나 한 칸 떨어져있는 경우, 항상 적을 향해 공격하거나 이동하여 뒤에있는 줄은 빈 공간을 채울 수 없습니다.

나는 다른 제출물에 대해 이것을 테스트하지 않았으므로 얼마나 잘 될지 모릅니다.

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

Btw, 플레이어 2 인 경우 의도 한대로 작동하지 않는 것 같습니다.
justhalf

3

간단한 셀에서 수행 node.js. 노드 셀 예제를 테스트하고 Kostronor와 비교하여 테스트했습니다.

최신 정보

여전히 간단하지만 적을 향해 움직이거나 분열하려고합니다.

// 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는 산도를 설명하지 않았습니다). 업데이트 된 드라이버 코드를 다시 테스트하고 제출 내용을 업데이트하려는 경우 알려주십시오. 그렇지 않다면 그것도 완벽합니다.
apillers

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는 산도를 설명하지 않았습니다). 업데이트 된 드라이버 코드를 다시 테스트하고 제출 내용을 업데이트하려는 경우 알려주십시오. 그렇지 않으면 완벽하게 괜찮습니다.
apillers

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

@Alex 우리는 그것이 어떻게 작동하는지 볼 것이지만, 이것이 Amoeba를 지울 수 있다고 생각합니다. 코드에 대해 어떻게 생각하십니까? 클로저를 처음 접했습니다.
seequ

귀하의 예에서, 새로 생성 된 셀은 어떻게 이동할 수 있습니까? 한 번 더 기다릴 필요가 있다고 생각 했습니까?
justhalf

@justhalf Eh, 세포는 몇 살인지 모릅니다.
seequ

예, 그러나 컨트롤러는 알고 있습니다. 새로 형성된 세포를 돌릴 수는 없습니다.
justhalf

2

배고픈 배고픈 봇

다음은 R의 항목입니다. 프로그램과 통신하는 방법에 대한 기술 사양이 무엇인지 올바르게 이해했으면합니다. 로 트리거해야합니다 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.
apillers

@apsillers arf 내가 없어야 할 줄 바꿈을 추가했습니다. 지금 작동해야합니다.
plannapus

셀 오류를 극복 할 수 있도록 오류가 수정되었습니다. 이제 게임이 실제로 시작될 때 또 다른 것을 얻습니다 :Error in if (dir[1] < 0) where <- paste(where, "N", sep = "") : missing value where TRUE/FALSE needed
apsillers

이제 첫 번째 턴은 정상적으로 진행되지만 다음 턴 Error: object 'food' not found에서는 Alex의 Ruby 제출에 반대 할 때 생산 될 가능성이 있습니다.
apsillers

당신은 이제 잘 지내요, 고마워요! :) 그러나 justhalf는 드라이버 프로그램에서 몇 가지 심각한 버그를 발견했습니다 (MOVE는 비용이 들지 않았으며 EXPLODE는 산도를 설명하지 않았습니다). 업데이트 된 드라이버 코드를 다시 테스트하고 제출 내용을 업데이트하려는 경우 알려주십시오. 그렇지 않으면 완벽하게 괜찮습니다.
apillers

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

상용구 논리를 추가하는 데 관대하기 때문에 제출 내용을 게시 할 것입니다 ...

당신의 논리에 먹는 행동이 EAT 대신 공격을 발행하고 시체를 낭비하는 문제가있었습니다.

나는 작동하는 솔루션을 갖기 위해 요점을 수정했는데, 이는 상대적으로 잘 수행되어야합니다. 4 마력과 8 에너지로 시작하므로 분할 및 휴식 후 두 셀이 다시 분할 될 수 있습니다. 그것은 스스로를 번식시키고, 적을 공격하고, 시체를 먹고 휴식을 취하려고 시도 할 것입니다. 따라서 내부 세포는 8 개의 에너지 점을 저장하여 사멸 된 외부 세포를 빠르게 대체하고 3 개의 에너지 점을 남겨 3 점 공격을하거나 1 턴 휴식 후 스스로 곱합니다. 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. 노드 응용 프로그램 콘솔에 이것을 입력하면 세 개의 점이 생깁니다 .Windows cmd에서 시도하고 실행할 때 '노드'가 내부 또는 외부 명령, 작동 가능한 프로그램 또는 배치 파일로 인식되지 않습니다. 모든 파일을 올바른 폴더에 .js 파일로 저장했습니다. 멍청한 놈을 도와 줄까? 채팅이나 다른 곳에 댓글을 달았지만 담당자가 너무 낮습니다.
overactor

테스트 할 수 없기 때문에 지금은 누군가 내 세포가 세포에 어떻게 작용하는지 말해 줄 수 있으면 좋습니다. 나는 내 전술을 추측하거나 최소한 정제가 필요하다고 생각합니다.
overactor

당신은 줄에 유형을 갖고있는 것 같다 if((nearbyEnemies.length - nearbyFriendlies.length > 1 ¦¦ 사람들은 - ¦¦유효한 연산자로하지 않는 것 그리고 당신은 괄호를 일치하지 않는했다. 코드를 게시했을 때 코드 형식이 엉망이었던 것 같습니다.
apillers

이것은 내 테스트에 따라 상당히 나쁘게 수행됩니다. =평등 비교 ( ==) 를 원하는 경우 많은 과제 ( )가 있습니다 .
justhalf

아, 젠장 나는 주로 이것을 쓸 때 (=)가 할당 된 언어로 프로그래밍하고있었습니다. 지금 더 잘 작동합니까? 나는 그것이 위대한 일을 기대하지 않았습니다.
overactor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.