Trận chiến cho đĩa Petri


32

Trong thử thách này, bạn phải thiết kế một loài sinh vật đơn bào để chiến đấu đến chết trong đấu trường đĩa petri. Đấu trường được biểu diễn dưới dạng lưới hình chữ nhật, trong đó mỗi ô chiếm một khoảng trống:

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

Thuộc tính

Mỗi ô có ba thuộc tính. Khi chỉ định loài tế bào của bạn khi bắt đầu trò chơi, bạn phân bổ 12 điểm trong số các thuộc tính này.

  • Điểm nhấn (HP): Nếu HP của tế bào rơi về 0, nó sẽ chết. Các tế bào mới có HP đầy đủ.
    • Khi một tế bào chết, nó để lại một xác chết có thể bị các tế bào khác ăn để lấy năng lượng.
    • Một ô không thể lấy lại được lượng HP đã mất, nhưng nó có thể tạo ra một ô mới với toàn bộ HP bằng cách chia.
  • Năng lượng : Hầu hết các hành động mà một tế bào có thể cần đến năng lượng. Bằng cách tích cực nghỉ ngơi, một tế bào có thể lấy lại năng lượng đã mất đến mức tối đa của loài đó.
    • Một loài tế bào có ít hơn 5 năng lượng có khả năng thất bại, vì nó không thể phân chia để tạo ra các tế bào mới.
    • Một tế bào không thể lấy lại năng lượng vượt quá giá trị tối đa của loài.
    • Một ô mới được tạo có giá trị năng lượng ban đầu được sao chép từ cha mẹ của nó (và giá trị tối đa được quy định bởi đặc tả loài của nó).
  • Độ axit : Nếu một tế bào chọn phát nổ, mức độ axit của tế bào được sử dụng để tính toán thiệt hại cho các tế bào lân cận.

Hành động

Mỗi lượt, mỗi ô có thể thực hiện một hành động:

  • Di chuyển: Tế bào di chuyển một không gian theo bất kỳ hướng nào (N / S / E / W / NE / NW / SE / SW) với chi phí 1 năng lượng.

    • Một tế bào không thể di chuyển lên một không gian bị chiếm bởi một tế bào sống khác.
    • Một tế bào không thể di chuyển ra khỏi lưới điện.
    • Di chuyển lên một xác chết tế bào phá hủy xác chết.
  • Tấn công: Một tế bào tấn công một tế bào liền kề, gây sát thương từ 1 đến 3, bằng cách tăng 1 đến 3 điểm năng lượng.

    • Một tế bào có thể tấn công theo bất kỳ hướng nào (N / S / E / W / NE / NW / SE / SW).
    • Nó là hợp pháp để tấn công các tế bào thân thiện.
  • Phân chia: Tế bào phân chia và tạo ra một tế bào mới trên một không gian liền kề, với chi phí là 5 năng lượng.

    • Một ô có thể phân chia theo bất kỳ hướng nào (N / S / E / W / NE / NW / SE / SW).
    • Các tế bào mới có đầy đủ HP theo đặc điểm kỹ thuật ban đầu của bạn.
    • Tế bào mới có nhiều năng lượng như tế bào mẹ của nó sau khi trừ chi phí phân chia. (Ví dụ, một tế bào cha mẹ có 8 điểm năng lượng ban đầu sẽ bị giảm xuống còn 3 năng lượng và tạo ra một tế bào con có 3 năng lượng).
    • Một tế bào mới không thể hoạt động cho đến lượt tiếp theo của bạn.
    • Một tế bào không thể phân chia thành một không gian bị chiếm giữ bởi một tế bào sống, nhưng nó có thể phân chia thành một không gian bị chiếm giữ bởi một xác chết tế bào (điều này phá hủy xác chết).
  • Ăn: Một tế bào ăn một xác chết tế bào liền kề, thu được 4 năng lượng.

    • Một tế bào có thể ăn theo bất kỳ hướng nào (N / S / E / W / NE / NW / SE / SW).
  • Nghỉ ngơi: Một tế bào không làm gì trong một lượt, lấy lại 2 năng lượng.

  • Phát nổ: Khi một ô có 3 hoặc ít HP hơn và nhiều năng lượng hơn HP, nó có thể chọn phát nổ, gây sát thương cho cả tám ô liền kề.

    • Thiệt hại cho mỗi ô liền kề là (exploding cell HP) + (explodng cell acidity)
    • Một tế bào phát nổ sẽ chết và để lại một xác chết, cũng như bất kỳ tế bào nào bị giết trong vụ nổ.

