Hunger Gaming - Ăn hoặc Chết


60

Hunger Gaming - Ăn hoặc Chết

Nếu bạn không ăn, bạn sẽ chết. Nếu bạn ăn, bạn sống (cho đến khi bạn chết). Bạn sẽ chết, vì vậy hãy cố gắng để chết cuối cùng.

Tổng quan

Có một hòn đảo với một đàn thú săn mồi. Bạn điều khiển một gói năm động vật ăn thịt. Đối tượng của bạn là để giữ cho gói của bạn còn sống. Làm điều này bằng cách ăn con mồi. Con mồi có xu hướng chạy khỏi những kẻ săn mồi, và cố gắng ở trong một đàn khác. Tất nhiên, gói của bạn sẽ nằm trên cùng một lĩnh vực như mọi gói khác , vì vậy cuộc thi sẽ cố gắng ăn chúng trước khi bạn có thể. Đừng để điều này làm bạn nản lòng, nếu không bạn sẽ chết đói.

Cách chơi

Tạo và gửi một chương trình dòng lệnh để chỉ đạo gói của bạn. Nó sẽ nhận thông tin trạng thái từ chương trình điều khiển trên STDIN và các lệnh đầu ra trên STDOUT. Các định dạng được phác thảo chi tiết dưới đây. Mỗi chương trình sẽ chỉ được thực hiện một lần và phải duy trì hoạt động cho đến khi không còn thành viên gói nào còn sống. Bạn sẽ cần phải đọc đầu vào khi nó đến, và trả lời nhanh chóng. Có thời gian chờ nghiêm ngặt là 200ms cho mỗi phản hồi. Nếu sau đó bạn chưa phản hồi, gói của bạn sẽ không nhận được hướng dẫn mới cho lượt hiện tại.

Nếu chương trình của bạn không thể được chạy bởi bộ điều khiển, nó sẽ không được coi là hợp lệ. Vui lòng bao gồm chuỗi dòng lệnh tôi sẽ cần sử dụng để chạy trình của bạn. Nếu có bất kỳ hướng dẫn đặc biệt nào (để thiết lập trình biên dịch, v.v.), vui lòng bao gồm chúng. Nếu tôi không thể làm cho nó hoạt động, tôi sẽ yêu cầu bạn giúp đỡ trong các ý kiến. Nếu bạn không trả lời, tôi sẽ không thể chấp nhận trình của bạn.

Giải đấu sẽ được tổ chức trên hệ thống Linux 64 bit. Hãy ghi nhớ điều này khi đưa ra bất kỳ hướng cần thiết nào.

Chi tiết

  • Vị trí và hướng của mỗi sinh vật ở dạng một cặp số dấu phẩy động chính xác kép (ví dụ double) tương ứng với chúng xytọa độ của chúng .

  • Mỗi sinh vật là một điểm. Điều này có nghĩa là chúng có thể chồng lên nhau và chiếm cùng một không gian. Bạn sẽ không bị va chạm sang một bên, và không có khái niệm va chạm với các sinh vật khác.

  • Hòn đảo là một hình vuông, 500 đơn vị sang một bên. Nếu bạn cố gắng mạo hiểm vượt ra ngoài giới hạn đó, bạn sẽ bị kẹp vào cạnh. Nguồn gốc {0,0}nằm ở phía trên bên trái, với xtăng dần về bên phải và ytăng xuống. Một lần nữa, bản đồ không bao bọc .

  • Trò chơi bắt đầu với động vật săn mồi 1500 + (packCount * 50) . Họ sẽ được tập trung tại trung tâm của hòn đảo, nhưng nhanh chóng quyết định bắt đầu di chuyển.

  • Các gói sẽ được sắp xếp trong một vòng tròn cách đều nhau xung quanh chu vi. Thứ tự gói được xáo trộn, vì vậy đừng tính bắt đầu ở một vị trí cụ thể.

  • Động vật săn mồi có thể nhìn thấy tất cả các động vật khác trong bán kính 30 đơn vị. Họ có thể di chuyển tối đa 6.0 đơn vị mỗi lượt.

  • Động vật ăn thịt có thể nhìn thấy tất cả các động vật khác trong bán kính 50 đơn vị Chúng có thể di chuyển tối đa 6,1 đơn vị mỗi lượt. Điều này có nghĩa là chúng có thể nhìn thấy con mồi trước khi được nhìn thấy và (hầu như) vượt xa chúng.

  • Động vật ăn thịt sống và chết theo mức độ đói của chúng . Nó bắt đầu từ 1000 , và giảm từng lượt một. Nếu, sau khi di chuyển, một kẻ săn mồi ở trong 1 đơn vị con mồi, nó sẽ tự động ăn nó. Việc này sẽ loại bỏ con mồi và khiến cơn đói của kẻ săn mồi thành 1000. Mỗi kẻ săn mồi chỉ có thể ăn một con mồi mỗi lượt. Nếu có nhiều hơn một trong phạm vi, nó sẽ ăn bất cứ thứ gì mà vòng lặp đạt được trước tiên (không nhất thiết là gần nhất). Một động vật ăn thịt chết nếu cơn đói của nó bằng không.

  • Gói bắt đầu với năm thành viên mỗi. Cứ sau 5000 lượt, tất cả các gói vẫn còn trong trò chơi sẽ sinh ra một thành viên mới. Nó sẽ được đặt trong phạm vi có thể nhìn thấy của một thành viên gói. Hãy chắc chắn rằng các mục của bạn có thể xử lý nhiều hơn năm thành viên.

  • Cứ sau 1000 lượt, con mồi sẽ sinh sản nhiều hơn. Số lượng con mồi mới sẽ là số lượng động vật ăn thịt sống trừ đi một con.

  • Động vật ăn thịt không thể tấn công các động vật ăn thịt khác. Chúng ăn con mồi khi chúng bắt nó. Đó là nó.

  • Thứ tự trong một lượt là:

    • Tất cả con mồi đưa ra quyết định
    • Tất cả các động vật ăn thịt đưa ra quyết định
    • Tất cả con mồi di chuyển
    • Tất cả động vật ăn thịt di chuyển / ăn
  • Thứ tự mỗi gói đưa ra quyết định / di chuyển của chúng sẽ được chọn ngẫu nhiên trên mỗi lượt.

Giao thức (chung)

Tất cả các thông tin liên lạc được thực hiện ở định dạng chuỗi US-ASCII. Các số được chuyển đổi thành chuỗi bằng Java Double.toString()hoặc Integer.toString(). Đầu ra của bạn phải được định dạng để Java có thể đọc được Double.valueOf(String)(bạn sẽ không xuất ra số nguyên). Để biết chi tiết về các định dạng có thể phân tích cú pháp, xem tài liệu choDouble . Tất cả các trường trên một dòng được phân tách bằng \tký tự chuẩn và dòng mới là \n. Toàn bộ chuỗi sẽ được kết thúc sẽ là một byte null \0.

Trong các ví dụ dưới đây, tôi đang sử dụng <>để đánh dấu các trường cho mục đích dễ đọc. Đây không phải là hiện diện trong chuỗi thực tế.

Giao thức (Đầu vào)

Chuỗi đầu vào khác nhau về chiều dài, tùy thuộc vào số lượng sinh vật được hiển thị cho gói của bạn. Nó có thể vượt quá 100k ký tự, vì vậy hãy chuẩn bị cho điều đó. Các thiết lập cơ bản là:

  • Dòng 0: Thông tin cơ bản về trò chơi. turnlà số lượt hiện tại và tổng số là tổng số con mồi và động vật ăn thịt còn lại trên sân. Đây là integerở dạng chuỗi.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Dòng 1: Id và mức độ đói duy nhất của thành viên gói của bạn. Chúng không được đưa ra theo cùng một thứ tự cho mỗi đầu vào. Sử dụng các id duy nhất để theo dõi các thành viên riêng lẻ, không theo thứ tự xuất hiện trong đầu vào. Một lần nữa, đây là integernhư chuỗi. Đối với một gói hai, điều này sẽ là:

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • Dòng 2: Vị trí thành viên gói của bạn, theo cùng thứ tự như được đưa ra trên dòng 1 . Đây là doubledưới dạng chuỗi:

    <x[0]>\t<y[0]>\t<x[1]>\t<y[1]>\n
    

Các dòng sau đây là khả năng hiển thị của mỗi thành viên gói, theo cùng thứ tự như được đưa ra trên dòng 1 . Chúng sẽ được đưa ra dưới dạng hai dòng trên mỗi thành viên.

Đầu tiên cho mỗi bao gồm các vị trí cho con mồi anh ta có thể nhìn thấy. Thứ hai là vị trí cho những kẻ săn mồi anh ta có thể nhìn thấy. Những địa điểm này không phải là duy nhất nói chung. Chẳng hạn, nếu hai thành viên gói có thể nhìn thấy cùng một con vật, nó sẽ nằm trong chuỗi của cả hai thành viên. Ngoài ra, các thành viên gói của riêng bạn sẽ được bao gồm. Nếu bạn muốn loại trừ chúng, bạn có thể muốn so sánh các vị trí với các thành viên của riêng bạn. Tất cả các vị trí ở doubledạng chuỗi.

Đối với mỗi thành viên sống:

<prey[0].x>\t<prey[0].y>\t<prey[1].x>\t<prey[1].y>\n
<predator[0].x>\t<predator[0].y>\t<predator[1].x>\t<predator[1].y>\n

Cuối cùng, ký tự cuối cùng sẽ là \0, ở đầu dòng tiếp theo.

Ngoại lệ: Nếu bạn nhận được đầu vào dead\0, gói của bạn đã chết. Kết thúc chương trình của bạn một cách duyên dáng, xin vui lòng. Bộ điều khiển sẽ tắt tất cả các quy trình sống khi đóng, nhưng tôi không muốn có các quy trình zombie ở khắp mọi nơi. Như một phép lịch sự, bạn có thể bao gồm thời gian chờ đầu vào. Chẳng hạn, lớp ví dụ của tôi kết thúc nếu nó không nhận được đầu vào trong 15 giây.

Giao thức (đầu ra)

Đầu ra rất đơn giản. Bạn sẽ đưa ra một cặp doublegiá trị cho mỗi thành viên gói sống. Chúng đại diện cho phong trào bạn muốn họ thực hiện lần lượt này. Ví dụ: nếu sinh vật của bạn hiện đang ở {100.0, 100.0}và bạn đưa cho chúng lệnh {-1.0, 1.0}, chúng sẽ chuyển đến {99.0, 101.0}. Tất cả các số sẽ nằm trên một dòng, cách nhau bởi tab.

Ví dụ: nếu bạn có 3 thành viên gói còn sống, đây sẽ là phản hồi hợp lệ:

1.0\t-1.0\t2.0\t-2.0\t3.0\t-3.0\0

Điều này sẽ di chuyển những sinh vật của bạn bằng {1.0,-1.0}, {2.0,-2.0}{3.0,-3.0}. Thứ tự giống như nhận được trong đầu vào. Đừng quên kết thúc \0!

Nếu bạn cho đầu vào không hợp lệ, kết quả xấu sẽ theo sau. Nếu bất kỳ số nào không thể được phân tích cú pháp thành a double, nó sẽ trở thành số không. Nếu toàn bộ chuỗi không thể được phân tích cú pháp, sẽ không có hướng dẫn mới nào được đưa ra và toàn bộ gói của bạn sẽ sử dụng các hướng dẫn từ lượt trước.

Tất cả các hướng sẽ được kẹp vào khoảng cách tối đa 6,1 đơn vị. Bạn có thể di chuyển chậm hơn thế này nếu bạn muốn. Ví dụ, {1, 0}sẽ di chuyển cho bạn một đơn vị. {6,8}(khoảng cách 10) sẽ chỉ di chuyển bạn 6,1 đơn vị và sẽ giảm xuống xung quanh {3.66, 4.88}. Hướng không đổi.

Quan trọng: Chương trình điều khiển đọc cả STDOUT STDERR của bạn . Nếu bạn ném một ngoại lệ và in ra STDERR, rất có thể tin nhắn đó sẽ ở dạng phản hồi hợp lệ. Cố gắng tránh làm điều này.

Chương trình điều khiển / Kiểm tra

Nguồn cho bộ điều khiển có thể được tìm thấy ở đây tại bitbucket.org . Bạn sẽ cần phải biên dịch nó trước khi chạy. Lớp chính là Gamevà tất cả các lớp nằm trong gói mặc định. Để chạy, bao gồm lệnh của mỗi gói như là một đối số riêng biệt. Chẳng hạn, nếu bạn muốn chạy Java ChaserPack và Python LazyPack.py, bạn có thể sử dụng:

java Game "java ChaserPack" "python LazyPack.py"

Trên bản đồ, con mồi xuất hiện màu xanh lá cây và động vật ăn thịt màu đỏ. Tuy nhiên, gói nào là gói đầu tiên được đưa ra làm đối số sẽ có màu xanh lam thay thế. Điều này nhằm phân biệt chúng dễ dàng hơn cho mục đích thử nghiệm. Động vật ăn thịt cũng sẽ nhấp nháy màu trắng trong năm khung khi chúng ăn.

Trò chơi sẽ tiến hành cho đến khi kẻ săn mồi cuối cùng chết đói, viết lên bàn điều khiển khi các sự kiện chết đói hoặc tuyệt chủng xảy ra. Khi trò chơi hoàn tất, điểm sẽ được trao cho mỗi gói. Nếu bạn không muốn xem các sự kiện chết đói / tuyệt chủng, bạn có thể sử dụng -silentđối số. Sau đó, nó sẽ chỉ xuất điểm số cuối cùng. Bạn phải vượt qua điều này như là đối số đầu tiên :

java Game -silent "java ChaserCat" "./someOtherPack"

Bao gồm là một gói Java bộ xương có tên GenericPack. Nó bao gồm các hoạt động đầu vào / đầu ra cơ bản cần thiết. Nó ở đó để đưa ra một ví dụ rõ ràng về cách phân tích và trả lời. Nếu bạn muốn thêm một mẫu bằng ngôn ngữ khác, hãy cho tôi biết.

Cũng bao gồm là một động vật ăn thịt dựa trên mẫu , ChaserPack. Nó sẽ không được bao gồm trong giải đấu, và chỉ được bao gồm cho mục đích thử nghiệm. Nó thực hiện khá tệ, do một lỗ hổng nhắm mục tiêu có chủ ý. Nếu bạn không thể đánh bại nó, hãy tiếp tục cố gắng.

Dưới đây là một ví dụ chạy chương trình điều khiển (bấm vào video). Chất lượng video không tốt (xin lỗi), nhưng bạn có thể cảm nhận được cách con mồi di chuyển. ( thận trọng: âm thanh )

ảnh chụp màn hình

Chấm điểm

Người chiến thắng sẽ được xác định theo giải đấu, giành được điểm trong mỗi vòng.

Mỗi vòng tiến hành cho đến khi tất cả động vật ăn thịt đã chết. Mỗi gói sẽ được tính điểm dựa trên thời điểm thành viên cuối cùng của nó chết vì đói. Sau đó, họ sẽ được chỉ định điểm dựa trên thứ tự. Điểm sẽ tích lũy trong mười vòng, và người chiến thắng là gói có tổng số điểm cao nhất.

Vị trí đầu tiên cho mỗi vòng sẽ nhận được 100 điểm. Đối với mỗi nơi sau đó, phần thưởng sẽ giảm 20% (làm tròn xuống). Điều này sẽ tiếp tục cho đến khi các điểm đạt đến không (sau vị trí thứ 17). Địa điểm 18+ sẽ không nhận được điểm nào cho vòng đấu. Gói người buộc sẽ nhận được điểm bằng nhau. Ví dụ:

1st : 100
2nd : 80
3rd : 64 (T)
3rd : 64 (T)
4th : 51
...
17th: 1
18th: 0
19th: 0

Điểm tối đa có thể trong suốt giải đấu là 1000, từ vị trí đầu tiên tất cả mười lần.

Nếu nhiều chương trình kết thúc giải đấu gắn liền với vị trí đầu tiên, một giải đấu vòng mười khác sẽ được tổ chức chỉ với các mục đầu tiên được gửi. Điều này sẽ tiếp tục cho đến khi một người chiến thắng xuất hiện.

Tôi sẽ cố gắng để chạy một giải đấu khoảng hàng tuần, hoặc khi đệ trình mới đến.

Quy tắc bổ sung (chơi công bằng!)

  • Bạn không thể đọc hoặc ghi vào bất kỳ tài nguyên bên ngoài. Vì bạn sẽ không gọi chương trình nhân đôi chương trình của mình, bất kỳ thông tin trạng thái nào cũng có thể được lưu trữ nội bộ.

  • Không can thiệp vào các quá trình / đệ trình khác. Điều này không có nghĩa là đừng cố gắng đánh cắp con mồi của chúng, vượt qua chúng, v.v. Điều đó có nghĩa là đừng can thiệp vào quá trình chạy. Đây là theo ý của tôi.

  • Thí sinh được giới hạn tối đa ba mục. Nếu bạn gửi thêm, tôi sẽ chỉ ghi được ba lần gửi đầu tiên. Nếu bạn muốn thu hồi nó, hãy xóa nó.

  • Các mục có thể không tồn tại chỉ để chống đỡ các mục khác. Mỗi người nên chơi để giành chiến thắng trên giá trị riêng của mình.

  • Chương trình của bạn có thể sinh ra tối đa một quá trình con tại một thời điểm ( tổng số con cháu, không trực tiếp). Dù bằng cách nào, hãy đảm bảo bạn không vượt quá thời gian chờ. Bạn không được gọi Gamechính nó theo bất kỳ cách nào.

Kết quả - 29 tháng 4 năm 2014

Dưới đây là kết quả của giải đấu mười vòng mới nhất:

Clairvoyant         : 1000
EcoCamels           : 752
Netcats             : 688
RubySpiders         : 436
RubyVultures        : 431
CivilizedBeasts     : 382
LazyPack            : 257

Các gói được gửi trước 09:00 EDT 2014/04/29 được bao gồm trong lần chạy này.

Bạn cũng có thể xem chi tiết cho mỗi vòng . Vì một số lý do, tôi quyết định đánh số vòng ngược, vì vậy nó bắt đầu bằng "vòng 10".

Cập nhật

23/03/2014: FGreg đã báo cáo một lỗi liên quan đến thời gian chờ (cảm ơn!). Một bản sửa lỗi đã được thực hiện, vì vậy những người kiểm tra sẽ muốn cập nhật mã chương trình kiểm soát của họ.


28
Tôi thích những câu hỏi của vua đồi!
Cruncher

2
@Manu Tôi đã viết các bot mẫu trên Windows 7 và đã thử nghiệm trên cả win và linux. Bạn có vấn đề gì với họ?
Geobits

2
Những câu hỏi về vua trên đồi khá tuyệt vời, và câu hỏi này chắc chắn rất thú vị. Tôi đã có hai gói khác nhau trong công việc bây giờ!
mackthehobbit

2
@githubphagocyte Tôi thực sự không muốn tiêu diệt một gói trong thời gian chờ đầu tiên, đơn giản vì tôi đã thấy các chương trình đơn giản hết thời gian một lần sau mỗi 40k + lượt hoặc tương tự. Tôi đã cam kết thay đổi tên trong bộ điều khiển. Lần lượt được gọi là lượt trong suốt chương trình, trừ khi tôi bỏ lỡ một nơi nào đó.
Geobits

2
@Geobits eh, điều đó tốt với tôi. Bạn biết đấy, điều này trông thực sự giống với một dự án nghiên cứu mà một trong những giáo sư vật lý của tôi đang thực hiện, điều mà tôi có thể giúp đỡ trong mùa hè. Tôi sẽ giải thích một chút về điều đó sau nếu tôi có thể.
krs013

Câu trả lời:


10

Nhà ngoại cảm

Mã được cập nhật để đối mặt với AbleDogs

Woo hoo! Cuối cùng cũng đánh bại Netcats đó! Tôi đã mở rộng mã hiện có (tín dụng cho Geobits!) Với một số sửa đổi nhỏ để tạo gói dự đoán trong tương lai này. Không có gì đánh bại những kẻ săn mồi biết con mồi sẽ di chuyển ở đâu!

Từ hai bài kiểm tra mà tôi đã thực hiện, gói của tôi luôn giành chiến thắng trước Netcats. Nhưng điều này sẽ không hoạt động tốt nếu không có gói khác, vì dự đoán vẫn thất bại nếu có quá nhiều con mồi khác ở gần.

Có lẽ tôi có thể bao gồm thủ thuật của CivilizedBeasts để giảm số lượng con mồi đáng kể trong vài nghìn lượt đầu tiên.

Thực hiện trong 5,21 phút
Clairvoyant (1): Biến 9270: Điểm 100
EcoCamel.pl (3): Biến 8118: Điểm 80
Netcats (0): Biến 6111: Điểm 64
RubyVultures.rb (5): Lần lượt 4249: Điểm 51
RubySpiders.rb (4): Biến 3495: Điểm 40
CivilizedBeasts (2): Biến 3176: Điểm 32
ChaserPack (6): Biến 2492: Điểm 25

Từ tên gói của tôi, bạn nên biết tôi sử dụng chiến lược nào = D

Chỉnh sửa :

  • Hệ thống quản lý gói được cập nhật để không đuổi theo con mồi giống nhau (và cũng cố gắng tìm kết quả phù hợp nhất!)
  • Cải thiện quá trình đi lang thang khi số lượng con mồi nhỏ (điều này rất quan trọng để giành chiến thắng!).
  • Cải thiện các trường hợp đặc biệt khi phiên bản trước chỉ bị kẹt ở góc.
  • Đã sửa lỗi thuật toán phát hiện động vật ăn thịt (hiện tại nó khá chính xác!)
  • Bao gồm các flock[ALIGN]yếu tố con mồi
  • Giữ một con mồi làm thú cưng nếu thức ăn khan hiếm
  • Tạo một den nơi gói sẽ đưa con mồi của chúng vào
  • Thu hút kẻ săn mồi gần đó để đuổi theo con mồi của chúng, chúng sẽ không thắng

Tôi đã đếm xem có bao nhiêu con mồi mỗi gói ăn và đây là kết quả:

Clairvoyant (1) tiêu thụ 916 con mồi trong 9270 lượt (0,099 con mồi / lượt)
EcoCamel.pl (3) đã tiêu thụ 73 con mồi trong 8118 lượt (0,009 con mồi / lượt)
Netcats (0) đã tiêu thụ 563 con mồi trong 611 lượt (0,092 con mồi / lượt)
RubyVultures.rb (5) đã tiêu thụ 77 con mồi trong 4249 lượt (0,008 con mồi / lượt)
RubySpiders.rb (4) đã tiêu thụ 293 con mồi trong 3495 lượt (0,084 con mồi / lượt)
CivilizedBeasts (2) tiêu thụ 10 con mồi trong 3176 lượt (0,003 con mồi / lượt)
ChaserPack (6) tiêu thụ 43 con mồi trong 2492 lượt (0,017 con mồi / lượt)

Gói của tôi rất hung dữ, và hầu hết số 916 tôi nghĩ rằng nó có được từ việc đánh cắp con mồi từ Netcats, giống như RubySpiders.

CivilizedBeasts không may bị mất do lạc đà trung tâm từ EcoCamel.

Và EcoCamel (với cơn đói quan trọng 500) khá hiệu quả, nó ăn vừa đủ để sống sót đến cuối cùng.

Cũng với Clairvoyant được cập nhật này, trò chơi chỉ đạt 10.000 lượt.

Mật mã:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;

public class Clairvoyant extends GenericPack {
    private static final double MAX_SPEED = 6.1;

    private TreeSet<Animal> foods = new TreeSet<Animal>(new AnimalComparator());
    private TreeSet<Animal> predators = new TreeSet<Animal>(new AnimalComparator());

    private XY abattoirCorner;
    private double abattoirRadius = 100;

    private MyMember[] myMembers = new MyMember[100];

    public class AnimalComparator implements Comparator<Animal>{

        @Override
        public int compare(Animal arg0, Animal arg1) {
            if(arg0.x < arg1.x){
                return -1;
            } else if (arg0.x > arg1.x){
                return 1;
            } else {
                if(arg0.y < arg1.y){
                    return -1;
                } else if(arg0.y > arg1.y){
                    return 1;
                } else {
                    return 0;
                }
            }
        }
    }

    public class MyMember extends Member{
        public XY target;
        public XY herdPos;
        public double herdRadius; 
        public boolean mayEat;
        public XY pos;
        public ArrayList<MyAnimal> closestPreys;
        public boolean outdated;

        public MyMember(int id) {
            super(id);
            this.pos = new XY(x, y);
            closestPreys = new ArrayList<MyAnimal>();
            mayEat = true;
            outdated = true;
        }

        public MyMember(Member member){
            super(member.id);
            this.pos = new XY(x, y);
            closestPreys = new ArrayList<MyAnimal>();
            mayEat = true;
            outdated = true;
        }

        public MyMember(Member member, Animal target){
            super(member.id);
            this.target = new XY(target.x, target.y);
            this.pos = new XY(x, y);
            closestPreys = new ArrayList<MyAnimal>();
            mayEat = true;
            outdated = true;
        }

        public void reset(Member me){
            x = me.x;
            y = me.y;
            pos = new XY(x, y);
            closestPreys.clear();
            mayEat = true;
            outdated = true;
        }
    }

    public class MyAnimal extends Animal{
        public ArrayList<MyMember> chasers;
        public XY pos;
        public boolean resolved;

        public MyAnimal(double x, double y){
            super(x, y);
            pos = new XY(x, y);
            chasers = new ArrayList<MyMember>();
            resolved = false;
        }

        public MyAnimal(Animal ani){
            super(ani.x, ani.y);
            pos = new XY(x, y);
            chasers = new ArrayList<MyMember>();
            resolved = false;
        }
    }

    public static void main(String[] args){
        new Clairvoyant().run();
    }

    public Clairvoyant(){
        for(int i=0; i<100; i++){
            nextIdx[i] = 0;
        }
        int cornerIdx = (int)Math.floor(Math.random()*4);
        switch (cornerIdx){
        case 0: abattoirCorner = new XY(0,0); break;
        case 1: abattoirCorner = new XY(500,0); break;
        case 2: abattoirCorner = new XY(500,500); break;
        case 3: abattoirCorner = new XY(0,500); break;
        }
    }

    @Override
    public void respond(){
        updateData();
        goToTarget();
    }

    private void updateData(){
        for(int i=0; i<100; i++){
            if(myMembers[i]!=null){
                myMembers[i].pos = null;
            }
        }
        foods.clear();
        predators.clear();
        for(Member me: members){
            foods.addAll(me.foods);
            predators.addAll(me.others);
            predators.add(new Animal(me.x, me.y));
            if(myMembers[me.id] != null){
                myMembers[me.id].reset(me);
            } else {
                myMembers[me.id] = new MyMember(me);
            }
        }
        for(int i=0; i<100; i++){
            if(myMembers[i]!=null && myMembers[i].pos == null){
                myMembers[i] = null;
            }
        }

        TreeSet<MyAnimal> closestPreys = new TreeSet<MyAnimal>(new AnimalComparator());
        for(int i=0; i<100; i++){
            if (myMembers[i]==null) continue;
            MyMember me = myMembers[i];
            ArrayList<Animal> animals = findClosest(foods, me.pos, members.size());
            boolean first = true;
            for(Animal ani: animals){
                MyAnimal myAni = new MyAnimal(ani);
                if(closestPreys.contains(ani)){
                    myAni = closestPreys.ceiling(myAni);
                } else {
                    closestPreys.add(myAni);
                }
                if(first){
                    myAni.chasers.add(me);
                    first = false;
                }
                me.closestPreys.add(myAni);
            }
        }
        performMatching();
        for(int i=0; i<100; i++){
            if (myMembers[i] == null) continue;
            MyMember me = myMembers[i];
            if(!me.outdated) continue;
            if(me.closestPreys.size() == 0) continue;
            MyAnimal closestPrey = me.closestPreys.get(0);
            if(closestPrey.resolved) continue;
            if(closestPrey.chasers.size() > 1){
                MyMember hungriest = me;
                MyMember closest = me;
                for(MyMember otherMe: closestPrey.chasers){
                    if(sqDist(closestPrey.pos, otherMe) < sqDist(closestPrey.pos, closest)){
                        closest = otherMe;
                    }
                    if(otherMe.hunger < hungriest.hunger){
                        hungriest = otherMe;
                    }
                }
                if(hungriest.hunger > 200){ // Nobody's critically hungry, the closest takes the prey
                    closest.target = closestPrey.pos;
                    closest.mayEat = true;
                    closest.herdPos = abattoirCorner;
                    closest.herdRadius = abattoirRadius;
                    closest.outdated = false;
                } else {
                    if(hungriest == closest){
                        closest.target = closestPrey.pos;
                        closest.mayEat = true;
                        closest.herdPos = abattoirCorner;
                        closest.herdRadius = abattoirRadius;
                        closest.outdated = false;
                    } else {
                        closest.target = closestPrey.pos;
                        closest.mayEat = false;
                        closest.herdPos = hungriest.pos;
                        closest.herdRadius = 0;
                        closest.outdated = false;
                        hungriest.target = closestPrey.pos;
                        hungriest.mayEat = true;
                        hungriest.herdPos = abattoirCorner;
                        hungriest.herdRadius = 10;
                        hungriest.outdated = false;
                    }
                }
                closestPrey.resolved = true;
            } else {
                me.target = closestPrey.pos;
                me.herdPos = abattoirCorner;
                me.herdRadius = abattoirRadius;
                me.mayEat = true;
                me.outdated = false;
            }
        }
        for(int i=0; i<100; i++){
            if (myMembers[i] == null) continue;
            MyMember me = myMembers[i];
            if(me.outdated){
                me.target = null;
                me.outdated = false;
            }
        }
    }