Giao thức

Thiết lập

Chương trình của bạn sẽ chạy với chuỗi BEGINđược cung cấp trên stdin. Chương trình của bạn phải viết ra một danh sách gồm 3 số nguyên không âm, đại diện cho HP, năng lượng và tính axit cho các loại tế bào của bạn: ví dụ : 5 6 1. Các số phải tổng bằng 12. Tính axit có thể 0, nếu bạn muốn. (Các thuộc tính khác cũng có thể bằng 0, nhưng làm như vậy có chức năng làm mất trò chơi!)

Bạn bắt đầu với một ô, ở góc tây bắc hoặc đông nam, một khoảng cách từ hai cạnh. Các tế bào bắt đầu có đầy đủ HP và năng lượng.

Mọi tế bào hoạt động

Mỗi lượt, chương trình của bạn sẽ được gọi một lần cho mỗi ô còn sống trong nhóm của bạn (ngoại trừ các ô vừa tạo lần lượt này) để ô có thể hoạt động. Chương trình của bạn được cung cấp dữ liệu về stdin bao gồm trạng thái đĩa petri và thông tin về ô cụ thể này:

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

6 3 5 7

Hai số đầu tiên cho biết chiều rộng và chiều cao của đấu trường: ở đây, có một đấu trường 10 nhân 4.

  • Các otế bào là của bạn; các xtế bào là kẻ thù của bạn. (Điều này luôn đúng; mỗi người chơi luôn nhìn thấy các ô của riêng mình o.)
  • Các .không gian trống rỗng.
  • Các ckhông gian đại diện cho xác chết tế bào ăn được.

Các số sau dòng trống biểu thị thông tin về ô này:

  • Hai số đầu tiên là x,ytọa độ, được lập chỉ mục từ 0,0phía trên bên trái (vì vậy 6 3ở đây đề cập đến ô phía nam nhất o).
  • Số thứ ba là HP của tế bào; số thứ tư là năng lượng của tế bào.

Chương trình của bạn sẽ xuất ra (để xuất chuẩn) một hành động. Trong các ví dụ dưới đây, chúng tôi sẽ sử dụng Nlàm hướng dẫn ví dụ, nhưng nó có thể là bất kỳ hướng hợp pháp nào cho hành động đó ( N/ S/ E/ W/ NE/ NW/ SE/ SW). Tất cả đầu ra chương trình không phân biệt chữ hoa chữ thường, nhưng các ví dụ sẽ sử dụng chữ hoa. Bất kỳ hành động đầu ra nào không hợp lệ (vì nó có cú pháp không hợp lệ hoặc cố gắng thực hiện một hành động bất hợp pháp) sẽ bị bỏ qua và dẫn đến việc nhập tế bào REST(và do đó thu được 2 năng lượng).

  • MOVE N
  • DIVIDE N
  • EAT N
  • ATTACK N 2 - con số thể hiện sức mạnh của cuộc tấn công (1 - 3)
  • REST
  • EXPLODE

Lượt của nhóm của bạn bao gồm tất cả các tế bào của bạn sẽ có cơ hội hành động, từng cái một. Tất cả các tế bào của bạn hoạt động trước khi bất kỳ tế bào của đối thủ hành động. Khi tất cả các tế bào của bạn hoạt động, lượt của bạn kết thúc và lượt của đối thủ bắt đầu. Khi tất cả các tế bào của đối thủ hành động, lượt của bạn bắt đầu lại. Trong lượt của bạn, mỗi ô được ưu tiên hành động dựa trên tuổi của nó: các ô cũ nhất trong nhóm của bạn hoạt động trước tiên trước các ô trẻ hơn.

Thí dụ