    private void goToTarget(){
        for(Member me: members){
            MyMember mem = myMembers[me.id];
            if(mem.target == null){
                wander(me, 2*(me.id%2)-1);
                continue;
            } else {
                nextIdx[me.id] = 0;
                XY[] nearestHostile = new XY[100];
                for(Animal other: me.others){
                    XY otherPos = new XY(other.x, other.y);
                    boolean isMember = false;
                    for(Member otherMember: members){
                        if(other.x==otherMember.x && other.y==otherMember.y){
                            isMember = true;
                            break;
                        }
                    }
                    if(!isMember){
                        if(nearestHostile[me.id] == null || XY.sqDistance(mem.pos, otherPos) < XY.sqDistance(mem.pos,  nearestHostile[me.id])){
                            nearestHostile[me.id] = otherPos;
                        }
                    }
                }

                // Go towards the target by predicting its next position
                XY target = predictNextPos(mem.target, me);

                me.dx = (target.x - me.x);
                me.dy = (target.y - me.y); 

                // Try to herd the target to our abattoir if this member is not too hungry
                // and if there is no other hostile predator who is closer to the target than us
                // This will make the other hostile predator to keep targeting this target, while
                // it is certain that we will get the target.
                // This is a win situation for us, since it will make the other predator wasting his turn.
                if((me.hunger <= 200 && XY.sqDistance(mem.target, mem.pos) > 400) || me.hunger <= 50 ||
                        (nearestHostile[me.id] != null && Math.sqrt(XY.sqDistance(mem.target, nearestHostile[me.id])) < Math.sqrt(XY.sqDistance(mem.target, mem.pos)))){
                    continue;
                }

                // Don't eat if not threatened nor hungry
                if(me.hunger > 50 && (nearestHostile[me.id] == null ||
                        Math.sqrt(XY.sqDistance(mem.target, nearestHostile[me.id])) > Math.sqrt(XY.sqDistance(mem.target, mem.pos)) + 6)){
                    mem.mayEat = false;
                }

                // Herd to abattoir corner
                double distFromHerd = Math.sqrt(XY.sqDistance(target, mem.herdPos));
                XY oppositeAbattoirCorner = new XY(500-abattoirCorner.x, 500-abattoirCorner.y);
                double distFromOpposite = Math.sqrt(XY.sqDistance(target, oppositeAbattoirCorner));
                if((me.dx*me.dx+me.dy*me.dy > 64 && distFromHerd > mem.herdRadius && distFromOpposite > abattoirRadius)
                        || (preyCount < 5*predCount)){
                    double herdDistance = 4*(distFromHerd-mem.herdRadius)/(Island.SIZE-mem.herdRadius);
                    if(!mem.mayEat) herdDistance = 4;
                    XY gradient = target.minus(abattoirCorner);
                    me.dx += gradient.x*herdDistance/distFromHerd;
                    me.dy += gradient.y*herdDistance/distFromHerd;
                }
            }
        }
    }

    private void performMatching(){
        for(int i=0; i<100; i++){
            if (myMembers[i] == null) continue;
            MyMember me = myMembers[i];
            if(me.closestPreys.size()==0) continue;
            MyAnimal closestPrey = me.closestPreys.get(0);
            if(closestPrey.chasers.size() > 1){
                resolveConflict(closestPrey);
            }
        }
    }

    private void resolveConflict(MyAnimal prey){
        ArrayList<MyMember> chasers = prey.chasers;
        MyMember winner = null;
        double closestDist = Double.MAX_VALUE;
        for(MyMember me: chasers){
            if(sqDist(prey.pos, me) < closestDist){
                closestDist = sqDist(prey.pos, me);
                winner = me;
            }
        }
        for(int i=chasers.size()-1; i>=0; i--){
            MyMember me = chasers.get(i);
            if(me!=winner){
                me.closestPreys.get(0).chasers.remove(me);
                me.closestPreys.add(me.closestPreys.remove(0));
                me.closestPreys.get(0).chasers.add(me);
            }
        }
    }

    private Animal findClosest(Collection<Animal> preys, XY me){
        Animal target = null;
        double cDist = Double.MAX_VALUE;
        double x, y, sqDist;
        for (Animal food : preys) {
            x = food.x - me.x;
            y = food.y - me.y;
            sqDist = x * x + y * y + Double.MIN_NORMAL;
            if (sqDist < cDist) {
                cDist = sqDist;
                target = food;
            }
        }
        return target;
    }

    private ArrayList<Animal> findClosest(Collection<Animal> preys, XY me, int num){
        ArrayList<Animal> result = new ArrayList<Animal>();
        for(Animal food: preys){
            int addIdx = -1;
            for(int i=0; i<num && i<result.size(); i++){
                Animal regFood = result.get(i);
                if(sqDist(me, food) < sqDist(me, regFood)){
                    addIdx = i;
                    break;
                }
            }
            if(addIdx == -1){
                result.add(food);
            } else {
                result.add(addIdx, food);
            }
            if(result.size() > num){
                result.remove(num);
            }
        }
        return result;
    }

    private Member findClosestToTarget(Collection<Member> members, Animal target){
        Member member = null;
        double cDist = Double.MAX_VALUE;
        double x, y, sqDist;
        for (Member me : members) {
            x = me.x - target.x;
            y = me.y - target.y;
            sqDist = x * x + y * y + Double.MIN_NORMAL;
            if (sqDist < cDist) {
                cDist = sqDist;
                member = me;
            }
        }
        return member;
    }

    private static final XY[] CHECKPOINTS = new XY[]{
        new XY(49.5,49.5),
        new XY(450.5,49.5),
        new XY(450.5,100),
        new XY(49.5,100),
        new XY(49.5,150),
        new XY(450.5,150),
        new XY(450.5,200),
        new XY(49.5,200),
        new XY(49.5,250),
        new XY(450.5,250),
        new XY(450.5,300),
        new XY(49.5,300),
        new XY(49.5,350),
        new XY(450.5,350),
        new XY(450.5,400),
        new XY(49.5,400),
        new XY(49.5,450.5),
        new XY(450.5,450.5)};
    private int[] nextIdx = new int[100];

    private int advanceIdx(int idx, int sign, int amount){
        return sign*(((Math.abs(idx)+CHECKPOINTS.length-1+sign*amount) % CHECKPOINTS.length) + 1);
    }

    private void wander(Member me, int sign) {
        if(preyCount > 20*predCount){
            if (me.dx == 0 && me.dy == 0) {
                me.dx = 250 - me.x;
                me.dy = 250 - me.y;
                return;
            }

            double lx, ly, px, py;
            lx = me.dx / 4;
            ly = me.dy / 4;
            boolean dir = Math.random() < 0.5 ? true : false;
            px = dir ? ly : -ly;
            py = dir ? -lx : lx;

            me.dx += px;
            me.dy += py;
        } else {
            if(nextIdx[me.id]==0){
                XY farthest = new XY(2000,2000);
                int farthestIdx = -1;
                for(int i=0; i<CHECKPOINTS.length; i++){
                    if(sign*sqDist(CHECKPOINTS[i], me) > sign*sqDist(farthest, me)){
                        farthest = CHECKPOINTS[i];
                        farthestIdx = i+1;
                    }
                }
                nextIdx[me.id] = farthestIdx*sign;
                for(Member mem: members){
                    if(mem.id == me.id) continue;
                    if(nextIdx[mem.id]==nextIdx[me.id]){
                        nextIdx[me.id] = advanceIdx(nextIdx[me.id], sign, 5); 
                    }
                }
            }
            if(sqDist(CHECKPOINTS[Math.abs(nextIdx[me.id])-1],me) < 1){
                nextIdx[me.id] = advanceIdx(nextIdx[me.id], sign, 1);
            }
            me.setDirection(CHECKPOINTS[Math.abs(nextIdx[me.id])-1].x-me.x,
                    CHECKPOINTS[Math.abs(nextIdx[me.id])-1].y-me.y);
        }
    }

    private double sqDist(XY me, Animal target){
        double dx = me.x-target.x;
        double dy = me.y-target.y;
        return dx*dx + dy*dy + Double.MIN_NORMAL;
    }

    private double sqDist(XY me, Member target){
        double dx = me.x-target.x;
        double dy = me.y-target.y;
        return dx*dx + dy*dy + Double.MIN_NORMAL;
    }

    private double sqDist(Animal target, Member me){
        double dx = me.x-target.x;
        double dy = me.y-target.y;
        return dx*dx + dy*dy + Double.MIN_NORMAL;
    }

    private List<Animal> getNeighbors(double radius, XY pos, Collection<Animal> candidates) {
        List<Animal> neighbors = new ArrayList<Animal>();
        for(Animal neighbor: candidates){
            if(sqDist(pos, neighbor) < radius * radius){
                neighbors.add(neighbor);
            }
        }
        return neighbors;
    }

    final double[] weights = { 1, 1, 0.96, 2, 4 };
    double weightSum;

    static final int ALIGN = 0;
    static final int SEPARATE = 1;
    static final int COHESION = 2;
    static final int FLEE = 3;
    static final int WALL = 4;
    static final int VISIBLE = 30;
    static final int VISIBLE_PRED = 50;

    private HashMap<Member, List<Animal>> prevPreys = new HashMap<Member, List<Animal>>();

    private XY matchPreys(List<Animal> prevs, List<Animal> curs, XY prey){
        XY result = new XY();
        double sqDist = 0;
        Animal candidate;
        XY otherPos;
        for(Animal otherPrey: curs){
            otherPos = new XY(otherPrey.x, otherPrey.y);
            sqDist = XY.sqDistance(prey, otherPos);
            if(sqDist > VISIBLE * VISIBLE)
                continue;
            candidate = findClosest(getNeighbors(6, otherPos, prevs), prey);
            if(candidate == null){
                return null;
            }
            result.add(otherPos.x-candidate.x, otherPos.y-candidate.y);
        }
        return result;
    }

    private XY predictNextPos(XY prey, Member me) {
        List<Animal> preys = getNeighbors(VISIBLE_PRED, prey, foods);
        List<Animal> preds = getNeighbors(VISIBLE, prey, predators);

        XY flock[] = new XY[weights.length];
        for (int i = 0; i < weights.length; i++)
            flock[i] = new XY();

        double dx, dy, dist, sqDist;
        for (Animal otherPrey : preys) {
            sqDist = XY.sqDistance(prey, new XY(otherPrey.x, otherPrey.y));
            if(sqDist > VISIBLE * VISIBLE)
                continue;
            dx = otherPrey.x - prey.x;
            dy = otherPrey.y - prey.y;
            flock[COHESION].add(dx*sqDist, dy*sqDist);
            flock[SEPARATE].add(-dx*(1d/sqDist), -dy*(1d/sqDist));
            flock[ALIGN].add(new XY(prey.x-me.x,prey.y-me.y));
        }

        if(sqDist(prey, me) < 400){
            if(prevPreys.get(me) == null){
                prevPreys.put(me, preys);
            } else {
                XY flockAlign = matchPreys(prevPreys.get(me), preys, prey);
                if(flockAlign == null){
                    prevPreys.put(me , null);
                } else {
                    flock[ALIGN] = flockAlign;
                    prevPreys.put(me, preys);
                }
            }
        }

        flock[ALIGN].unitize().multiply(5);
        flock[COHESION].unitize().multiply(5);
        flock[SEPARATE].unitize().multiply(5);

        for (Animal predator : preds){
            flock[FLEE].add(prey.x-predator.x, prey.y-predator.y);
        }

        dx = Island.CENTER.x - prey.x;
        dy = Island.CENTER.y - prey.y;
        dist = Math.max(Math.abs(dx), Math.abs(dy));
        if(dist > 240){
            flock[WALL].x = dx * dist;
            flock[WALL].y = dy * dist;
            flock[WALL].unitize().multiply(5);
        }

        XY vec = new XY();
        vec.x = 0;
        vec.y = 0;
        for (int i = 0; i < flock.length; i++) {
            flock[i].multiply(weights[i]);
            vec.add(flock[i]);
        }
        limitSpeed(vec);
        return vec.add(prey);
    }

    private XY limitSpeed(XY move) {
        if (move.x*move.x+move.y*move.y > MAX_SPEED*MAX_SPEED)
            move.unitize().multiply(MAX_SPEED);
        return move;
    }
}

1
Có vẻ rất tốt, bạn thực sự tốt hơn netcats trong trò chơi của tôi. Nhưng tôi ghét rằng tôi không thể điều hành những kẻ săn mồi khác vì những con thú của tôi làm một công việc thực sự tồi tệ trong chỉ số của bạn (trong khi evilcamel làm quá tốt). Có lẽ tôi phải cố gắng cài đặt một trình biên dịch perl hoặc như vậy.
Herjan

Vâng, tôi nghĩ rằng phương pháp của bạn không hoạt động nếu có một kẻ săn mồi ở giữa, như được thảo luận trong câu trả lời của bạn. Tôi đã thử thực hiện một phiên bản khác tương tự như của bạn. Nó có thể thay đổi đội hình tùy thuộc vào số lượng động vật ăn thịt có sẵn, vì vậy thật thú vị khi xem, mặc dù không tốt hơn nhiều so với của bạn.
justhalf

Có, chiến lược của tôi có thể được nâng cấp theo nhiều cách như các đội hình khác với số lượng khác với các thành viên vì các quái thú của tôi đã bị tiêu diệt với <4 kẻ săn mồi. Hoặc những nơi ngẫu nhiên để thu thập (thay vì chỉ ở giữa) chẳng hạn. Nhưng tôi quá lười để thực hiện điều đó (bây giờ). Và nó sẽ không bao giờ tốt như cái này vì nếu con mồi bị hạ thấp thì chiến thuật của tôi không hoạt động. Đó là khi bạn cần một con thú giống như bạn (bạn đã đề cập để bắt đầu với chiến thuật của tôi và khi con mồi xuống thấp để sử dụng chiến thuật này). Vì vậy, tôi đoán bạn đã nghĩ điều này thông qua.
Herjan

Tôi đang ở một thử thách khác ngay bây giờ và GeoBits dường như đã mất hứng thú với thử thách này, vì vậy tôi sẽ để nó ngồi một lúc trừ khi kết quả được cập nhật. Tôi có ý tưởng cho một vài bài nộp khác, vì vậy tôi hy vọng thử thách này sẽ được duy trì. Tôi sẽ xem xét cập nhật của bạn, tất nhiên.

15

Netcats

Đây là một gói để giúp các bạn bắt đầu. Nó mở rộng GenericPacklớp bao gồm chương trình điều khiển. Nó đã được cải thiện kể từ khi đăng bài gốc, và không còn chết đói với một đàn thưa thớt nữa.

Netcats sử dụng đội hình lưới hình vee để bẫy con mồi trong góc, nơi chúng có thể ăn chúng lúc rảnh rỗi. Mạng được hình thành với một thành viên "đứng đầu" tại trung tâm. Một khi người đứng đầu ăn, nó đổi chỗ với thành viên gói đói nhất, vì người đứng đầu thường là người đầu tiên có cơ hội ăn.

Mạng lưới bắt đầu khá nhỏ, nhưng mở rộng khi đàn nhỏ hơn để đánh bắt đồng ruộng hiệu quả hơn.

Nếu không có con mồi có thể nhìn thấy, đội hình mở rộng thành một mô hình tìm kiếm ngây thơ bao trùm hầu hết hòn đảo.

Khi gói xuống còn hai thành viên, mạng sẽ không hoạt động. Tại thời điểm đó, mỗi người đi theo cách riêng của mình, tham lam ăn thứ gần nhất mà nó có thể tìm thấy và đi bộ bán ngẫu nhiên nếu không.

Phiên bản này tồn tại tốt hơn nhiều so với Netcats ngây thơ được thấy trong video được liên kết trong câu hỏi.

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class Netcats extends GenericPack {

    boolean seeking;
    Member head = null;
    Set<Animal> foods;

    public static void main(String[] args) {
        new Netcats().run();
    }

    @Override
    public void respond() {
        if (foods == null)
            foods = new HashSet<Animal>();
        else
            foods.clear();
        for (Member member : members)
            foods.addAll(member.foods);

        if (members.size() < 3) {
            soloRun();
        } else {
            head = setHead();
            setHeadVec();
            for (int i = 1; i < members.size(); i++) {
                setMemberVec(i);
            }
        }
    }

    Member setHead() {
        if (!members.contains(head))
            return members.get(0);

        Member hungry = head;
        int idx = 0;
        for (int i = 0; i < members.size(); i++) {
            Member me = members.get(i);
            if (me.hunger < hungry.hunger) {
                hungry = me;
                idx = i;
            }
        }

        if (hungry != head) {
            members.remove(hungry);
            members.remove(head);
            members.add(0, hungry);
            members.add(idx, head);
            return hungry;
        }
        return head;
    }

    void setHeadVec() {
        double x = 0, y = 0;

        Collection<Animal> yummy = getFoods(head);

        seeking = false;
        if (yummy.size() == 0) {
            scoutHead();
            return;
        }

        if (members.size() == 1)
            if (findFood(head))
                return;

        for (Animal food : yummy) {
            x += food.x - head.x;
            y += food.y - head.y;
        }
        x *= 10000000;
        y *= 10000000;

        head.dx = x;
        head.dy = y;
        if (members.size() > 1)
            limitSpeed(head, MAX_SPEED * HEAD_MULT);
    }

    void scoutHead() {
        seeking = true;
        head.dy = 250 - head.y;
        head.dx = round % 80 < 40 ? -head.x : 500 - head.x;
    }

    void setMemberVec(int idx) {
        Member me = members.get(idx);
        Member leader;
        leader = idx < 3 ? members.get(0) : members.get(idx - 2);
        if (findFood(me))
            return;

        double lx, ly, px, py, tx, ty, dist;
        lx = -leader.dx;
        ly = -leader.dy;
        dist = Math.sqrt(lx * lx + ly * ly) + Double.MIN_NORMAL;
        lx /= dist;
        ly /= dist;
        px = idx % 2 == 0 ? ly : -ly;
        py = idx % 2 == 0 ? -lx : lx;

        tx = leader.x + leader.dx;
        ty = leader.y + leader.dy;
        int xtrack = seeking ? COMB : preyCount > 400 ? ASIDE : MID_SIDE;
        tx += lx * BEHIND + px * xtrack;
        ty += ly * BEHIND + py * xtrack;

        me.dx = tx - me.x;
        me.dy = ty - me.y;
        limitSpeed(me, MAX_SPEED * (idx < 3 ? MID_MULT : 1));
    }

    Collection<Animal> getFoods(Member me) {
        return me.foods.size() == 0 ? foods : me.foods;
    }

    boolean findFood(Member me) {
        if (me.hunger > 500)
            return false;

        Collection<Animal> yummy = getFoods(me);
        if (yummy.size() == 0)
            return false;

        double x, y, sqDist, cDist = 10 * 10;
        Animal target = null;
        for (Animal food : me.foods) {
            x = food.x - me.x;
            y = food.y - me.y;
            sqDist = x * x + y * y + Double.MIN_NORMAL;
            if (sqDist < cDist) {
                cDist = sqDist;
                target = food;
            }
        }

        if (target == null)
            return false;

        if (cDist < 5 * 5 || me.hunger < 200) {
            me.dx = (target.x - me.x) * 10000000d;
            me.dy = (target.y - me.y) * 10000000d;
            return true;
        }
        return false;
    }

    void soloRun() {
        double x, y, sqDist, cDist;
        for (Member me : members) {
            Collection<Animal> yummy = getFoods(me);
            if (yummy.size() == 0) {
                wander(me);
                continue;
            }

            Animal target = null;
            cDist = Double.MAX_VALUE;
            for (Animal food : yummy) {
                x = food.x - me.x;
                y = food.y - me.y;
                sqDist = x * x + y * y + Double.MIN_NORMAL;
                if (sqDist < cDist) {
                    cDist = sqDist;
                    target = food;
                }
            }

            me.dx = (target.x - me.x) * 100000d;
            me.dy = (target.y - me.y) * 100000d;
        }
    }

    void wander(Member me) {
        if (me.dx == 0 && me.dy == 0) {
            me.dx = 250 - me.x;
            me.dy = 250 - me.y;
            return;
        }

        double lx, ly, px, py;
        lx = me.dx / 4;
        ly = me.dy / 4;
        boolean dir = Math.random() < 0.5 ? true : false;
        px = dir ? ly : -ly;
        py = dir ? -lx : lx;

        me.dx += px;
        me.dy += py;
    }

    void limitSpeed(Member me, double max) {
        double x = me.dx, y = me.dy;
        double dist = Math.sqrt(x * x + y * y) + Double.MIN_NORMAL;
        if (dist > max) {
            x = (x / dist) * max;
            y = (y / dist) * max;
        }
        me.dx = x;
        me.dy = y;
    }

    final static double MAX_SPEED = 6.1;
    final static double HEAD_MULT = 0.85;
    final static double MID_MULT = 0.92;
    final static int BEHIND = -25;
    final static int ASIDE = 15;
    final static int MID_SIDE = 30;
    final static int COMB = 150;
}

11

Nhện Ruby

Vì đôi khi ít hơn là nhiều và nhiều giải pháp có thể sẽ cố gắng dồn con mồi bằng mọi cách ...

Tôi nghĩ gói của tôi chỉ có thể tách ra và chờ người khác thực hiện công việc.

gets
print "3.0\t3.0\t3.0\t-3.0\t-3.0\t-3.0\t-3.0\t3.0\t0.0\t0.0\0"
STDOUT.flush

Hãy cẩn thận: Nó không thực sự duy trì hoạt động, nó cũng không đọc đầu vào vì nó cũng không phản hồi nhanh. Tuy nhiên, vì nó hoạt động tốt với bộ điều khiển, tôi hy vọng nó đủ điều kiện mà không cần điều chỉnh thêm.


4
+1 dung dịch ký sinh trùng đầu tiên. Tôi nghĩ loại câu trả lời này sẽ thúc đẩy chất lượng của các câu trả lời khác bằng cách loại bỏ dần các sơ hở ...
trichoplax

@githubphagocyte Tôi đã có một ký sinh trùng thông minh hơn trong tâm trí nhưng điều này hiệu quả hơn về thời gian sống / dòng mã. Tôi hy vọng tôi sẽ tìm thấy thời gian để thực hiện nó.
Legat

Có lẽ @Synthetica đang mã hóa ý tưởng của tôi ngay bây giờ. Hoặc nếu ý tưởng của anh ta là một ý tưởng khác, chúng ta có thể sẽ sớm có nhiều ký sinh trùng hơn là thợ săn;)
Legat

1
@githubphagocyte chúng tôi được phép thực hiện ba mục, vì vậy tôi sẽ đăng một gói khác sau khi nó sẵn sàng. Tuy nhiên, tôi thấy thú vị rằng cái này đã được mã hóa trong thời gian đó và nó có thể chứng minh hiệu quả hơn. Nó tận dụng lợi thế của Netcats rất tốt và nó thực sự vượt xa gói thợ săn đầu tiên của tôi.
Legat

3
Điều này có thể nhập nguyên trạng, ngay cả khi tôi mất một giây để tìm hiểu tại sao. Có vẻ để làm tốt hơn Netcats bạn thêm vào (có ý nghĩa). +1 từ tôi, hãy xem loại thợ săn nào xuất hiện để tránh các góc :)
Geobits

11

Văn minh

Cuối cùng, thời gian để thể hiện những con thú của tôi!

Giống của tôi nghĩ rằng việc săn bắn có phần nguyên thủy nên chúng phối hợp với nhau trong một nhóm 4 người và vì vậy chúng từ bỏ đồng minh thứ 5 của chúng, bởi vì: ít động vật ăn thịt hơn = nhiều con mồi hơn cho bản thân. Những gì chúng cơ bản làm là những gì con người làm, chúng bắt mồi và chăm sóc gia súc tốt;)

public class CivilizedBeasts extends GenericPack{

    private static int TL = 0, TR = 0, BL = 0, BR = 0; // TopLeft/BotRight
    private static int teamSize = 0, turnsWaiting = 0, turnsToWait = 20;

    private boolean out = true;
    private double maxSpeed = 6.1, mapSize = 500;

    public CivilizedBeasts(){
    }

    @Override
    public void respond(){
        if(teamSize > members.size()){

            Member check = getMemberById(TL);
            totalLoop:
            if(check == null){
                for (Member member : members) {
                    if(member.id != TR && member.id != BL && member.id != BR){
                        TL = member.id;
                        break totalLoop;
                    }
                }

                TL = 0;
            }

            check = getMemberById(TR);
            totalLoop:
            if(check == null){
                for (Member member : members) {
                    if(member.id != TL && member.id != BL && member.id != BR){
                        TR = member.id;
                        break totalLoop;
                    }
                }

                TR = 0;
            }

            check = getMemberById(BL);
            totalLoop:
            if(check == null){
                for (Member member : members) {
                    if(member.id != TL && member.id != TR && member.id != BR){
                        BL = member.id;
                        break totalLoop;
                    }
                }

                BL = 0;
            }

            check = getMemberById(BR);
            totalLoop:
            if(check == null){
                for(Member member : members) {
                    if(member.id != TL && member.id != TR && member.id != BL){
                        BR = member.id;
                        break totalLoop;
                    }
                }

                BR = 0;
            }
        }else if(teamSize < members.size()){
            for(Member member : members) {
                if(member.id != TL && member.id != TR && member.id != BL && member.id != BR){
                    if(TL == 0)
                        TL = member.id;
                    else if(TR == 0)
                        TR = member.id;
                    else if(BL == 0)
                        BL = member.id;
                    else if(BR == 0)
                        BR = member.id;
                }
            }
        }

        teamSize = members.size();

        double border = 1;
        double x, y;
        boolean reached = true;

        double distance = 16.3;

        for (Member member : members) {
            boolean doesNotCount = false;
            x = 0; y = 0;
            if(member.id == TL){
                if(out){
                    x = -(member.x - border);
                    y = -(member.y - border);
                }else{
                    x = ((mapSize/2 - distance) - member.x);
                    y = ((mapSize/2 - distance) - member.y);
                }
            }else if(member.id == TR){
                if(out){
                    x = (mapSize - member.x - border);
                    y = -(member.y - border);
                }else{
                    x = ((mapSize/2 + distance) - member.x);
                    y = ((mapSize/2 - distance) - member.y);
                }
            }else if(member.id == BL){
                if(out){
                    x = -(member.x - border);
                    y = (mapSize - member.y - border);
                }else{
                    x = ((mapSize/2 - distance) - member.x);
                    y = ((mapSize/2 + distance) - member.y);
                }
            }else if(member.id == BR){
                if(out){
                    x = (mapSize - member.x - border);
                    y = (mapSize - member.y - border);
                }else{
                    x = ((mapSize/2 + distance) - member.x);
                    y = ((mapSize/2 + distance) - member.y);
                }
            }else{
                double dist = 50, temp = 0;
                int index = -1;
                for(int i = 0; i < member.foods.size(); i++){
                    temp = (Math.abs(member.foods.get(i).x - member.x)+Math.abs(member.foods.get(i).y - member.y));
                    if(temp < dist){
                        dist = temp;
                        index = i;
                    }
                }
                if(index != -1){
                    x = (member.foods.get(index).x - member.x);
                    y = (member.foods.get(index).y - member.y);
                }
                doesNotCount = true;
            }

            if(!doesNotCount && Math.abs(x)+Math.abs(y) > maxSpeed)
                reached = false;
            member.setDirection(x,y);
        }

        if(reached){
            if(!out){ // in the middle.
                if(teamSize < 4){
                    int temp = TL;
                    TL = BR;
                    BR = temp;
                    temp = TR;
                    TR = BL;
                    BL = temp;
                    out = true;
                }else{
                    turnsWaiting++;
                }
            }else // no need to wait in the corners
                out = false;

            if(turnsWaiting >= turnsToWait){
                turnsToWait = 15;
                out = true;
                turnsWaiting = 0;
            }

        }

    }

    public static void main(String[] args){
        new CivilizedBeasts().run();
    }
}

Bộ ngực của tôi trở nên khá khó khăn để tồn tại với ít hơn 200 con mồi trong lượt + -12.000 chỉ với Netcats của kẻ thù trong trò chơi. Bạn sẽ hài lòng với giống chó này vì nó thực sự nuốt chửng một lượng lớn con mồi với tốc độ như không có giống chó nào khác có thể (không phải là những người giết mổ nhanh và lớn mang lại chiến thắng, nhưng nó ảnh hưởng đến thời gian (cả) một vòng đáng kể).


3
Nếu bằng cách " chăm sóc chúng tốt ", bạn có nghĩa là " liên tục bầy chúng đến giữa và giết / ăn chúng ", thì đúng vậy, chúng làm tốt điều đó. +1
Geobits

Thật buồn cười, với phiên bản không biến đổi (nguyên bản) của Evil Camels, các chiến thuật văn minh hoàn toàn không hiệu quả vì 'trung tâm lạc đà'.
dùng2846289

1
@VadimR Crap, cảm ơn vì đã cập nhật lạc đà của bạn: PI không thể kiểm tra nó vì nó không phải là Java nhưng tôi biết chiến lược của mình là vô dụng với những kẻ săn mồi ở giữa lãnh thổ của tôi: P
Herjan

5
Lại là Herjan! Ngoài ra "Nó trở nên khá khó khăn cho bộ ngực của tôi để tồn tại với ít hơn 200 con mồi" (nhấn mạnh thêm). Tôi đã không nhận ra sức sống của bộ ngực của bạn phụ thuộc vào số lượng con mồi trong một mô phỏng máy tính ....
Justin

5

Ruby Kền kền

Ở đây có một gói ký sinh trùng hoạt động nhiều hơn . Chúng đang cố gắng bao vây kẻ săn mồi di chuyển gần nhất , để chúng có thể đánh cắp con mồi của mình . Họ có một chút may mắn phụ thuộc vì họ không có cách thông minh để chọn ai theo dõi nhưng họ thường đánh bại những kẻ săn đuổi và đôi khi là nhện .

Họ chưa hoàn thành, vì tôi đã đăng bài này để đẩy tiến độ :)

Tôi hy vọng:

  • làm cho chúng tìm kiếm động vật ăn thịt bên ngoài lĩnh vực xem
  • hãy tính đến con mồi - thường thì một trong số chúng nằm giữa một gói khác và con mồi!
  • bắt đầu xoay chúng để tránh chết đói khi tất cả những con khác được cho ăn tốt

Ngày 22 tháng 4 năm 2014: Thêm sự nhàm chán , khiến chúng bớt dính và cho phép chúng tự săn mồitìm kiếm kẻ săn mồi

class Animal
  attr_accessor :x, :y
end

class Hunter < Animal
  attr_accessor :id, :bored

  def initialize diff
   @diff = diff
   @lastGoal = nil
   @bored = false
  end

  def move goal
    if not goal.nil? 
      if @bored or goal != @lastGoal
        @lastGoal = goal
        return [goal.first - x + @diff.first, goal.last - y + @diff.last]
      end
    end
    [250 - x + 3*@diff.first, 250.0 - y + 3*@diff.last]
  end
end