Đây là cách một chương trình có thể hoạt động. Đầu vào từ stdin được ký hiệu ở đây với các >mũi tên hàng đầu (được phân tách khỏi đầu vào thực tế bằng một khoảng trắng làm rõ) và đầu ra trên thiết bị xuất chuẩn có <mũi tên.

> BEGIN
< 5 6 1

Sau đó, chương trình được gọi lại:

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

Sau lượt của đối thủ (người quyết định DIVIDE Wvới một ô bắt đầu), chương trình của bạn được gọi hai lần, một lần cho mỗi ô:

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

Đối với lời mời thứ hai trong lượt của bạn:

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

Lưu ý ô thứ hai này sẽ thấy trạng thái bảng cập nhật dựa trên chuyển động của ô khác trước đó trong lượt của bạn. Cũng lưu ý rằng tế bào này đã được tạo ra với 1 năng lượng, bởi vì tế bào mẹ có 6 năng lượng khi nó thực hiện phân chia lần cuối (vì vậy, 6 gốc, trừ chi phí phân chia 5 năng lượng, tạo ra một tế bào con có 1 năng lượng).

Bây giờ lượt của bạn đã kết thúc và lượt của đối thủ của bạn bắt đầu. Hai ô đối diện sẽ có cơ hội hành động, và sau đó lượt tiếp theo của bạn bắt đầu.

Chiến thắng

Bạn có thể giành chiến thắng bằng một trong hai cách:

  • Phá hủy tất cả các tế bào đối lập, hoặc
  • Có nhiều ô hơn đối thủ của bạn sau mỗi người chơi đã hoàn thành 150 lượt

Ghi điểm sẽ dựa trên số trận thắng trong 100 trận đấu với nhau. Trong một nửa số mô phỏng, chương trình của bạn sẽ được phép đi trước.

Các trò chơi tie (nghĩa là chính xác cùng số lượng ô sau 150 lượt hoặc các ô duy nhất còn lại bị giết trong một vụ nổ) không được tính vào tổng số chiến thắng của một trong hai người chơi.

Thông tin khác

  • Chương trình của bạn không nên cố gắng duy trì trạng thái (ngoài việc sử dụng trạng thái của đĩa Petri): các sinh vật đơn bào không có bộ nhớ rất tốt và phản ứng với thế giới từng khoảnh khắc. Cụ thể, ghi vào một tệp (hoặc lưu trữ dữ liệu khác), giao tiếp với máy chủ từ xa hoặc cài đặt các biến môi trường không được phép rõ ràng.
  • Đệ trình sẽ được chạy / biên dịch trên Ubuntu 12.04.4.
  • Các chi tiết cụ thể của 100 trò chơi ghi điểm chưa được xác nhận, nhưng chúng có thể sẽ liên quan đến nhiều kích cỡ đấu trường (ví dụ: 50 chạy trên một đấu trường nhỏ và 50 chạy trên một đấu trường lớn hơn). Đối với một đấu trường lớn hơn, tôi có thể tăng số lượt quay tối đa để đảm bảo rằng một trận chiến thích hợp có thể diễn ra.

Tài nguyên

Đây là mã trình điều khiển chạy mô phỏng, được viết cho Node.js, được gọi bởi node petri.js 'first program' 'second program'. Ví dụ, việc đặt một ô được viết bằng Python lên một ô được viết bằng Java có thể trông như thế node petri.js 'python some_cell.py' 'java SomeCellClass'.

Ngoài ra, tôi hiểu rằng đọc và phân tích nhiều dòng trên stdin có thể là một nỗi đau rất lớn, vì vậy tôi đã phác thảo một vài ô mẫu hoàn chỉnh bằng các ngôn ngữ khác nhau mà bạn có thể tự do xây dựng, đại tu hoàn toàn hoặc bỏ qua hoàn toàn.

Tất nhiên bạn có thể tự do viết một tế bào bằng một ngôn ngữ khác; đây chỉ đơn giản là ba ngôn ngữ tôi quyết định viết mã soạn sẵn để hỗ trợ tiết kiệm thời gian.