class Pack
  def initialize
    @file = File.open "pack_log", "w"
    @count = 0
    @pack = []
    @order = []
    @hunters = []
    @closest = nil
    @random_goal = [250.0, 250.0]
    @locations = []
    @timer = 0
    d = 25.0
    diffs = [[d, d], [d, -d], [-d, -d], [-d, d], [0.0, 0.0]]
    5.times do |i|
      @pack << (Hunter.new diffs[i])
    end
    line = 0
    s = gets
    loop do
      s = gets
      if not (s =~ /dead\0/).nil?
        break
      end
      if line == 0
        get_structure s
      elsif line == 1
        get_positions s
      end
      @pack.length.times do |i|
        if line == i*2 + 3
          look_for_hunters s
          if @count <= i+1
            @closest = closest_hunter
            move
          end
        end
      end
      if not (s =~ /\0/).nil?
        line = 0
        @hunters = []
      else
        line += 1
      end
    end
  end

  def member_by_id id
    member = nil
    @pack.each do |v|
      if v.id == id
        member = v
        break
      end
    end
    member
  end

  def member_by_order index
    member_by_id @order[index]
  end

  def distance a, b
    Math.sqrt((a.first - b.first)**2 + (a.last - b.last)**2)
  end

  def bored?
    bored = true
    l1 = @locations.first
    @locations.each do |l2|
      if distance(l1, l2) > 20
        bored = false
      end
    end 
    bored
  end

  def bored_move v
    if @timer <= 0
      @random_goal = [rand(1000).to_f - 250, rand(1000).to_f - 250]
      @pack.each do |m|
        m.bored = true
      end
      @timer = 250 
    else
      @timer -= 1
    end
    v.move @random_goal
  end

  def move
    first_one = true
    answer = ""
    @order.each do |id|
      v = member_by_id id
      x, y = 0, 0
      if bored?
        x, y = (bored_move v)
      elsif @timer > 0
        @location = []
        x, y = (bored_move v)
      else
        @pack.each do |m|
          m.bored = false
        end
        @timer = 0
        x, y = v.move @closest
      end
      if not first_one
        answer << "\t"
      end
      answer << "#{x.to_i}.0\t#{y.to_i}.0"
      first_one = false
    end
    answer << "\0"
    print answer
    STDOUT.flush
  end

  def get_structure line
    @order = []
    if @pack.first.id.nil? 
      @count = 0
      line.split.each_with_index do |v, i|
        if i % 2 == 0
          @order << v.to_i
          @pack[i/2].id = v.to_i
          @count += 1
        end
      end
    else
      @count = 0
      line.split.each_with_index do |v, i|
        if i % 2 == 0
          @order << v.to_i
          @count += 1
        end
      end
    end
  end

  def get_positions line
    if not @order.empty?
      line.split.each_with_index do |v, i|
        if i % 2 == 0
          member_by_order(i/2).x = v.to_f
        else
          member_by_order(i/2).y = v.to_f
        end
      end
    end
  end

  def look_for_hunters line
    line.split.each_with_index do |v, i|
      if i % 2 == 0
        @hunters << [v.to_f]
      else
        @hunters.last << v.to_f
      end
    end
  end

  def closest_hunter
    mass_center
    closest = nil
    bestDist = 500*500
    if not @hunters.nil? and not @hunters == []
      @hunters.each do |h|
        our = false
        @pack.each do |v|
          if h.first == v.x and h.last == v.y
            our = true
          end
        end
        if our
          next
        end
        sqDist = (@mass_center.first - h.first)**2 + (@mass_center.last - h.last)**2
        if sqDist < bestDist
          closest = []
          closest << h.first
          closest << h.last
        end
      end
    end
    closest
  end

  def mass_center
    center_x = 0
    center_y = 0
    @pack.each do |v|
      center_x += v.x
      center_y += v.y
    end
    @mass_center = [center_x.to_f / @count, center_y.to_f / @count]
    if @locations.length > 30
      @locations.shift
      @locations << @mass_center
    else
      @locations << @mass_center
    end
  end
end

Pack.new

Bạn chắc chắn cần nhiều "thợ săn" hơn trong hỗn hợp. Như vậy, chúng có xu hướng bám vào các ký sinh trùng khác (vì đó là phần lớn trên cánh đồng). Mặc dù vậy, tôi thích xem họ và có thể thấy họ hiệu quả như thế nào với sự kết hợp khác nhau của các đối thủ cạnh tranh.
Geobits

Ồ vâng, trong môi trường thử nghiệm của tôi, tôi có hai gói thợ săn khác. Không có chúng kền kền có lẽ khá là không biết gì. Đặc biệt là netcats có thể nhanh chóng làm việc các góc mà không bị nhìn từ giữa.
Legat

Tôi nghĩ rằng, tôi biết những gì có thể gây rắc rối cho họ nói riêng. Vũ điệu chiến tranh của lạc đà ác. @Geobits làm thế nào về việc đưa các trận đánh lên Youtube? 10 vòng không phải là quá nhiều để có thể xem được. Tất nhiên, HQ sẽ là cần thiết. Không mong đợi hàng triệu khán giả nhưng sẽ rất thú vị khi xem các gói của bạn hoạt động như thế nào và có thể cổ vũ cho họ một chút :)
Legat

1
Toàn bộ tourney có thể hơi dài (~ 8 phút mỗi vòng) để thu hút sự chú ý, nhưng ghi lại một vòng "khán giả" có thể hoạt động. Tôi sẽ suy nghĩ cho các hoạt động trong tương lai.
Geobits

@Geobits Tốc độ có thay đổi nhiều trong vòng 8 phút không? Tôi tự hỏi liệu có đáng để ghi lại một khung hình mỗi lượt để chúng có thể được phát lại với tốc độ không đổi, thay vì làm chậm trong các phần tính toán chuyên sâu. Đối với mục đích YouTube, ý tôi là.
trichoplax

5

Ác Eco Camels

Chỉnh sửa: Đột biến # 2. Ồ, không, tôi đã muộn với việc thực hiện dự đoán chuyển động con mồi, là người đầu tiên đánh bại Netcats. OK, vậy là được.

Đột biến này có $hunger_criticalbiến (không đổi). Thay đổi nó thành giá trị trên 1000 làm cho lạc đà luôn luôn săn mồi, như Clairvoyants. Sau đó:

Done in 11.93 minutes
camels1.pl(0)                   : Turn 23112    : Score 100
Netcats(1)                      : Turn 22508    : Score 80

Nếu $hunger_criticalđược đặt thành 500 (như bên dưới), thì lạc đà của tôi (sau khi nhìn thấy sự khủng khiếp của nền văn minh ) cố gắng cư xử theo cách thân thiện với môi trường (do đó chúng đã thay đổi tên giống của chúng), tức là chúng chỉ giết khi đói. Nếu không đói, họ tuần tra các khu vực Đảo quan trọng - trung tâm và các góc, để ngăn chặn sự tàn sát vô nghĩa của một số thợ săn khác. Vâng, với trung tâm, nó ít nhiều hoạt động. Ý tưởng xoay quanh các góc là để xua đuổi con mồi và khiến cuộc sống của Mèo và ký sinh trùng trở nên khó khăn hơn. Vâng, nó không hoạt động. Con mồi ngu ngốc đi vào góc nào.

Ngoài ra, điều thú vị là flock[ALIGN]thành phần này chỉ có thể đoán được, bởi những kẻ săn mồi, và việc thực hiện của tôi khác với những kẻ tầm thường. Tôi e rằng có một số lỗi nhỏ trong quá trình triển khai mã Geobits của mình, xem / so sánh việc săn cá nhân của Camels và Clairvoyants.

Và chương trình là loại dài bây giờ, xin lỗi.


Chỉnh sửa: Đột biến # 1. Đảo hóa ra khá phóng xạ (điều này giải thích việc thiếu thảm thực vật và bản chất không thể giải thích được của các sinh vật 'con mồi'), vì vậy đây là đột biến đầu tiên của lạc đà của tôi. Bất kỳ ai trong số họ cũng có thể trở thành thợ săn solo, nếu đói hoặc nếu không có góc miễn phí cho mọi người. Hunter cố gắng tích cực theo đuổi con mồi gần đó. Nếu không có, nó tuần tra thành vòng tròn rộng quanh trung tâm hòn đảo, sau đó đuổi theo sinh vật gần nhất khi tìm thấy nó. Thật không may, hướng đi của người hâm mộ trở nên khó lường khi nó ở gần bầy đàn của nó (đáng để điều tra ...), vì vậy, cuộc rượt đuổi solo không hiệu quả lắm. Nhưng nếu thành công, Lạc đà đi tiêu hóa đến góc miễn phí gần nhất (nếu có). Khi mức độ đói dưới mức nhất định, bất kỳ Lạc đà nào cũng từ bỏ góc của nó (có thể là nguyền rủa Netcats ('thức ăn ở đâu?' )) và tự chuyển vùng miễn phí. Và như vậy.


Trò đùa tương tự được nói hai lần là không vui, nhưng (1) Tôi phải bắt đầu ở đâu đó và tôi mới biết những điều này, (2) Thành thật mà nói, tôi đã nghĩ về chiến thuật phạt góc (và ai đã không?), Xem Netcats, trước Ruby Nhện xuất hiện trên đảo.

Vì vậy, bao giờ nghe nói về lạc đà ăn thịt? Một ngày nọ, những con vật đáng thương thức dậy trên hòn đảo bị bỏ hoang này để không tìm thấy cỏ hay cây cối, nhưng có rất nhiều màu xanh kỳ lạ mặc dù có thể di chuyển nhanh (khá khó chịu). Tôi không có thói quen săn bắn (nhưng chúng sẽ biến đổi sớm, tôi hy vọng), những con lạc đà của tôi đã phát triển kế hoạch rất xấu xa để sinh tồn: chúng tách ra và đi vào 1 trong 4 góc, và con thứ 5 đi đến trung tâm (trước tiên sẽ chết ở đó nó bật ra). Trên những điểm đến của họ, họ kiên nhẫn chờ đợi, thực hiện điệu nhảy chiến tranh lạc đà, hoặc có lẽ họ chỉ cố gắng không giẫm lên những con vật khác đã ở đó, những con nhện và tất cả ...

#!/usr/bin/env perl
use strict;
use warnings;

binmode STDOUT;
binmode STDIN;
$| = 1;
$, = "\t";

my $hunger_critical = 500;
my %pack;
my ($turn, $prey_count, $predators_count);
my $patrol_radius_hunt = 150;
my $patrol_radius_corner = 16;
my $patrol_radius_center = 1;
my @roles = qw/C LL LR UL UR/; # or P (patrol if > 5), H (hunt)
my %places = (
    UL => {x =>   1 + $patrol_radius_corner, y =>   1 + $patrol_radius_corner},
    UR => {x => 499 - $patrol_radius_corner, y =>   1 + $patrol_radius_corner},
    LR => {x => 499 - $patrol_radius_corner, y => 499 - $patrol_radius_corner},
    LL => {x =>   1 + $patrol_radius_corner, y => 499 - $patrol_radius_corner},
    C  => {x => 250, y => 250},
);

sub sq_dist {
    my ($x1, $y1, $x2, $y2) = @_;
    return ($x1 - $x2)**2 + ($y1 - $y2)**2
}

sub distance {
    return sqrt(&sq_dist)
}

sub assign_role {
    my $camel = shift;
    if (@roles) {
        my %choice = (d => 1000, i => 0);
        for my $i (0..$#roles) {
            my $r = $roles[$i];
            if ($r eq 'C') {
                if ($prey_count > 700) {
                    $choice{i} = $i;
                    last
                }
                else {
                    next
                }
            }
            my $d = distance($camel->{x}, $camel->{y}, $places{$r}{x}, $places{$r}{y});
            if ($d < $choice{d}) {
                @choice{qw/d i/} = ($d, $i)
            }
        }
        return splice @roles, $choice{i}, 1
    }
    else {
        return 'P'
    }
}

sub xy_average {
    my $xy = shift;
    my $x = my $y = 0;
    if ($xy && @$xy) {
        for my $item (@$xy) {
            $x += $item ->{x};
            $y += $item->{y}
        }
        $x /= @$xy;
        $y /= @$xy
    }
    return $x, $y
}

sub patrol {
    my ($xc, $yc, $radius, $camel) = @_;
    my ($x, $y) = ($camel->{x} - $xc, $camel->{y} - $yc);
    my $d = distance(0, 0, $x, $y);
    my $a = atan2($y, $x);
    if (abs($d - $radius) < 3) {
        $a += 6 / $radius
    }
    return $radius * cos($a) - $x, $radius * sin($a) - $y
}

while (1) {

    # Get input

    my @in;
    # Line 0 - turn, counts
    $_ = <>;
    die if /dead/;
    ($turn, $prey_count, $predators_count) = /\0?(\S+)\t(\S+)\t(\S+)/;
    # Line 1 - pack's ids and hunger
    $_ = <>;
    while (/(\S+)\t(\S+)/g) {
        push @in, {id => $1, hunger => $2}
    };
    # Line 2 - positions
    $_ = <>;
    for my $animal (@in) {
        /(\S+)\t(\S+)/g;
        ($animal->{x}, $animal->{y}) = ($1, $2);
    }
    # 2 lines per member, visible prey and predators
    for my $animal (@in) {
        $_ = <>;
        my @prey;
        while (/(\S+)\t(\S+)/g) {
            push @prey, {x => $1, y => $2}
        };
        $animal->{prey} = \@prey;
        $_ = <>;
        my @beasts;
        while (/(\S+)\t(\S+)/g) {
            push @beasts, {x => $1, y => $2}
        };
        $animal->{beasts} = \@beasts
    }
    # trailing \0 zero will be prepended to next turn input

    # Update my pack

    for my $n (0..$#in) {
        my $animal = $in[$n];
        my $id = $animal->{id};
        # old average prey position
        my @opp = xy_average($pack{$id}{prey});
        # new average prey position
        my @npp = xy_average($animal->{prey});
        # average prey displacement
        my %apd = (x => $npp[0] - $opp[0], y => $npp[1] - $opp[1]);
        $pack{$id}{apd}    = \%apd;
        $pack{$id}{hunger} = $animal->{hunger};
        $pack{$id}{x}      = $animal->{x};
        $pack{$id}{y}      = $animal->{y};
        $pack{$id}{prey}   = $animal->{prey};
        $pack{$id}{beasts} = $animal->{beasts};
        $pack{$id}{num}    = $n;
        $pack{$id}{dead}   = 0
    }

    # Bury dead animals, retrieve their roles

    while (my ($id, $camel) = each %pack) {
        if ($camel->{dead}) {
            my $role = $camel->{role};
            push @roles, $role if $role ne 'P' and $role ne 'H';
            delete $pack{$id};
        }
        else {
            $camel->{dead} = 1
        }
    }

    # See that everyone has a role and lives accordingly

    my @out;
    for my $camel (values %pack) {
        my $role = $camel->{role} ||= assign_role($camel);
        if ($camel->{hunger} < $hunger_critical and $role ne 'H') {
            push @roles, $role if $role ne 'P';
            $role = $camel->{role} = 'H'
        }
        if ($camel->{hunger} > $hunger_critical and ($role eq 'H' or $role eq 'P') and $prey_count > 400) {
            $role = $camel->{role} = assign_role($camel)
        }
        my @vector = (0, 0);
        if ($role eq 'H') {
            my @prey = @{$camel->{prey}};
            if (@prey) {
                my %nearest = (p => undef, dd => 2500);
                for my $prey (@prey) {
                    my $dd = sq_dist($camel->{x}, $camel->{y}, $prey->{x}, $prey->{y});
                    if ($dd <= $nearest{dd}) {
                        @nearest{qw/p dd/} = ($prey, $dd)
                    }
                }
                my $target = $nearest{p};
                if ($nearest{dd} > 900) {
                    @vector = ($target->{x} - $camel->{x}, $target->{y} - $camel->{y})
                }
                else {
                    my @vect = map{{x => 0, y => 0}}1..5;
                    my $n = 0;
                    for my $prey (@prey) {
                        next if $prey eq $target;
                        my $dd = sq_dist($target->{x}, $target->{y}, $prey->{x}, $prey->{y}) + 1/(~0);
                        next if $dd > 900;
                        $n ++;
                        my $dx = $prey->{x} - $target->{x};
                        my $dy = $prey->{y} - $target->{y};
                        $vect[1]{x} -= $dx / $dd;
                        $vect[1]{y} -= $dy / $dd;
                        $vect[2]{x} += $dx * $dd;
                        $vect[2]{y} += $dy * $dd
                    }
                    $vect[0] = {x => $n * $camel->{apd}{x}, y => $n * $camel->{apd}{y}};
                    my $dx = abs(250 - $target->{x});
                    my $dy = abs(250 - $target->{y});
                    my $d = $dx > $dy ? $dx : $dy;
                    if ($d > 240) {
                        $vect[4]{x} = $dx * $d;
                        $vect[4]{y} = $dy * $d;
                    }
                    for my $v (@vect) {
                        my $d = sqrt($v->{x}**2 + $v->{y}**2) + 1/(~0);
                        $v->{x} /= $d;
                        $v->{y} /= $d;
                    }
                    for my $beast (@{$camel->{beasts}}, $camel) {
                        my $dd = sq_dist($target->{x}, $target->{y}, $beast->{x}, $beast->{y});
                        next if $dd > 900;
                        $vect[3]{x} += $target->{x} - $beast->{x};
                        $vect[3]{y} += $target->{y} - $beast->{y};
                    }
                    $vector[0] = 5 * 1   * $vect[0]{x}
                               + 5 * 1   * $vect[1]{x}
                               + 5 * .96 * $vect[2]{x}
                               + 1 * 2   * $vect[3]{x}
                               + 5 * 4   * $vect[4]{x};
                    $vector[1] = 5 * 1   * $vect[0]{y}
                               + 5 * 1   * $vect[1]{y}
                               + 5 * .96 * $vect[2]{y}
                               + 1 * 2   * $vect[3]{y}
                               + 5 * 4   * $vect[4]{y};
                    my $dd = $vector[0]**2 + $vector[1]**2;
                    if ($dd > 36) {
                        my $d = sqrt($dd);
                        @vector = map {$_ * 6.1 /$d} @vector
                    }
                }
            }
            else {
                @vector = patrol(250, 250, $patrol_radius_hunt, $camel)
            }
        }
        elsif ($role eq 'P') {
            @vector = patrol(250, 250, $patrol_radius_hunt, $camel)
        }
        else {
            my $r = $role eq 'C' 
                ? $patrol_radius_center 
                : $patrol_radius_corner;
            @vector = patrol($places{$role}{x}, $places{$role}{y}, $r, $camel)
        }
        my $id_x = $camel->{num} << 1;
        my $id_y = $id_x + 1;
        @out[$id_x, $id_y] = @vector
    }

    # And let the cruel world know about it

    print @out;
    print "\0"
}

__END__

5
Đây phải là kịch bản perl dễ đọc nhất mà tôi thấy trên trang web này cho đến nay.
Geobits

Bạn cần phải đi đến góc chính xác để trốn tránh chúng một cách hiệu quả, nếu không bạn sẽ thực sự tham gia vào lò mổ của Netcats, haha
justhalf

@justhalf, Giống như tôi đã nói: kế hoạch không hiệu quả. Ký sinh trùng ngồi trong các góc cũng không xua đuổi con mồi. Hừm, có thể 2 hoặc nhiều quái thú tuần tra một góc sẽ giúp ích.
dùng2846289

Lạc đà của bạn là khá tốt thực sự! Rất may (đối với tôi) tôi đã cải thiện khả năng của mình, vì vậy hầu hết thời gian (không phải lúc nào cũng vậy), gói của tôi giành chiến thắng trước bạn trong trận chiến cuối cùng. Hấp dẫn!
justhalf

1
Nếu bạn ở gần hơn 8 (20-2 * 6) đơn vị từ con mồi, chúng ta có thể thấy bất kỳ chuyển động nào của tất cả các con mồi khác trong vòng 30 đơn vị con mồi của chúng ta trong lượt hiện tại. Và vectài sản về cơ bản chỉ là sự dịch chuyển từ lượt trước sang lượt hiện tại. Và như tôi đã nói, chúng tôi thực hiện khớp từ lượt trước để tìm ra con mồi đi theo con đường nào, chúng tôi không thể dựa vào thứ tự của con mồi. Điều này là có thể bởi vì các con mồi thường (trong kịch bản điển hình) giữ đủ khoảng cách với nhau (> 12 đơn vị), và vì vậy hầu hết thời gian chúng ta có thể khớp các con mồi trong lượt trước với lượt hiện tại.
justhalf

4

AbleDogs - PHP

Những con chó đẹp này đã học được cách cắn một con bê của con mồi để đi dọc theo các bức tường. Chúng cũng thích đi lang thang trên đồng cỏ để tìm kiếm con mồi mới. Cuối cùng, họ đã được dạy cách kiềm chế ăn trừ khi họ thực sự cần calo.

Đặt mã vào một AbleDogstệp và chạy nó vớiphp AbleDogs

<?php
// simulation parameters

define ("ARENA_SIZE", 500);

define ("HUNGER_MAX", 1000);

define ("PREY_SPEED", 6);
define ("PRED_SPEED", 6.1);

define ("PREY_VISION", 30);
define ("PRED_VISION", 50);

define ("WALL_BOUNCE", 10); // distance from a wall from which a prey starts bouncing

// derived constants

define ("PRED_SPEED2" , PRED_SPEED  * PRED_SPEED );
define ("PRED_VISION2", PRED_VISION * PRED_VISION);
define ("PREY_VISION2", PREY_VISION * PREY_VISION);

// grid to speedup preys lookup

define ("GRID_SIZE", ceil (ARENA_SIZE/PRED_VISION));
define ("GRID_STEP", ARENA_SIZE/GRID_SIZE);

// search patterns

define ("SEARCH_OFFSET", WALL_BOUNCE+PRED_VISION/sqrt(2));
define ("SEARCH_WIDTH" , 2*sqrt(PRED_VISION2-PRED_SPEED2/4));
define ("SEARCH_HEIGHT", ARENA_SIZE-2*SEARCH_OFFSET);
define ("SEARCH_SIZE"  , ceil(SEARCH_HEIGHT/SEARCH_WIDTH));
define ("SEARCH_STEP"  , SEARCH_HEIGHT/SEARCH_SIZE);
define ("SEARCH_LEGS"  , 2*SEARCH_SIZE+1);

// tracking

define ("MAX_TRACK_ERROR", 10); // max abs distance for prey tracking correlation
define ("TRACKING_HUNGER_START", HUNGER_MAX*.9); // hunger limit to try and eat the tracked prey (start of game)
define ("TRACKING_HUNGER_END", 4);     // idem, for endgame
define ("TRACKING_DISTANCE", PREY_SPEED*2.5);
define ("TRACKING_DISTANCE2", TRACKING_DISTANCE * TRACKING_DISTANCE);

class Point {
    public $x = 0;
    public $y = 0;

    function __construct ($x=0, $y=0)
    {
        $this->x = (float)$x;
        $this->y = (float)$y;
    }

    function __toString() // for comparisons
    {
        return "$this->x,$this->y";
    }

    function multiply ($scalar)
    {
        return new Point ($this->x * $scalar, $this->y * $scalar);
    }

    function add ($v)
    {
        return new Point ($this->x + $v->x, $this->y + $v->y);
    }

    function dot($v)
    {
        return $this->x * $v->x + $this->y * $v->y;
    }

    function rotate90()
    {
        return new Point (-$this->y, $this->x);
    }

    function vector_to ($goal)
    {
        return new Point ($goal->x - $this->x, $goal->y - $this->y);
    }

    function norm2 ()
    {
        return $this->dot ($this);
    }

    function norm ()
    {
        return sqrt ($this->norm2());
    }

    function normalize ($norm = 1)
    {
        $n = $this->norm();
        if ($n != 0) $n = $norm/$n;
        return $this->multiply ($n);
    }

    function limit ($norm)
    {
        return $this->norm() > $norm
             ? $this->normalize($norm)
             : clone $this;
    }
}

class Search {

    function __construct ($direction)
    {
        switch ($direction % 4)
        {
            case 0: $this->pos = new Point (          0,           0); break;
            case 1: $this->pos = new Point (SEARCH_SIZE,           0); break;
            case 2: $this->pos = new Point (SEARCH_SIZE, SEARCH_SIZE); break;
            case 3: $this->pos = new Point (          0, SEARCH_SIZE); break;
        }
        $this->start();
    }

    private function start ()
    {
        $this->dir = $this->pos->x == $this->pos->y;
        $this->adj = $this->pos->x ? -1 : 1;
        $this->target = new Point ($this->pos->x * SEARCH_STEP + SEARCH_OFFSET,
                                   $this->pos->y * SEARCH_STEP + SEARCH_OFFSET);
        $this->leg = 0;
    }

    function point ($pos)
    {
        if ($pos == $this->target)
        {
            if ($this->leg % 2)
            {
                if ($this->dir) $this->pos->y+= $this->adj;
                else            $this->pos->x+= $this->adj;
            }
            else
            {
                if ($this->dir) $this->pos->x = $this->pos->x ? 0 : SEARCH_SIZE;
                else            $this->pos->y = $this->pos->y ? 0 : SEARCH_SIZE;
            }
            $this->leg++;
            if ($this->leg == SEARCH_LEGS) $this->start();
            $this->target = new Point ($this->pos->x * SEARCH_STEP + SEARCH_OFFSET,
                                       $this->pos->y * SEARCH_STEP + SEARCH_OFFSET);
        }
        return $this->target;
    }
}

class Pack {

    public static $turn;   // turn number
    public static $size;   // number of live members
    public static $member; // array of members

    public static $prev_preys;     // previous coordinates of all preys
    public static $prev_preds;     // previous coordinates of foreign predators

    public static $n_preys; // total number of preys     (including those not currently seen)
    public static $n_preds; // total number of predators (including those not currently seen)

    public static $preys;     // coordinates of all preys
    public static $preds;     // coordinates of all predators
    public static $own_preds; // coordinates of all predators in our pack
    public static $foe_preds; // coordinates of all foreign predators

    public static $arena_center; // arena center

    private static $output_order; // to send output according to input order

    function init ()
    {
        Pack::$member = array();
        Pack::$arena_center = new Point (ARENA_SIZE/2, ARENA_SIZE/2);
    }

    function read_line ($line)
    {
        $values = array();
        if ($line == "") return $values;
        $input = explode ("\t", $line);
        $num = count($input);
        if ($num % 2) panic ("read_line: invalid input $line num $num");
        $num /= 2;
        for ($i = 0 ; $i != $num ; $i++)
        {
            $values[] = new Point ($input[$i*2  ], $input[$i*2+1]);
        }
        return $values;
    }

    function read_input ()
    {
        // read controller input (blocking)
        $input = "";
        while (($in = fread(STDIN, 1)) !== false)
        {
            if ($in == "\0") break;
            $input .= $in;
        }

        // check extinction
        if ($input == "dead") return false;
        $lines = explode ("\n", $input);

        // save previous predators and preys positions
        Pack::$prev_preys = Pack::$preys;
        Pack::$prev_preds = Pack::$foe_preds;

        // line 0: turn, preys, predators
        list (self::$turn, Pack::$n_preys, Pack::$n_preds) = explode ("\t", $lines[0]);

        // line 1: list of ids and hunger levels
        $id = array();
        Pack::$size = 0;
        Pack::$output_order = array();
        foreach (Pack::read_line($lines[1]) as $i=>$v)
        {
            $id[$i] = $v->x;
            Pack::$output_order[] = $id[$i];

            if (!isset (Pack::$member[$id[$i]])) Pack::$member[$id[$i]] = static::new_member();
            Pack::$size++;
            Pack::$member[$id[$i]]->hunger = $v->y;
            Pack::$member[$id[$i]]->ttl = self::$turn;
        }

        // line 2: member positions
        Pack::$own_preds = array();
        foreach (Pack::read_line($lines[2]) as $i=>$pos)
        {
            Pack::$member[$id[$i]]->pos = $pos;
            Pack::$own_preds[] = $pos;
        }

        // lines 3 to 2*#members+3: coordinates of all visible preys and predators
        $preys = array();
        $preds = array();
        $y_seen = array();
        $d_seen = array();
        for ($i = 0 ; $i != Pack::$size ; $i++)
        {
            // visible preys
            foreach (Pack::read_line($lines[2*$i+3]) as $coords)
            {
                if (!in_array ($coords, $preys) || !isset($y_seen[(string)$coords]))
                {
                    $preys[] = $coords;
                }
            }
            foreach ($preys as $p) $y_seen[(string)$p] = true;

            // visible predators
            foreach (Pack::read_line($lines[2*$i+4]) as $coords)
            {
                if (!in_array ($coords, $preds) || !isset($d_seen[(string)$coords]))
                {
                    $preds[] = $coords;
                }
            }
            foreach ($preds as $p) $d_seen[(string)$p] = true;
        }

        // remove dead members
        foreach (Pack::$member as $k => $m)
        {
            if ($m->ttl != self::$turn)
            {
                unset (Pack::$member[$k]);
            }
        }

        // filter out own positions from predators list
        Pack::$foe_preds = array_diff ($preds, Pack::$own_preds);
        Pack::$preds = Pack::$foe_preds;
        foreach (Pack::$own_preds as $p) Pack::$preds[] = $p;
        Pack::$preys = $preys;

        // done
        return true;
    }

    function output_moves ()
    {
        $output = array();
        foreach (Pack::$output_order as $i)
        {
            $output[] = Pack::$member[$i]->move->x;
            $output[] = Pack::$member[$i]->move->y;
        }
        echo implode ("\t", $output) . "\0";
    }

    static function point_closest_to_walls ($pos)
    {
        $delta = $pos->vector_to (Pack::$arena_center);
        if (abs ($delta->x) > abs ($delta->y))
        {
            $y = $pos->y;
            $x = $delta->x > 0 ? -1 : ARENA_SIZE+1;
        }
        else
        {
            $x = $pos->x;
            $y = $delta->y > 0 ? -1 : ARENA_SIZE+1;
        }
        return new Point ($x, $y);
    }

    static function in_arena ($pos)
    {
        $delta = $pos->vector_to (Pack::$arena_center);
        return abs ($delta->x) <= ARENA_SIZE/2 && abs ($delta->y) <= ARENA_SIZE/2;
    }

    static function clamp_to_arena (&$pos)
    {
        // mimics the slightly strange behaviour of the Java engine setInZeroBounds function
        if ($pos->x >= ARENA_SIZE) $pos->x = ARENA_SIZE-1; // should rather be ARENA_SIZE
        if ($pos->x <           0) $pos->x = 0;
        if ($pos->y >= ARENA_SIZE) $pos->y = ARENA_SIZE-1;
        if ($pos->y <           0) $pos->y = 0;
    }

    function get_closest ($pos, $set, $max_dist)
    {
        // check for empty set
        if (count ($set) == 0) return null;

        // construct an array of distances with the same indexes as the points
        $dist = array();
        $max_dist *= $max_dist;
        foreach ($set as $k=>$pt)
        {
            $d = $pos->vector_to($pt)->norm2();
            if ($d <= $max_dist) $dist[$k] = $d;
        }
        if (count($dist) == 0) return false;

        // get the key of the smallest distance and use it to retrieve the closest point
        $keys = array_keys ($dist, min($dist));
        return $set[$keys[0]];
    }