Nếu bạn có bất kỳ vấn đề nào khi chạy trình điều khiển, vui lòng ping tôi trong phòng trò chuyện tôi đã tạo cho thử thách này . Nếu bạn không có đủ danh tiếng để trò chuyện, thì hãy để lại nhận xét.


1
@Ryan Bạn sẽ cần chỉ định một lệnh có thể chạy hoàn toàn như 'node c:/cell/cell_template.js'đối với từng đối số, giống như bạn cần chỉ định 'java CellTemplate'cho mã Java. Tôi sẽ làm cho nó rõ ràng hơn trong văn bản thách thức. Nếu bạn vẫn gặp sự cố, chúng tôi (và bất kỳ ai khác có vấn đề về kỹ thuật) có thể tiếp tục cuộc thảo luận này trong phòng trò chuyện tôi vừa thực hiện .
apsillers

1
@Moogie Chỉ có 2 đối thủ mỗi trò chơi.
apsillers

3
Man, những ví dụ thật tuyệt!
CommonGuy

3
@apsillers chúng tôi đã hỏi bạn trong trò chuyện nhưng quên ping bạn để bạn có thể không nhận thấy: chúng tôi đã tự hỏi khi nào bạn có kế hoạch chạy trò chơi?
plannapus

2
@Manu Cuối cùng, vâng! Tôi xin lỗi vì sự chậm trễ rất lâu. Tôi đã thiết lập mã ghép cặp / ghi điểm và hiện tôi đang xóa mọi vấn đề với bài nộp trong nỗ lực của mình để chạy mã của mọi người. Sau đó, tôi sẽ để nó chạy trên máy chủ của mình trong một ngày hoặc lâu hơn để hoàn thành các vòng.
apsillers

Câu trả lời:


3

Đây là bot tương đối đơn giản của tôi, mà tôi đã lập trình trong Ruby. Về cơ bản, nó ưu tiên phân chia trước và cố gắng phân chia về phía kẻ thù để giành quyền kiểm soát trên sân. Ưu tiên thứ hai của nó là ăn uống, và thứ ba là tấn công. Nó dễ dàng đánh bại tế bào Python mẫu.

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"

Tôi không phải là lập trình viên Ruby, vì vậy tôi tự hỏi tại sao một số biến là bình thường và một số biến bắt đầu bằng a $.
xem

$được sử dụng để chỉ ra một biến toàn cục. Vâng, họ là ác quỷ, nhưng trong chương trình nhỏ này, nó không quá quan trọng.
Alex

Biến toàn cầu là xấu chỉ trong mã sản xuất. Ai nhớ họ trong các kịch bản như thế này?
xem

Có phải tế bào của tôi thực sự là người duy nhất có khả năng lan truyền không phải là 4-8-0?
Alex

Đây là ứng cử viên tốt nhất cho Phối hợp vi khuẩn của tôi cho đến nay! Tôi đã xây dựng chiến lược của mình dựa trên kết quả kiểm tra trên sinh vật đơn bào của bạn. =)
cần

3

Amip

Đầu tiên tách thành bốn và sau đó cố gắng đến khu vực giữa để hạn chế không gian sao chép của đối thủ. Sau đó bắt đầu nhân rộng. Khi di chuyển hoặc sao chép, tìm đường dẫn tối ưu đến kẻ thù gần nhất và di chuyển hoặc phân chia về phía đó, để cố gắng cắt đứt không gian có sẵn của kẻ thù.

Nếu kẻ địch ở gần hoặc cách một khoảng trống, sẽ luôn tấn công hoặc di chuyển về phía nó, cho phép hàng phía sau không làm gì để lấp đầy bất kỳ khoảng trống nào.

Tôi chưa thử nghiệm điều này với bất kỳ bài nộp nào khác vì vậy không biết nó sẽ hoạt động tốt như thế nào.

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 + ")";
}

Đây thực sự là một chiến lược khá tốt, nếu bạn giảm bớt rủi ro bằng cách xem xét số lượng bạn bè lân cận trước khi di chuyển, nếu không, những người vội vàng khác có thể dễ dàng vượt qua hàng phòng thủ mỏng của bạn trong trận đấu sớm (và do đó chỉ áp dụng trong các bảng nhỏ)
vừa qua

Btw, điều này dường như không hoạt động như dự định nếu đó là người chơi 2.
cần

3

Tế bào đơn giản được thực hiện trong node.js. Đã kiểm tra nó một lần nữa ví dụ tế bào nút và chống lại Wesronor, nó đánh bại chúng.

Cập nhật

Vẫn khá đơn giản, cố gắng di chuyển về phía kẻ thù hoặc chia rẽ.

// 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 đã xác định được một vài lỗi nghiêm trọng trong chương trình trình điều khiển (MOVE miễn phí và EXPLODE không tính đến tính axit). Nếu bạn quan tâm đến việc kiểm tra lại mã trình điều khiển được cập nhật và cập nhật trình của bạn, vui lòng cho tôi biết. Nếu không, điều đó cũng hoàn toàn tốt.
apsillers

2

Sự phát triển

Trình này đã phát triển và không còn là một sinh vật đơn bào! Nó cố gắng tấn công / phát nổ bất cứ khi nào có thể, nếu không nó sẽ phân chia hoặc di chuyển về phía kẻ thù. Di chuyển sẽ giải quyết vấn đề của một tế bào được bao quanh bởi các tế bào thân thiện với năng lượng tối đa, không thể làm điều gì đó hữu ích.
Sau khi di chuyển, luôn có 3 năng lượng còn lại để đánh kẻ thù mạnh nhất có thể.

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 đã xác định được một vài lỗi nghiêm trọng trong chương trình trình điều khiển (MOVE miễn phí và EXPLODE không tính đến tính axit). Nếu bạn quan tâm đến việc kiểm tra lại mã trình điều khiển được cập nhật và cập nhật trình của bạn, vui lòng cho tôi biết. Nếu không, điều đó hoàn toàn tốt.
apsillers

2

Berserker

Bởi vì tôi đã sử dụng Clojure, có một số hạn chế, chủ yếu là thời gian khởi động khổng lồ, tôi đã có một chút gan. Khi chương trình được đưa BEGINra 4 6 2 LOOP, nó chỉ ra rằng nó không dừng. Sau đó, nó nhận các đầu vào như một luồng liên tục và kết thúc bằng END. Nó không lưu bất kỳ trạng thái nào, được làm rõ bằng cách không sử dụng bất kỳ biến toàn cục hoặc sử dụng lại các giá trị trả về. Bởi vì việc triển khai cho hành động loopy này chưa được thực hiện, tôi không thể kiểm tra đầy đủ mã (tôi hy vọng mã này đủ rõ ràng).

Tế bào có được tên của nó từ bản chất của nó là phát nổ bất cứ khi nào có thể (và do đó có tính axit) và bằng cách ưu tiên tấn công ngay sau khi phân chia.

Tôi đã tải lên tệp jar được tạo vào Dropbox của mình . Chạy vớijava -jar petridish-clojure.jar

Chỉ cần làm rõ:

> 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))

Cập nhật nhật ký

1. Fixed the logic a little and removed redundancies.

Sử dụng tốt tính axit - trên thực tế, tôi nghĩ rằng đây là bot duy nhất sử dụng tính axit.
Alex

@Alex Chúng ta sẽ xem nó hoạt động như thế nào, nhưng tôi nghĩ rằng điều này sẽ có thể xóa Amoeba. Bạn nghĩ gì về mã? Tôi còn quá mới với clojure.
xem

Trong ví dụ của bạn, làm thế nào các tế bào mới được sinh ra có thể di chuyển? Tôi nghĩ bạn cần chờ thêm một lượt nữa?
vừa rồi

@justhalf Eh, tế bào không biết chúng bao nhiêu tuổi.
xem

Có, nhưng bộ điều khiển biết, phải không? Nó không phải là một lượt cho tế bào mới được hình thành.
vừa rồi

2

Đói, đói bot