    function get_visible ($pos, $set)
    {
        $res = array();
        $skipped = false;
        $pts = 0;
        foreach ($set as $point)
        {
            $d = $pos->vector_to($point)->norm2();
            if ($d == 0 && !$skipped)
            {
                $skipped = true;
                continue; // skip ourself
            }
            if ($d > PREY_VISION2) continue; // skip far points
            $res[] = $point;
            if ($pts++ > 10) break; // too many points are useless since prediction will go haywire anyway
        }
        return $res;
    }
}
Pack::init();

class PackMember {
    public $pos; // current position
    public $ttl; // last turn reported alive

    function move_to ($goal)
    {
        $this->move = $this->pos->vector_to ($goal);
    }

    function intercept ($target_pos, $target_speed)
    {
        // change reference to position difference
        $delta = $this->pos->vector_to($target_pos);
        $i = $delta->normalize();
        $j = $i->rotate90();

        // match tangential speeds
        $vj = $target_speed->dot ($j);

        // deduce axial speed
        $vi = PRED_SPEED2 - $vj*$vj; // this should always be positive since predators are faster than preys
        $vi = sqrt ($vi);

        // return intercept speed in original reference coordinates
        return $i->multiply($vi)->add($j->multiply($vj));
    }
}

class Target {
    public $pos;      // current position
    public $pos_next; // predicted position
    public $speed;    // estimated speed

    function __construct ($pos)
    {
        $this->pos    = $pos;
        $this->speed  = new Point(0,0);
        $this->predict();
    }

    private function predict()
    {
        // predators contribution
        $preds = Pack::get_visible ($this->pos, Pack::$preds);
        $this->preds = count ($preds);
        $res = new Point();
        foreach ($preds as $predator)
        {
            $res = $res->add ($predator->vector_to ($this->pos));
        }
        $res = $res->multiply (2);

        // preys contribution
        $preys = Pack::get_visible ($this->pos, Pack::$preys);
        $this->preys = count ($preys);

        $f_cohesion  = new Point;
        $f_separate  = new Point();
        foreach ($preys as $prey)
        {
            $delta = $this->pos->vector_to ($prey);
            $d2 = $delta->norm2();
            if ($d2 != 0)
            {
                $f_cohesion  = $f_cohesion ->add ($delta->multiply ($d2));
                $f_separate  = $f_separate ->add ($delta->multiply (-1/$d2));
            }
        }

        $res = $res
        ->add ($this->speed->normalize(5*.96)) // assume all preys have same speed as target
        ->add ($f_cohesion ->normalize(5*1))
        ->add ($f_separate ->normalize(5*1));
        $delta = $this->pos->vector_to(Pack::$arena_center);
        $dist = max (abs($delta->x), abs($delta->y));
        if ($dist > (ARENA_SIZE/2-WALL_BOUNCE))
        {
            $res = $res->add ($delta->normalize(5*4));
        }

        $this->raw_speed = $res;
        $this->speed = $res->limit(PREY_SPEED);
        $this->pos_next = $this->pos->add ($this->speed);
        Pack::clamp_to_arena ($this->pos_next);
    }

    function track ()
    {
        // see if we can find our prey at the start of a new turn
        $min = 1e10;
        foreach (Raptors::$free_preys as $k=>$prey)
        {
            $dist = abs ($this->pos_next->x - $prey->x) + abs ($this->pos_next->y - $prey->y);
            if ($dist < $min)
            {
                $min = $dist;
                $new_pos = $prey;
                $new_k = $k;
                if ($min < .001) break;
            }
        }
        if ($min > MAX_TRACK_ERROR) return false;

        // remove this prey from free preys
        unset(Raptors::$free_preys[$new_k]);

        $delta = $new_pos->vector_to($this->pos_next);

        // update postion and speed
        if ($this->speed->norm2() == 0)
        {
            // this can be either an endgame prey not yet moving
            // OR initial speed for a new target
            $this->speed = $this->pos->vector_to ($new_pos);
        }
        $this->pos = $new_pos;

        // predict speed and position
        $this->predict();
        return true;
    }
}

class Raptor extends PackMember {

    // possible states
    const IDLE     = 1;
    const TRACKING = 2;
    const HUNTING  = 3;
    const RUSHING  = 4;
    public $state;

    public  $target;  // current prey
    public  $patrol;  // patrol governor

    private static $id_gen;

    function __construct ()
    {
        $this->patrol = new Search (++self::$id_gen);
        $this->target  = null;
        $this->state = Raptor::IDLE;
        $this->pos = null;
        $this->hunger = HUNGER_MAX;
    }

    function __destruct ()
    {
        $this->tracking_lost();
    }

    function tracking_lost()
    {
        $this->target  = null;
        $this->state = Raptor::IDLE;
    }

    function track_prey()
    {
        // stop tracking if hunger went back to max
        if ($this->hunger == HUNGER_MAX)
        {
            $this->tracking_lost();
        }

        // try to acquire a new target
        if (!$this->target)
        {
            $victim = Pack::get_closest ($this->pos, Raptors::$free_preys, PRED_VISION);
            if (!$victim) return;
            $this->target = new Target ($victim);
            $this->state = Raptor::TRACKING;
        }

        // track prey
        if (!$this->target->track (Pack::$preys))
        {
            // prey was eaten or move prediction failed
            $this->tracking_lost();
        }
    }

    function beat_competition ()
    {
        if ($this->target === null) return;
        $pm = $this->target->pos_next->vector_to ($this->pos);
        $dm = $pm->norm2();
        foreach (Pack::$foe_preds as $f)
        {
            $pf = $this->target->pos_next->vector_to($f);
            $df = $pf->norm2();
            if ($df > PRED_VISION2) continue;
//          if ($df < ($dm*2))
            {
                $this->state = Raptor::RUSHING;
                return;
            }
        }
        if ($this->state == Raptor::RUSHING) $this->state = Raptor::TRACKING;
        return;
    }
}

class Raptors extends Pack {
    public static $free_preys; // coordinates of all preys that are not targeted

    // allows generic Pack to create a proper pack member instance
    static function new_member()
    {
        return new Raptor();
    }

    // main AI loop
    static function think ()
    {
        $hunger_limit = Pack::$n_preys > 2 * Pack::$n_preds ? TRACKING_HUNGER_START : TRACKING_HUNGER_END;
        self::$free_preys = static::$preys;

        // update targets and members states
        foreach (Pack::$member as $m)
        {
            // track current targets
            $m->track_prey();

            // rush to target if a competitor draws near
            $m->beat_competition();

            // hunt if hungry enough
            if ($m->state == Raptor::TRACKING && $m->hunger < $hunger_limit)
            {
                $m->state = Raptor::HUNTING;
            }
        }

        // move members
        foreach (Pack::$member as $m)
        {
            switch ($m->state)
            {
            case Raptor::IDLE:
                $destination = $m->patrol->point($m->pos);
                break;
            case Raptor::TRACKING:
                $wall_point = Pack::point_closest_to_walls ($m->target->pos_next);
                $destination = $wall_point->vector_to ($m->target->pos_next)->normalize (TRACKING_DISTANCE)->add($m->target->pos_next);
                break;
            case Raptor::HUNTING:
                $wall_point = Pack::point_closest_to_walls ($m->target->pos_next);
                $to_hunter = $m->target->pos_next->vector_to ($m->pos);
                $dist_to_target = $to_hunter->norm();

                if ($dist_to_target > (PREY_VISION-PREY_SPEED)) // intercept the prey
                {
                    // use actual speed (i.e. true position delta, including wall stops)
                    $target_true_speed = $m->target->pos->vector_to ($m->target->pos_next);
                    $intercept_speed = $m->intercept ($m->target->pos, $target_true_speed);
                    $destination = $m->pos->add ($intercept_speed);
                }
                else if ($dist_to_target < PRED_SPEED) // pounce on the prey!
                {
                    $destination = $m->target->pos_next;
                }
                else if ($to_hunter->dot($m->target->speed) > 0)
                {
                    $destination = $m->target->pos_next;
                }
                else // goad the prey
                {
                    $to_wall = $m->target->pos->vector_to ($wall_point);
                    $wall_point = $wall_point;
                    $raw_speed = $m->target->raw_speed->add($m->target->pos->vector_to($m->pos)->multiply(2))->multiply (-0.5);
                    $wpd_t = $m->target->pos->vector_to ($wall_point)->normalize()->rotate90(); // wpd = Wanted Prey Direction
                    $delta = $wpd_t->multiply ($raw_speed->dot ($wpd_t));
                    $destination = $delta->vector_to ($m->target->pos_next);
                    if (!Pack::in_arena ($destination)) $destination = $m->target->pos_next;
                }
                break;
            case Raptor::RUSHING:
                $destination = $m->target->pos_next;
                break;
            }
            $m->move_to ($destination);
        }
    }
}

while (Raptors::read_input())
{
    Raptors::think();
    Raptors::output_moves();
}
?>

Xem xét chung

  • Đó là kết thúc có giá trị. Bạn có thể có thuật toán săn thông minh nhất từ ​​trước đến nay, nếu bạn không phát hiện ra và bắt được vài con mồi cuối cùng nhanh hơn đối thủ, bạn sẽ thua.

  • Nếu động vật săn mồi của bạn không thể bắt được con mồi một mình (hoặc ít nhất là theo cặp), bạn sẽ nướng ngay khi mật độ con mồi giảm xuống đủ thấp để dựa vào may mắn mù quáng hoặc chặn con mồi vào các góc.

  • Một dự đoán chuyển động con mồi về cơ bản là bắt buộc. Tôi không thể tưởng tượng việc đánh bại một chương trình dựa trên dự đoán mà không có dự đoán của riêng bạn.

Đuổi theo đuôi

Cách không hiệu quả nhất để bắt con mồi là bám đuôi nó. Giả sử một kẻ săn mồi duy nhất đuổi theo một con mồi và không có ảnh hưởng bên ngoài (tường, con mồi khác, v.v.), một cuộc săn đuổi đuôi có thể tồn tại mãi mãi. Ngay khi bạn vào bán kính tầm nhìn của con mồi là 30 đơn vị, con mồi sẽ chạy với tốc độ 6 cho 6.1 của bạn, do đó bạn đạt được .1 khoảng cách mỗi lượt: theo một đường thẳng, bạn sẽ cần khoảng 300 lượt để có được nó.

Nếu tính kích thước đấu trường, một con mồi sẽ di chuyển nhiều nhất theo đường chéo của một hình vuông 500 đơn vị trước khi đâm vào tường hoặc một góc, sẽ mất tối đa 117 lượt.

Chiến lược chiến thắng rõ ràng là tìm cách làm chậm con mồi, cụ thể là bằng cách có một kẻ săn mồi khác hoặc một bức tường / góc phía trước nó.

Dự đoán

Với tốc độ con mồi là 6, con mồi có thể di chuyển đến khu vực có đơn vị 36 * pi bình phương. Với bán kính bắt là 1, dự đoán mù về việc con mồi sẽ ở đâu tiếp theo có cơ hội 1/36 * pi (khoảng 1%) để thành công. Rõ ràng một cái gì đó phải được thực hiện để tăng cường điều đó!

Nhìn vào mã công cụ mô phỏng, bạn có thể thấy rằng các đầu vào là:

  • con mồi có thể nhìn thấy và vị trí động vật ăn thịt
  • tốc độ con mồi trước đó

Trong khi tất cả các vị trí đều có sẵn, tốc độ con mồi trước đó thì không. Cách duy nhất để tính toán các tốc độ này là theo dõi từng con mồi từ lượt này sang lượt khác, điều này không thể thực hiện được (trừ khi bạn thực hiện thuật toán theo dõi chuyển động rất thông minh). Vì vậy, một người dự đoán có thể dễ dàng sao chép tất cả các điều khoản của tính toán, ngoại trừ đóng góp tốc độ phải được đoán.

Trong trường hợp con mồi đơn lẻ, tốc độ có thể được theo dõi mà không gặp quá nhiều vấn đề, điều này cho phép xây dựng một công cụ dự đoán "hoàn hảo" để bắt con mồi cách ly với đàn. Về cơ bản, đó là tất cả những gì bạn cần cho trò chơi cuối cùng, khi con mồi quá ít để tương tác với nhau. Khi con mồi dồi dào và hiệu ứng đàn đủ mạnh để đánh lừa người dự đoán, mật độ tuyệt đối của con mồi sẽ bù đắp cho các lỗi (nếu bạn không bắt được con mồi mà bạn đang nhắm tới, rất có thể bạn sẽ có được một trong những người bạn thân nhất của nó ).

Tải con mồi

Với kiến ​​thức chính xác về tính toán tốc độ của con mồi, có thể "điều khiển" một con mồi nhất định theo hướng mong muốn, bằng cách điều chỉnh vị trí của kẻ săn mồi.

Điều này cho phép ghim con mồi vào tường hoặc hướng nó về phía thành viên gói khác. Tôi đã thử một số chiến lược tinh tế, như chèn ép con mồi giữa hai thành viên gói. Thật không may, điều này tỏ ra kém hiệu quả hơn so với thói quen "pin và quét" hiện tại, vì việc giữ hai kẻ săn mồi bận rộn để đuổi theo một con mồi để lại sự phản đối với quá nhiều kẻ săn mồi tự do đi tìm đồng cỏ.

Ăn cắp con mồi

Một đặc điểm của hành vi con mồi là ảnh hưởng của động vật ăn thịt tăng tỷ lệ thuận với khoảng cách của nó với con mồi (miễn là nó vẫn nằm trong bán kính tầm nhìn của con mồi). Kẻ săn mồi gần nhất đến với con mồi, con mồi ít nhất sẽ tránh xa nó.

Nó có nghĩa là khi hai kẻ săn mồi cạnh tranh để bắt một con mồi, gần nhất sẽ bị bắt trước. Ngay cả một ứng cử viên siêu thông minh có thể tự xoay sở ngay trước trục của kẻ săn đuổi / con mồi về cơ bản sẽ khiến con mồi sợ hãi trong hàm của đối thủ.

Để quản lý để đánh cắp con mồi, cần ít nhất một cặp động vật ăn thịt. Một con sẽ giết, và con còn lại sẽ ở trong bán kính tầm nhìn của con mồi, càng tạo ra con mồi càng tốt để tối đa hóa ảnh hưởng và đưa con mồi về phía thợ săn.

Bên cạnh đó, mọi thay đổi hướng sẽ cho phép đối thủ cắt góc về phía con mồi và chỉ có thể đứng sau đối thủ nếu "con dê" đủ gần con mồi khi bắt đầu hành động.

Vì vậy, việc đánh cắp con mồi chỉ có cơ hội thành công nếu vị trí ban đầu của "kẻ đánh cắp" là thuận lợi và bạn có thể tha cho ít nhất một kẻ săn mồi thứ hai. Theo kinh nghiệm của tôi, điều này không đáng để phức tạp.

Đề xuất thay đổi