Đây là một mục trong R. Tôi hy vọng tôi hiểu chính xác các thông số kỹ thuật về cách giao tiếp với chương trình của bạn. Nên được kích hoạt với Rscript Hungryhungrybot.R.
Nếu nó có ít nhất 6 năng lượng, nó sẽ phân chia, tốt nhất là theo hướng của kẻ thù. Nếu không, nó ăn bất cứ thứ gì bên cạnh nó hoặc bất cứ thứ gì có thể tiếp cận. Nếu không có thức ăn nào có thể tiếp cận được thì nó sẽ phát nổ khi có nhiều kẻ thù xung quanh hơn các tế bào chị em, hoặc chiến đấu với kẻ thù gần đó. Chỉ nghỉ nếu năng lượng bằng 0 và không có gì để ăn.

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())

Tôi (cuối cùng) đang cố gắng thực hiện thử thách và tôi tiếp tục nhận được Error: unexpected 'else' in "else"mã của bạn. Tôi sợ rằng tôi hoàn toàn không biết R, vì vậy tôi không thể bắt đầu giải quyết lỗi này. Để tham khảo, tôi gặp lỗi này cả khi tôi chạy nó trong trình điều khiển và khi tôi chỉ cần chạy chương trình và gõ thủ công BEGIN.
apsillers

@apsillers arf tôi đã thêm một dòng mới mà tôi không nên có: nó sẽ hoạt động ngay bây giờ.
plannapus

Điều đó đã sửa lỗi đó để chúng tôi có thể thông qua ô init; bây giờ tôi sẽ nhận được một cái khác khi trò chơi thực sự bắt đầu:Error in if (dir[1] < 0) where <- paste(where, "N", sep = "") : missing value where TRUE/FALSE needed
apsillers

Bây giờ lượt đầu tiên chạy tốt, nhưng lượt tiếp theo tạo ra Error: object 'food' not found(khi đối mặt với đệ trình Ruby của Alex, có thể là những người khác)
apsillers

Bạn di động bây giờ chạy tốt, cảm ơn! :) Tuy nhiên, justhalf đã xác định được một vài lỗi nghiêm trọng trong chương trình trình điều khiển (MOVE miễn phí và EXPLODE không tính đến tính axit). Nếu bạn quan tâm đến việc kiểm tra lại mã trình điều khiển được cập nhật và cập nhật trình của bạn, vui lòng cho tôi biết. Nếu không, điều đó hoàn toàn tốt.
apsillers

2

Vi khuẩn phối hợp

Tôi hy vọng tôi không quá muộn.

Chiến thắng trước các đối thủ khác (và luôn luôn bằng cách tiêu diệt tất cả), trong các thử nghiệm của tôi, và trận chiến sẽ không bao giờ kết thúc nếu nó phải đối mặt với chính nó, một bằng chứng cho thấy chiến lược này rất mạnh.

Khi bạn là người đơn bào, bạn có thể ghi nhớ trạng thái trước đó, nhưng bạn có thể khai thác vị trí của chính mình để hành xử khác đi! =)

Điều này sẽ phân chia vi khuẩn thành dải phân cách và động lực, và khi làm như vậy sẽ giữ cho nhiều vi khuẩn hữu ích hơn thay vì chỉ ở tuyến đầu, trong khi vẫn giữ tuyến phòng thủ.

Nó cũng điều phối các cuộc tấn công của mình để tập trung vào kẻ thù cụ thể, để kẻ thù bị giết nhanh hơn (điều này là để đối mặt với một tế bào đơn khác của tôi tập trung vào HP).

Ở giữa trò chơi, được phát hiện bởi số lượng tế bào trên bảng, chúng sẽ cố gắng di chuyển vào lãnh thổ của kẻ thù bằng cách vượt qua chúng. Đây là chiến lược chiến thắng quan trọng.

Điều này có tốc độ tăng trưởng cao nhất so với tất cả các đối thủ khác hiện tại, nhưng nó có khởi đầu chậm, vì vậy điều này hoạt động tốt hơn trên đấu trường lớn.

Chạy nó với 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

Tôi nghĩ rằng tôi sẽ đăng bài của mình, vì bạn rất hào phóng khi thêm logic soạn sẵn ...

Có một vấn đề trong logic của bạn, trong đó hành động ăn uống sẽ tạo ra ATTACK thay vì ĂN và sẽ lãng phí xác chết.

Tôi đã sửa đổi ý chính của bạn nhiều như vậy để có một giải pháp hoạt động, điều đó sẽ thực hiện tương đối tốt. Nó bắt đầu với 4 hp và 8 năng lượng, vì vậy sau khi phân tách và nghỉ ngơi, cả hai tế bào có thể phân chia lại. Nó sẽ cố gắng tự nhân lên, tấn công kẻ thù, ăn xác chết và nghỉ ngơi, theo thứ tự này. Vì vậy, các tế bào bên trong sẽ lưu trữ 8 điểm năng lượng của chúng, để nhanh chóng thay thế các tế bào bên ngoài bị tiêu diệt và để lại cho chúng 3 điểm năng lượng để thực hiện một cuộc tấn công 3 điểm hoặc tự nhân lên sau một lượt nghỉ ngơi. 4 hp sẽ tồn tại ít nhất một cuộc tấn công toàn lực.

axit dường như là một sự lãng phí điểm đối với tôi vì vậy tôi đã giữ nó ...

Tôi đã không kiểm tra trình, vì nó là một điều 2 phút;)

đây là mã của tôi:

/*
 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

Máy bay ném bom mỏng

Vì bạn rất vui lòng cung cấp mã soạn sẵn, tôi quyết định tạo ô đơn giản của riêng mình; Tế bào này có 4 độ axit, chỉ 1 hp và 7 năng lượng. Nó cố gắng thoát ra khỏi phạm vi kết bạn và sau đó đợi ở đó (hoặc ăn nếu có thể) cho đến khi có cơ hội nổ tung hoặc sao chép. Tấn công chỉ khi đó là lựa chọn duy nhất.

Đó là một chiến lược bóng bẩy và có thể sẽ hoạt động kém, nhưng tôi tò mò muốn xem nó như thế nào. Tôi sẽ kiểm tra nó và cải thiện nó sau hôm nay, có thể.

/*
 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 + ")";
}

Tôi đang cố kiểm tra nhưng tôi không chạy được. Tôi đã cài đặt node.js đã thử dòng lệnh node c:/cells/petri.js 'node c:/cells/bomber.js' 'node c:/cells/sample.js. Khi tôi gõ cái này trong bảng điều khiển ứng dụng nút, tôi chỉ nhận được ba dấu chấm, khi tôi thử và chạy nó trong cửa sổ cmd, tôi nhận được: 'nút' không được nhận dạng như một lệnh nội bộ hoặc bên ngoài, chương trình có thể hoạt động hoặc tệp bó. Tôi đã lưu tất cả các tệp dưới dạng tệp .js trong thư mục chính xác. Có ai giúp cho một người không? Tôi sẽ đi đến cuộc trò chuyện hoặc bình luận ở một nơi khác, nhưng đại diện của tôi quá thấp.
overactor

Vì tôi không thể kiểm tra, nên bây giờ, thật tuyệt nếu ai đó có thể cho tôi biết các tế bào của tôi chống lại chúng như thế nào. Tôi thứ hai đoán chiến thuật của tôi, hoặc ít nhất là nghĩ rằng nó cần tinh chỉnh.
overactor

Bạn dường như có một loại trên dòng if((nearbyEnemies.length - nearbyFriendlies.length > 1 ¦¦ - những cái đó ¦¦dường như không phải là một toán tử hợp lệ và bạn có dấu ngoặc đơn không khớp. Tôi nghĩ có lẽ định dạng mã đã bị rối khi bạn đăng nó?
apsillers

Điều này thực hiện khá tệ theo các thử nghiệm của tôi. Bạn có rất nhiều bài tập ( =) khi điều bạn muốn là so sánh bằng ( ==).
vừa rồi

Ôi chết thật. Tôi chủ yếu lập trình bằng ngôn ngữ trong đó (=) được gán lại khi tôi viết bài này, bây giờ nó có chạy tốt hơn không? Tôi không bao giờ mong đợi nó sẽ làm tốt mặc dù.
overactor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.