Để cho phép các chiến lược phức tạp hơn, việc di chuyển động vật ăn thịt trên tốc độ tối đa của con mồi có thể phải trả giá bằng điểm đói, tỷ lệ thuận với tốc độ vượt quá. Ví dụ, di chuyển lên tốc độ 6 là miễn phí và mỗi điểm tốc độ trên 6 tốn 100 điểm đói (đến 6,3 tốn 30 điểm đói mỗi lượt, đốt 1000 điểm đói sẽ cho phép đạt tốc độ 16 trong một lượt - và chết nếu bạn không 't bắt một con mồi làm như vậy!).

Thay vì giết chết một kẻ săn mồi ngẫu nhiên khi có nhiều hơn một con đủ gần để ăn con mồi, tôi đề nghị chia lợi ích (ví dụ 3 kẻ săn mồi sẽ nhận được 333,33 điểm đói mỗi lần). Điều này sẽ cho phép các chiến lược kết thúc thâm nhập hơn (che giấu kẻ săn mồi kẻ thù sẽ trở nên hữu ích nếu bạn cho rằng bạn có nhiều điểm đói hơn).

Màu sắc đặc biệt cho gói đầu tiên khá khó nhìn. Tôi đề nghị màu lục lam hoặc cam thay vì màu xanh.


Cuối cùng là một đối thủ cạnh tranh khác! Tôi cố tình thực hiện mọi điểm bạn đề cập ngoại trừ việc đánh cắp con mồi mà tôi hài lòng với tác dụng phụ hiện tại. Từ ý kiến ​​của bạn, có vẻ như bạn đang chiến thắng trước Clairvoyant của tôi? Điều đó thật thú vị, tôi sẽ kiểm tra vào ngày mai. = D. Ngoài ra, bạn có thể thử cập nhật GUI của tôi để xem đồ họa tốt hơn (ít nhất là theo tôi).
vừa qua

Tôi không chiến thắng mọi lúc. Nó phụ thuộc vào người gần con mồi cuối cùng khi chúng sinh sản. Những con chó có khả năng của tôi có thể có một lợi thế thống kê, mặc dù. Tôi cũng điều chỉnh công cụ mô phỏng để hiển thị mỗi đội theo một màu khác nhau, nhưng cuối cùng, nó tỏ ra quá sặc sỡ theo sở thích của tôi, vì vậy tôi quyết định chọn màu cam thay vì màu xanh cho đội 1 và tất cả các màu đỏ khác.

Wow, tôi chỉ chạy trình của bạn. Điều đó thật điên rồ, tôi không biết bạn có thể làm cho lời cầu nguyện đứng yên như thế. Và tôi cảm thấy một chút bị lừa rằng khi con mồi chính xác ở rìa, những kẻ săn mồi của tôi sẽ không thể phát hiện ra chúng, đó chắc chắn là một bất lợi lớn đối với tôi.
cần

Đó là mỹ phẩm vector cho bạn :). Đó là những gì tôi đã cố gắng giải thích trong bài viết của mình: đối với một con mồi, bạn biết tất cả các thông số chuyển động (bao gồm cả tốc độ trước đó), vì vậy bạn có thể tính toán vị trí động vật ăn thịt sẽ tạo ra tốc độ con mồi thích hợp để khiến nó đứng yên gần tường.

1
Vâng, giải pháp phương trình được mã hóa cứng (không cần lặp lại). Về cơ bản, bạn tính toán vectơ mà con mồi nên sử dụng cho tốc độ quay tiếp theo nếu kẻ săn mồi của bạn không ở đó. Đưa ra hướng bạn muốn con mồi đi, bạn suy ra sự khác biệt vectơ cần thiết để làm cho con mồi có điểm tốc độ theo hướng đó. Sự khác biệt vectơ này cung cấp cho bạn vị trí động vật ăn thịt so với con mồi. Điều này khiến bạn có một mức độ tự do cho phép bạn (trong giới hạn nhất định) chọn khoảng cách với con mồi.

3

Gói lười Haskell

import Control.Monad
import Control.Concurrent

main :: IO ()
main=do
    t<-forkIO $ forever $ (putStrLn "Pack is paralyzed with indecision.\0")
    loop
    killThread t
        where
            loop=do
                line <- getLine
                case line of
                    "dead\0" -> return ()
                    _        -> loop

Bạn sẽ cần nền tảng haskell để chạy này. Sau đó, bạn sử dụng runhaskelllệnh để chạy nó. Gói của tôi chờ đợi con mồi đến với chúng.


+1 cho một giải pháp bộ xương trong một ngôn ngữ mới. Có lẽ bạn rất vui khi mọi người xây dựng chiến lược mới trên đầu trang này?
trichoplax

Chắc chắn, tại sao không. (Mặc dù, nó không làm gì ngoài việc tạo ra đầu ra liên tục và thoát khỏi "dead \ 0", vì vậy tôi không chắc liệu nó có hữu ích không.)
PyRulez

Tôi vẫn khuyên mọi người chạy -silenttùy chọn này, mặc dù ...
Geobits

3

Không phải là một mục, tôi luôn quan tâm đến việc thêm màu tùy chỉnh cho mỗi mục tham gia trong ;)

Và quá trình ăn uống không được hình dung bằng cách thay đổi màu sắc, nhưng thay đổi kích thước thay vào đó, để chúng ta có thể thấy nhiều sự kiện ăn trong thời gian ngắn.

Game.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JFrame;

public class Game {

    static int preyStartCount = 0; // 0 means 1500 + (packs * 50)
    static int turn;
    static boolean silent = false;
    long startTime;

    JFrame frame;
    BufferedImage img;
    Color[] colors;

    Island map;
    List<Prey> preys;
    List<Predator> predators;
    List<Pack> packs;
    List<Pack> initPacks;

    public static void main(String[] args) throws InterruptedException {

        Game game = new Game();
        game.init(args);
        if (game.packs.size() > 0){
            game.run();
            game.score();
        }
        game.end();
    }

    void end() {
        frame.setVisible(false);
        frame.dispose();
        for (Pack pack : packs)
            pack.handler.end();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        } finally {
            for (Pack pack : packs)
                pack.handler.shutdown();
        }

        System.exit(0);
    }

    void score() {
        Collections.sort(initPacks);
        int score = 100;
        initPacks.get(0).score = score;
        for (int i = 1; i < initPacks.size(); i++) {
            Pack pack = initPacks.get(i);
            if (pack.extinctionTurn < initPacks.get(i - 1).extinctionTurn)
                score = score < 1 ? score : score * 80 / 100;
            pack.score = score;
        }
        print("", true);
        print("Done in " + getElapsedTime(), true);
        for (Pack pack : initPacks)
            print(pack.toString() + "\t: Turn " + pack.extinctionTurn + "\t: Score " + pack.score, true);
    }

    String getElapsedTime(){
        double elapsed = (System.currentTimeMillis() - startTime) / 1000d;
        if(elapsed < 60)
            return String.format("%.2f", elapsed) + " seconds";
        elapsed /= 60;
        if(elapsed < 60)
            return String.format("%.2f", elapsed) + " minutes";
        elapsed /= 60;
        return String.format("%.2f", elapsed) + " hours";       
    }


    public Game() {
        initPacks = new ArrayList<Pack>();
        packs = new ArrayList<Pack>();
        preys = new ArrayList<Prey>();
        predators = new ArrayList<Predator>();
    }

    void run() throws InterruptedException {
        frame.setVisible(true);
        turn = 0;
        Graphics2D g = img.createGraphics();

        printStatus();
        while (true) {
            turn++;

            getAllMoves();
            moveAll();
            spawn();
            removeDead();
            shuffle();

            if (turn % 500 == 0)
                printStatus();
            paint(frame, g);
            Thread.sleep(5);
            if (packs.size() < 1)
                break;
        }
    }

    void getAllMoves(){
        for (Prey prey : preys)
            prey.setNextMove();
        for (Pack pack : packs){    
            pack.talk(preys.size(), predators.size(), turn);
        }
        while(true){
            int doneCount = 0;
            for(Pack pack : packs)
                if(pack.doneTalking)
                    doneCount++;
            if(doneCount >= packs.size())
                break;
            try {Thread.sleep(1);}catch(InterruptedException e){}
        }
    }

    void moveAll(){
        for (Creature prey : preys) 
            prey.move();
        for(Pack pack : packs){
            for (Predator predator : pack.members) {
                predator.move();
                predator.eatOrStarve();
            }
        }
    }

    void paint(JFrame frame, Graphics2D g){
        g.setPaint(Color.BLACK);
        g.fillRect(0, 0, img.getWidth(), img.getHeight());

        for(Prey prey : preys)
            prey.paint(g);
        for(Pack pack : packs)
            for(Predator predator : pack.members)
                predator.paint(g);

        frame.repaint();
    }

    List<Prey> deadPreys;
    List<Predator> deadPredators;
    List<Pack> deadPacks;

    void removeDead(){
        deadPreys.clear();
        for (Prey prey : preys)
            if (!prey.alive)
                deadPreys.add(prey);
        preys.removeAll(deadPreys);

        deadPredators.clear();
        for (Predator predator : predators)
            if (!predator.alive)
                deadPredators.add(predator);
        predators.removeAll(deadPredators);

        deadPacks.clear();
        for (Pack pack : packs)
            if (!pack.alive)
                deadPacks.add(pack);
        packs.removeAll(deadPacks);

        for (Pack pack : packs) {
            pack.members.removeAll(deadPredators);
        }

        map.rebuildLists(preys, predators);
    }

    void shuffle(){
        Collections.shuffle(packs);
        for(Pack pack : packs)
            Collections.shuffle(pack.members);
    }

    void spawn(){
        if(turn % 5000 == 0)
            addPredators(1);
        if(turn % 1000 == 0)
            populatePrey(predators.size()-1, false);
    }

    void addPredators(int count){
        for(Pack pack : packs){
            if(!pack.alive)
                continue;
            if(pack.aliveCount == 0)
                continue;
            Predator parent = null;
            for(Predator predator : pack.members)
                if(predator.alive)
                    parent = predator;
            if(parent != null){
                for(int i=0;i<count;i++){
                    XY pos = new XY(Math.random() * 30, Math.random()   * 30);
                    pos.add(parent.pos);
                    pos.setInZeroBounds(Island.SIZE, Island.SIZE);
                    Predator child = new Predator(pack);
                    child.color = colors[pack.id];
                    child.moveTo(pos, Island.getCellByPosition(pos));
                    predators.add(child);
                    pack.members.add(child);
                    pack.aliveCount++;
                }
            }
        }
    }

    Color[] generateColors(int n){
        Color[] result = new Color[n];
        double maxR = -1000;
        double minR = 1000;
        double maxG = -1000;
        double minG = 1000;
        double maxB = -1000;
        double minB = 1000;
        double[][] colors = new double[n][3];
        for(int i=0; i<n; i++){
            double cos = Math.cos(i * 2 * Math.PI / n);
            double sin = Math.sin(i * 2 * Math.PI / n);
            double bright = 1;
            colors[i][0] = bright + sin/0.88;
            colors[i][1] = bright - 0.38*cos - 0.58*sin;
            colors[i][2] = bright + cos/0.49;
            maxR = Math.max(maxR, colors[i][0]);
            minR = Math.min(minR, colors[i][0]);
            maxG = Math.max(maxG, colors[i][1]);
            minG = Math.min(minG, colors[i][1]);
            maxB = Math.max(maxB, colors[i][2]);
            minB = Math.min(minB, colors[i][2]);
        }
        double scaleR = 255/(maxR-minR);
        double scaleG = 255/(maxG-minG);
        double scaleB = 255/(maxB-minB);
        for(int i=0; i<n; i++){
            int R = (int)Math.round(scaleR*(colors[i][0]-minR));
            int G = (int)Math.round(scaleG*(colors[i][1]-minG));
            int B = (int)Math.round(scaleB*(colors[i][2]-minB));
            result[i] = new Color(R,G,B);
        }
        return result;
    }

    void populatePredators(String[] args) {
        int start = 0;
        if(args[0].equals("-silent")){
            silent = true;
            start = 1;
        }

        colors = generateColors(args.length-start);
        if(colors.length==1){
            colors[0] = Color.BLUE;
        }

        for (int i = start; i < args.length; i++) {
            Pack pack = new Pack(args[i]);
            if (pack.handler.init()) {
                packs.add(pack);
                initPacks.add(pack);
            }
        }
        Collections.shuffle(packs);
        XY[] positions = map.getPackStartLocations(packs.size());
        XY offset = new XY(-15, -15);
        for(int i=0;i<packs.size();i++){
            Pack pack = packs.get(i);
            for (Predator predator : pack.members) {
                predator.color = colors[pack.id];
                XY pos = new XY(Math.random() * 30, Math.random()   * 30);
                pos.add(positions[i]);
                pos.add(offset);
                pos.setInZeroBounds(Island.SIZE, Island.SIZE);
                predator.moveTo(pos, Island.getCellByPosition(pos));
                predators.add(predator);
            }
        }
        deadPredators = new ArrayList<Predator>(predators.size());
        deadPacks = new ArrayList<Pack>(packs.size());
    }

    void populatePrey(int count, boolean center) {
        XY pos = new XY();
        for (int i = 0; i < count; i++) {
            Prey prey = new Prey();
            if(center){
                pos.x = Math.random() * 100 + 200;
                pos.y = Math.random() * 100 + 200;
            } else {
                pos.x = Math.random() * 500;
                pos.y = Math.random() * 500;
            }

            prey.moveTo(pos, Island.getCellByPosition(pos));
            preys.add(prey);
        }
        deadPreys = new ArrayList<Prey>(preys.size());
    }

    static void print(String txt){
        print(txt, false);
    }

    static void print(String txt, boolean override){
        if(!silent || override)
            System.out.println(txt);
    }

    void printStatus(){
        print("Turn " + turn + " : Prey " + preys.size()
                + " : Predators " + predators.size() + " (" + getElapsedTime() + " elapsed)");
    }

    @SuppressWarnings("serial")
    void init(String[] args) {
        startTime = System.currentTimeMillis();
        map = new Island();
        populatePredators(args);
        if (preyStartCount == 0)
            preyStartCount = 1500 + (packs.size() * 50);

        populatePrey(preyStartCount, true);
        map.rebuildLists(preys, predators);
        img = new BufferedImage(Island.SIZE, Island.SIZE, 1);
        frame = new JFrame() {
            @Override
            public void paint(Graphics g) {
                g.drawImage(img, 32, 32, null);
            }
        };
        frame.setSize(Island.SIZE+64, Island.SIZE+64);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                for (Pack pack : packs)
                    pack.handler.end();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                } finally {
                    for (Pack pack : packs)
                        pack.handler.shutdown();
                }
            }
        });
    }
}

Động vật ăn thịt

import java.awt.Graphics2D;


public class Predator extends Creature {

    static int count = 0;

    int id;
    int hunger;
    Pack pack;

    public Prey eatOrStarve() {
        for (Prey prey : preys) {
            if (prey.alive && pos.isCloserThan(prey.pos, eatDist)) {
                prey.die();
                hunger = MAX_HUNGER;
                return prey;
            }
        }
        if (hunger-- < 1)
            die();
        return null;
    }

    @Override
    public void die() {
        super.die();
        pack.aliveCount--;
        Game.print(pack.toString() + " starved! " + pack.aliveCount + " members remaining.");
    }

    @Override
    void paint(Graphics2D g){
        g.setPaint(color);
        int size = ((hunger + 10) > MAX_HUNGER && Game.turn > 10) ? 3+(int)Math.pow((hunger+10-MAX_HUNGER)/4,3) : 3;
        g.drawOval((int)pos.x - 1, (int)pos.y - 1, size, size);
        g.fillOval((int)pos.x - 1, (int)pos.y - 1, size, size);
    }

    Predator(Pack pack) {
        super();
        id = count++;
        this.pack = pack;
        MAX_SPEED = 6.1;
        VISIBLE = 50;
        hunger = MAX_HUNGER;
    }

    final double eatDist = 1;
    final static int MAX_HUNGER = 1000;
}
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.