헝거 게임-식사 또는 죽기


60

헝거 게임-식사 또는 죽기

먹지 않으면 죽습니다. 먹으면 죽을 때까지 산다. 당신 죽을 것이다 , 그래서 마지막으로 죽어보십시오.

개요

먹이 동물 무리로 채워진 섬이 있습니다. 당신은 5 명의 포식자 무리를 통제합니다. 당신의 목표는 팩을 살아있게하는 것입니다. 먹이를 먹음으로써 이것을하십시오. 먹이는 포식자에게서 도망 치는 경향이 있으며, 그렇지 않으면 무리에 머물려고 노력합니다. 물론, 당신의 팩은 다른 모든 팩 과 같은 필드에 있을 것입니다. 낙심하지 마십시오. 굶주리게됩니다.

게임 방법

팩을 지시하는 명령 행 프로그램을 작성하여 제출하십시오. STDIN의 제어 프로그램에서 상태 정보를 수신하고 STDOUT의 명령을 출력합니다. 형식은 아래에 자세히 설명되어 있습니다. 각 프로그램은 한 번만 실행되며 더 이상 팩 구성원이 없을 때까지 실행 상태유지 해야합니다 . 입력되는 내용을 읽고 신속하게 응답해야합니다. 각 응답에 대해 200ms의 엄격한 시간 초과가 있습니다. 그때까지 응답하지 않으면 팩은 현재 턴에 대한 새로운 지시를받지 않습니다.

컨트롤러에서 프로그램을 실행할 수 없으면 유효한 것으로 간주되지 않습니다. 제출을 실행하는 데 사용해야하는 명령 줄 문자열을 포함하십시오. 컴파일러를 설정하는 등의 특별한 지시 사항이 있으면 포함 시키십시오. 작동하지 않으면 의견에 대한 도움을 요청합니다. 응답하지 않으면 제출을 수락 할 수 없습니다.

토너먼트는 64 비트 Linux 시스템에서 진행됩니다. 필요한 지시를 할 때 이것을 명심하십시오.

세부

  • 각 생물체의 위치와 방향은 각각 과 좌표를 double나타내는 한 쌍의 배정도 부동 소수점 숫자 (예 :)의 형태입니다 .xy

  • 각 생물은 한 점으로 간주됩니다. 즉, 동일한 공간에서 겹치거나 차지할 수 있습니다. 당신은 옆으로 부딪치지 않으며 다른 생물과의 충돌의 개념이 없습니다.

  • 섬은 정사각형이며 측면에 500 단위입니다. 당신이 그 한계를 넘어 모험을 시도하면, 당신은 가장자리에 고정됩니다. 원점 {0,0}은 왼쪽 상단 x에 있으며 오른쪽으로 y증가하고 아래쪽으로 증가합니다. 다시, 맵 은 랩핑되지 않습니다 .

  • 게임은 1500 + (packCount * 50) 먹이 동물로 시작합니다. 그들은 섬의 중앙에 모일 것이지만 빨리 움직이기로 결정합니다.

  • 팩은 주변 둘레에 고른 간격으로 배치됩니다. 팩 주문이 섞여 있으므로 특정 위치에서 시작하지 마십시오.

  • 먹이 동물은 30 단위 반경 내의 다른 모든 동물을 볼 수 있습니다 . 한 턴에 최대 6.0 단위 로 움직일 수 있습니다 .

  • 육식 동물은 내 다른 모든 동물을 볼 수있는 50 그들은 최대로 이동할 수 있습니다 단위 반경 6.1 회전 당 단위. 즉, 먹이를보기 전에 먹이를 볼 수 있고, 간신히 뛰어 넘을 수 있습니다.

  • 포식자들은 그들의 기아 수준 에 따라 살고 죽 습니다 . 1000 에서 시작 하여 1 회전마다 감소합니다. 만약 이동 후 포식자가 1 단위의 먹이 안에 있으면 자동으로 먹습니다. 이것은 먹이를 제거하고 포식자의 굶주림을 1000으로 설정합니다. 각 포식자는 한 번에 하나의 먹이 만 먹을 수 있습니다. 범위 내에 둘 이상이 있으면 루프가 가장 먼저 도착하는 것을 먹습니다 (필자는 가장 가까운 것은 아닙니다). 기아가 0에 도달하면 포식자는 죽습니다.

  • 팩은 각각 5 명의 멤버로 시작 합니다. 5000 턴 마다 , 게임에 남아있는 모든 팩은 하나의 새로운 멤버를 생성합니다. 동료 팩 구성원의 가시 범위 내에 배치됩니다. 출품작이 5 명 이상의 회원을 처리 할 수 ​​있는지 확인하십시오.

  • 1000 턴 마다 더 많은 먹이가 생성됩니다. 새로운 먹이의 수는 살아있는 포식자 수에서 1을 뺀 것입니다.

  • 포식자는 다른 포식자를 공격 할 수 없습니다. 그들은 잡을 때 먹이를 먹는다. 그게 다야.

  • 턴 내 순서는 다음과 같습니다.

    • 모든 먹이는 결정을 내립니다
    • 모든 포식자 결정
    • 모든 먹이 이동
    • 모든 포식자 이동 / 먹기
  • 각 팩이 결정 / 이동하는 순서는 매 턴마다 무작위로 결정됩니다.

프로토콜 (일반)

모든 통신은 문자열 형식으로 수행됩니다 US-ASCII. 숫자는 자바의 사용 문자열로 변환 Double.toString()또는 Integer.toString(). 출력은 Java에서 읽을 수 있도록 형식화되어야합니다 Double.valueOf(String)(정수는 출력하지 않음). 구문 분석 가능한 형식에 대한 자세한 내용은의 설명서를Double 참조하십시오 . 줄의 모든 필드는 표준 \t문자 로 구분되며 줄 바꿈은 \n입니다. 전체 문자열은 null 바이트로 종료됩니다 \0.

아래 예제에서는 <>가독성을 위해 필드를 표시하는 데 사용 하고 있습니다. 실제 문자열에는 없습니다.

프로토콜 (입력)

입력 문자열은 팩에 보이는 생물의 수에 따라 길이가 다릅니다. 100k자를 초과 할 수 있으므로 준비하십시오. 기본 설정은 다음과 같습니다.

  • 라인 0 : 게임에 대한 기본 정보. turn는 현재 턴 번호이며 카운트는 필드에 남은 먹이와 포식자 수입니다. 이들은 integer문자열 형태입니다.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • 1 행 : 팩 멤버의 고유 ID 및 기아 수준. 모든 입력에 대해 동일한 순서로 제공되는 것은 아닙니다 . 입력에 나타나는 순서가 아니라 고유 한 ID를 사용하여 개별 멤버를 추적 하십시오. 다시, 이것들은 integer문자열입니다. 두 팩의 경우 다음과 같습니다.

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • 2 행 : 1 행과 동일한 순서로 팩 구성원의 위치 . 이것들은 double문자열입니다 :

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

다음 라인은 각 라인 멤버에 표시 되는 순서와 동일한 순서로 각 팩 멤버의 가시성 입니다. 이들은 멤버 당 두 줄로 제공됩니다.

각각에 대한 첫 번째는 그가 볼 수 있는 먹이 의 위치로 구성됩니다 . 두 번째는 그가 볼 수 있는 포식자의 위치입니다 . 이 위치는 전체적으로 고유하지 않습니다. 이 개 팩 구성원이 같은 동물을 볼 수있는 경우 예를 들어, 그것은있을 것 모두 회원의 문자열입니다. 또한 자신의 팩 구성원 포함됩니다. 제외하려는 경우 위치를 자신의 회원과 비교할 수 있습니다. 모든 위치는 double문자열 형식입니다.

각 살아있는 회원에 대해 :

<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

마지막 \0줄은 다음 줄의 시작 부분에 있습니다.

예외 : 입력을 받으면 dead\0팩이 죽은 것입니다. 프로그램을 정상적으로 종료하십시오. 컨트롤러 닫힐 때 모든 살아있는 프로세스를 종료 해야 하지만, 좀비 프로세스가 필요하지 않습니다. 무료로 입력 시간 초과를 포함 할 수 있습니다. 예를 들어, 예제 클래스는 15 초 동안 입력을받지 않으면 종료됩니다.

프로토콜 (출력)

출력은 간단하다. double각 라이브 팩 멤버에 대해 한 쌍의 값을 제공합니다 . 이것들은 당신이 이번 차례에 원하는 움직임을 나타냅니다. 예를 들어, 당신의 생물이 현재 {100.0, 100.0}있고 당신에게 그들에게 명령을 주면 {-1.0, 1.0}, 그들은로 이동합니다 {99.0, 101.0}. 모든 숫자는 한 줄에 탭으로 구분됩니다.

예를 들어 팩 구성원이 3 명인 경우 유효한 응답입니다.

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

이렇게하여 생물을 이동 것 {1.0,-1.0}, {2.0,-2.0}하고 {3.0,-3.0}. 순서는 입력에서받은 것과 동일합니다. 결말을 잊지 마세요 \0!

잘못된 입력을하면 잘못된 결과가 나타납니다. 단일 숫자를로 구문 분석 할 수 없으면 double0이됩니다. 문자열 전체를 구문 분석 할 수 없으면 새로운 지침이 제공되지 않으며 전체 팩에서 이전 차례의 지침을 사용합니다.

모든 방향은 6.1 단위의 최대 거리로 고정됩니다. 원한다면 이보다 느리게 움직일 수 있습니다. 예를 들어 {1, 0}한 단위로 이동합니다. {6,8}(거리 10)은 6.1 유닛만큼 이동하며 약으로 줄어 듭니다 {3.66, 4.88}. 방향은 일정하게 유지됩니다.

중요 사항 : 제어 프로그램은 STDOUT STDERR을 모두 읽습니다 . 예외를 던져서 STDERR로 인쇄하면 메시지가 올바른 응답의 형태 일 가능성은 거의 없습니다. 이것을 피하십시오.

제어 프로그램 / 테스트

컨트롤러의 소스 는 bitbucket.org에서 찾을 수 있습니다 . 실행하기 전에 컴파일해야합니다. 기본 클래스는입니다 Game. 모든 클래스는 기본 패키지에 있습니다. 실행하려면 각 팩의 명령을 별도의 인수로 포함하십시오. 예를 들어 Java ChaserPack과 Python LazyPack.py를 실행하려면 다음을 사용할 수 있습니다.

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

지도에서 먹이는 녹색으로, 포식자는 빨간색으로 나타납니다. 그러나 인수로 제공된 첫 번째 팩 중 어느 팩이든 파란색으로 표시됩니다. 이것은 테스트 목적으로보다 쉽게 ​​구별하기위한 것입니다. 육식 동물은 먹을 때 5 프레임 동안 흰색으로 깜박입니다.

게임은 마지막 육식 동물이 굶어 죽을 때까지 진행되며 기아 또는 멸종 사건이 발생하면 콘솔에 기록합니다. 게임이 완료되면 각 팩마다 점수가 부여됩니다. 기아 / 소멸 이벤트를보고 싶지 않으면 -silent인수를 사용할 수 있습니다 . 그런 다음 최종 점수 만 출력합니다. 이것을 첫 번째 인수로 전달해야합니다 .

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

라는 해골 Java 팩이 포함되어 GenericPack있습니다. 필요한 기본 입출력 작업이 포함됩니다. 구문 분석하고 응답하는 방법에 대한 명확한 예를 제공합니다. 다른 언어로 된 템플릿을 추가하려면 알려주세요.

템플릿을 기반으로하는 포식자도 포함되어 있습니다 ChaserPack. 토너먼트에는 포함되지 않으며 테스트 목적으로 만 포함됩니다. 의도적 인 타겟팅 결함으로 인해 성능이 크게 저하됩니다. 이길 수 없다면 계속 노력하십시오.

아래는 제어 프로그램 실행 예입니다 (비디오를 보려면 클릭하십시오). 비디오 품질은 좋지 않지만 (미안) 먹이의 움직임에 대한 느낌을 얻을 수 있습니다. ( 주의 : 오디오 )

스크린 샷

채점

우승자는 토너먼트에 의해 결정되며 각 라운드에서 포인트를 얻습니다.

각 라운드는 모든 포식자가 죽을 때까지 진행됩니다. 각 팩은 마지막 회원이 기아로 사망 한 시점을 기준으로 점수가 매겨집니다. 그러면 순서에 따라 포인트가 할당됩니다. 점수는 10 라운드 동안 누적되며 승자는 총점이 가장 높은 팩입니다.

각 라운드의 첫 번째 장소는 100 점을받습니다. 그 후 각 장소에 대해 보상이 20 %만큼 줄어 듭니다 (반올림). 포인트가 0이 될 때까지 (17 위 이후) 계속됩니다. 18 세 이상은 라운드에서 점수를받지 않습니다. 묶는 팩은 동등한 점수를받습니다. 예를 들면 다음과 같습니다.

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

토너먼트 과정에서 가능한 최대 점수는 처음부터 10 번까지 1000 점입니다.

여러 프로그램이 1 위를 차지한 토너먼트를 종료 하면 1 위 참가작 만 제출 하여 10 회의 다른 토너먼트가 진행됩니다 . 이것은 한 명의 승자가 나타날 때까지 계속 될 것입니다.

대략 매주 또는 새로운 제출이있을 때 토너먼트를 진행하려고합니다.

추가 규칙 (놀이 페어!)

  • 외부 리소스를 읽거나 쓸 수 없습니다. 프로그램을 여러 번 호출하지 않기 때문에 상태 정보를 내부적으로 저장할 수 있습니다.

  • 다른 프로세스 / 제출을 방해하지 마십시오. 이것은 않습니다 하지 그들의 먹이를 도용하려고하지 않는다는 의미는, 그것은 프로세스의 실행을 방해하지 않는 의미 등을 상회. 이것은 나의 재량에 달려 있습니다.

  • 참가자는 최대 3 개 항목 으로 제한됩니다 . 더 제출하면 제출 한 처음 세 개만 채점합니다. 취소하려면 삭제하십시오.

  • 다른 출품작을 홍보하기위한 출품작 만 존재할 수는 없습니다. 각자 자신의 공로로 승리해야합니다.

  • 프로그램은 한 번에 최대 하나의 자식 프로세스를 생성 할 수 있습니다 ( 직접적인 것이 아닌 전체 자손). 어느 쪽이든, 시간 초과를 피하지 마십시오. Game어떤 식 으로든 클래스 자체를 호출 할 수 없습니다 .

결과-2014 년 4 월 29 일

최신 10 라운드 토너먼트 결과는 다음과 같습니다.

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

09:00 EDT 2014/04/29 이전에 제출 된 팩이이 실행에 포함됩니다.

각 라운드세부 정보를 볼 수도 있습니다 . 어떤 이유로 나는 라운드 수를 뒤로 결정하기로 결정 했으므로 "라운드 10"으로 시작합니다.

업데이트

2014/04/23 : FGreg 가 시간 초과와 관련된 버그를보고했습니다 (감사합니다). 수정 사항이 구현되었으므로 테스터는 제어 프로그램 코드를 업데이트하려고합니다.


28
이 언덕의 왕이 궁금한 점이 있습니다!
Cruncher

2
@Manu 저는 Windows 7에서 예제 봇을 작성했으며 win과 linux 모두에서 테스트했습니다. 그들에게 어떤 문제가 있습니까?
Geobits

2
이 언덕의 왕 질문은 정말 대단하고이 질문은 확실히 흥미 롭습니다. 나는 작품에서 두 가지 다른 팩을 가지고 있습니다!
mackthehobbit

2
@githubphagocyte 나는 첫 번째 타임 아웃에서 팩을 죽이고 싶지 않습니다. 단순한 프로그램조차도 40k + 회전 또는 이와 비슷한 시간마다 한 번 시간 초과되는 것을 보았 기 때문입니다. 컨트롤러에서 이름 변경을 커밋했습니다. 내가 어딘가에 하나를 그리워하지 않는 한, 턴은 이제 프로그램 전체에서 턴으로 알려져 있습니다.
Geobits

2
@Geobits 어, 그건 괜찮아. 아시다시피, 이것은 저의 물리 교수 중 한 명이 수행하는 연구 프로젝트와 정말 비슷해 보입니다. 나중에 가능하다면 조금 설명하겠습니다.
krs013

답변:


10

클레어 보이

AbleDogs에 맞도록 업데이트 된 코드

우후! 넷캣을 이겼다! 이 미래 예측 팩을 만들기 위해 약간의 수정으로 기존 코드 (신용 정보를 Geobits!)를 확장했습니다. 먹이가 어디로 움직일지를 아는 포식자를 능가하는 것은 없습니다!

내가 한 두 가지 테스트에서 내 팩은 항상 Netcats에 대해 이겼습니다. 그러나 근처에 다른 먹이가 너무 많으면 예측이 실패하므로 다른 팩이 없으면 성능이 좋지 않습니다.

아마도 나는 처음 몇 천 번의 턴 동안 먹이 수를 줄이기 위해 CivilizedBeasts의 트릭을 포함시킬 수 있습니다.

5.21 분 후에 완료
Clairvoyant (1) : 9270 턴 : 100 점
EcoCamel.pl (3) : 턴 8118 : 점수 80
넷캣 (0) : 턴 6111 : 64 점
RubyVultures.rb (5) : 턴 4249 : 점수 51
RubySpiders.rb (4) : Turn 3495 : Score 40
CivilizedBeasts (2) : 3176 턴 : 32 점
체이서 팩 (6) : Turn 2492 : Score 25

내 팩 이름에서 내가 사용하는 전략 = D를 알아야합니다.

편집 :

  • 같은 먹이를 쫓지 않도록 업데이트 된 팩 관리 시스템
  • 먹이의 수가 적을 때 방황 과정을 개선하십시오 (이는 승리에 중요합니다!).
  • 이전 버전이 막 났을 때 특별한 경우를 개선하십시오.
  • 포식자 감지 알고리즘의 버그를 수정했습니다 (현재는 정확합니다).
  • 먹이 flock[ALIGN]요소 포함
  • 음식이 부족한 경우 먹이 하나를 애완 동물로 유지하십시오
  • 팩이 먹이를 모을 수있는 굴을 만듭니다
  • 근처의 포식자를 유혹하여 우리의 먹이를 쫓아 가십시오.

나는 각 팩이 먹는 먹이의 수를 세었습니다. 결과는 다음과 같습니다.

Clairvoyant (1)는 9270 회전 (0.099 먹이 / 턴)으로 916 먹이를 소비했습니다.
EcoCamel.pl (3)은 8118 턴에서 73 마리의 먹이를 소비했습니다 (0.009 마리의 먹이 / 턴)
Netcats (0)는 6111 턴 (0.092 프레이 / 턴)에서 563 개의 먹이를 소비했습니다.
RubyVultures.rb (5)는 4249 번의 턴에서 77 마리의 먹이를 소비했습니다 (0.018 마리의 먹이 / 턴)
RubySpiders.rb (4)는 3495 턴에서 293 마리의 먹이를 소비했습니다 (0.084 마리의 먹이 / 턴)
CivilizedBeasts (2)는 3176 턴에 10 마리의 먹이를 소비했습니다 (0.003 마리의 먹이 / 턴)
ChaserPack (6)은 2492 번의 회전에서 43 개의 먹이를 소비했습니다 (0.017 개의 먹이 / 턴)

내 팩은 매우 공격적이며 RubySpiders와 마찬가지로 Netcats의 먹이를 훔쳐서 얻은 916 카운트의 대부분입니다.

EcoCamel의 중앙 낙타로 인해 CivilizedBeasts가 유실되었습니다.

그리고 EcoCamel (배고픔 500)은 매우 효율적이며, 끝까지 살아남기에 충분합니다.

또한이 업데이트 된 Clairvoyant를 통해 게임은 거의 10,000 회전에 도달하지 못합니다.

코드:

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
내 게임에서 넷캣보다 실력이 훨씬 좋아 보인다. 그러나 나는 내 짐승이 당신의 통계에서 정말 나쁜 일을하기 때문에 다른 포식자를 실행할 수 없다는 것이 싫어. (악당이 너무 좋은 반면). 어쩌면 나는 펄 컴파일러를 설치하려고 노력해야 할 것입니다.
Herjan

예, 답변에 설명 된 것처럼 중간에 포식자가 있으면 방법이 작동하지 않는다고 생각합니다. 귀하와 비슷한 다른 버전을 구현하려고했습니다. 사용 가능한 포식자 수에 따라 형태를 바꿀 수 있으므로 보는 것보다 훨씬 나쁘지는 않지만 재미있게 볼 수 있습니다.
justhalf

그렇습니다. 내 짐승은 <4 개의 포식자에게 운명이 있기 때문에 다른 전략과 마찬가지로 다른 방법으로 다른 방법으로 업그레이드 할 수 있습니다. 또는 예를 들어 중간 대신이 아닌 임의의 장소를 모을 수 있습니다. 그러나 나는 그것을 구현하기에는 너무 게으르다 (현재). 먹이가 낮아지면 내 전술이 효과가 없기 때문에 이만큼 좋은 것은 아닙니다. 그때 당신과 같은 짐승이 필요할 때입니다 (당신은 이미 내 전술로 시작하고 먹이 가이 전술을 사용하기 위해 낮아지는 것을 언급했습니다). 그래서 당신은 이미 이것을 통해 생각했다고 생각합니다.
Herjan

나는 지금 또 다른 도전에 직면하고 있으며 GeoBits는 이것에 대한 관심을 잃은 것처럼 보이므로 결과가 업데이트되지 않는 한 잠시 동안 그대로 두겠습니다. 몇 가지 다른 제출물에 대한 아이디어가 있으므로이 과제가 계속 유지되기를 바랍니다. 물론 업데이트를 살펴 보겠습니다.

15

넷캣

여기에 여러분을 시작할 수있는 팩이 있습니다. GenericPack제어 프로그램에 포함 된 클래스를 확장합니다 . 원래 게시 이후 개선되었으며 더 이상 희소 한 무리로 굶주 리지 않습니다.

Netcats는 vee 모양의 그물을 사용하여 구석에 먹이를 가두어 여가 시간에 먹을 수 있습니다. 네트는 중앙에 하나의 "헤드"부재로 형성된다. 머리가 먹으면 머리가 일반적으로 먹을 기회를 얻는 첫 번째이기 때문에 배고픈 팩 구성원과 장소를 교환합니다.

그물은 다소 작게 시작하지만, 더 효율적으로 들판을 끌기 위해 무리가 작아지면 넓어집니다.

먹이가 보이지 않으면 섬의 대부분을 포괄하는 순진한 탐색 패턴으로 형성이 넓어집니다.

팩이 두 명의 멤버에게 전달되면 네트는 작동하지 않습니다. 그 시점에서 각각 자신의 길을 가고, 탐욕스럽게 찾을 수있는 가장 가까운 것을 먹고 다른 방법으로 반 무작위 산책을합니다.

이 버전은 질문에 링크 된 비디오 에서 볼 수있는 순진한 Netcats보다 훨씬 잘 유지 됩니다.

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

루비 거미

때때로 더 적을수록 더 많은 솔루션이 어쨌든 먹이를 모 으려고 할 것입니다 ...

나는 내 팩이 분리되어 다른 사람들이 작업을 수행 할 때까지 기다릴 수 있다고 생각했다.

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

주의 사항 : 실제로 실행 상태를 유지하지 않으며 입력이 들어 오거나 빠르게 응답 할 때 입력을 읽지 않습니다. 여전히 컨트롤러와 잘 작동하므로 추가 조정없이 자격을 갖추기를 바랍니다.


4
첫 번째 기생충 용액 +1 나는 이런 유형의 답변이 허점을 점차적으로 제거함으로써 다른 답변의 질을 향상시킬 것이라고 생각한다.
trichoplax

@ githubphagocyte 나는 똑똑한 기생충을 염두에두고 있었지만 이것은 실시간 / 코드 라인 측면에서 더 효율적입니다. 구현할 시간을 찾을 수 있기를 바랍니다.
Legat

아마 @Synthetica가 내 아이디어를 코딩하고있을 것입니다. 또는 그의 아이디어가 또 다른 아이디어라면, 우리는 사냥꾼보다 더 많은 기생충을 갖게 될 것입니다.)
Legat

1
@githubphagocyte 우리는 세 가지 항목을 만들 수 있으므로 준비가되면 다른 팩을 게시 할 것입니다. 그럼에도 불구 하고이 코드가 코딩되어 더 효과적이라는 것이 흥미 롭습니다. Netcats를 실제로 잘 활용하며 실제로 첫 사냥꾼 팩보다 수명이 오래갑니다.
Legat

3
왜 그런지 알아내는 데 잠시 시간이 걸리더라도 그대로 입력 할 수 있습니다. 더 많은 Netcat을 추가할수록 더 좋습니다. +1, 코너를 피하기 위해 어떤 종류의 사냥꾼이
나오는지 봅시다

11

문명 야수

마침내 내 짐승을 과시 할 시간입니다!

저의 육종은 사냥이 다소 원시적이라고 생각하여 4 팀으로 함께 일하고 5 번째 동맹을 포기합니다. 그들이 기본적으로하는 일은 인간이하는 일이며, 먹이를 잡아 가축을 잘 돌보는 것입니다.)

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

내 가슴이 게임에서 적 넷캣만으로 +12.000 이하로 200 마리 미만의 먹이로 생존하기가 매우 어려워집니다. 이 품종은 다른 품종보다 빠른 속도로 엄청난 양의 먹이를 먹어 치우기 때문에 기뻐할 것입니다.


3
" 잘 돌봐야한다 "는 말이 " 중간에 반복적으로 무리를 지어 그들을 학살 / 먹는 것 "이라면, 그렇습니다. +1
Geobits

재미 있고, 변형되지 않은 (원래의) 이블 카멜 버전에서는 문명 전술이 '중앙 낙타'때문에 완전히 비효율적입니다.
user2846289

1
@VadimR Crap, 낙타를 업데이트 해 주셔서 감사합니다 .PI는 Java가 아니기 때문에 테스트 할 수 없지만 내 전략은 내 영토 한가운데 포식자와는 쓸모가 없다는 것을 알고 있습니다. : P
Herjan

5
또 헤르 잔입니다! 또한 "내 가슴 이 200 마리 미만의 먹이로 생존 하기가 상당히 어려워진다 "(강조 추가). 나는 당신의 가슴의 활력이 컴퓨터 시뮬레이션에서 먹이의 수에 달려 있다는 것을 깨닫지 못했습니다 ....
Justin

5

루비 독수리

좀 더 활동적인 기생충이 들어 있습니다. 그들은 가장 가까운 움직이는 포식자둘러싸 려고 노력하여 먹이를 훔칠 수 있습니다 . 그들은 따라갈 사람을 선택하는 현명한 방법이 없기 때문에 약간의 행운에 의존하지만 일반적으로 체이서와 때로는 거미를 때리고 있습니다. 있습니다.

내가 템포를 밀기 위해 이것을 올렸을 때 그들은 꽤 끝나지 않았다 :)

나는 바라고있다 :

  • 그들이 포식자를 검색 하게 시야 밖에서
  • 먹이를 고려하다 -종종 그들 중 하나가 다른 팩과 먹이 사이에 있습니다!
  • 다른 모든 음식이 잘 공급되면 굶주림을 피하기 위해 회전을 시작하십시오

2014 년 4 월 22 일 : 지루함이 추가되어 끈적 이지 않고 스스로 먹이찾고 포식자를 찾을 수 있습니다.

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

믹스에서 더 많은 "헌터"가 필요합니다. 그대로, 이들은 다른 기생충에 붙어있는 경향이 있습니다 (현장에서 대다수이기 때문에). 나는 그것들을 보는 것을 좋아하고 다른 경쟁자들과 어떻게 효과가 있는지 볼 수 있습니다.
Geobits

예, 테스트 환경에는 두 개의 다른 헌터 팩이 있습니다. 그들 없이는 독수리가 아마 실마리가 될 것입니다. 특히 넷캣은 중간에서 보지 않고 빠르게 코너를 돌 수 있습니다.
Legat

나는 그들이 무엇을 괴롭 히고 있는지 알고 있습니다. 사악한 낙타의 전쟁 춤. @Geobits 유튜브에 싸움을하는 것은 어떻습니까? 10 라운드는 시청하기에 너무 많지 않습니다. 물론 본사가 필요할 것입니다. 수백만 명의 관중을 기대하지는 않지만 팩이 어떻게 작동하는지보고 약간의 응원을하는 것은 재미있을 것입니다 :)
Legat

1
전체 토너먼트는주의를 기울이는 데 약간 길 수 있지만 (현재 라운드 당 ~ 8 분) "관중"라운드를 기록하면 효과가 있습니다. 나는 미래의 달리기를 생각할 것이다.
Geobits

@Geobits 8 분 동안 속도가 크게 변합니까? 턴당 프레임을 기록 할 가치가 있는지 궁금해서 계산 집약적 인 부분에서 속도를 늦추지 않고 일정한 속도로 재생할 수 있습니다. YouTube의 목적을 의미합니다.
trichoplax

5

이블 에코 낙타

편집 : 돌연변이 # 2. 아뇨, 나는 먹이 움직임 예측의 구현에 늦어서 넷캣을 이길 첫 번째 사람이되었습니다. 그래요

이 돌연변이는 $hunger_critical가변적이다 (상수). 1000 이상의 값으로 변경하면 Clairvoyants처럼 낙타가 항상 사냥됩니다. 그때:

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

경우 $hunger_critical예로 설정되어 500 (아래), 그 다음 나의 낙타 (의 공포 본 후에 문명을 )에만 배고픈 (따라서 그들의 품종의 이름을 변경 한), 즉 그들이 죽일 환경 친화적 인 방식으로 행동하려고합니다. 배가 고프지 않으면 다른 사냥꾼이 무의식적으로 도살하는 것을 막기 위해 중요한 섬 지역-중앙 및 모서리를 순찰합니다. 글쎄, 센터와 함께, 그것은 다소 작동합니다. 모퉁이를 돌다가는 아이디어는 먹이를 떼어 내고 고양이와 기생충의 삶을 더 어렵게 만드는 것이었다. 글쎄, 작동하지 않습니다. 어리석은 먹이는 어쨌든 모퉁이로 들어갑니다.

또한 flock[ALIGN]포식자에 의해서만 구성 요소를 추측 할 수 있으며 내 구현이 justhalf와 다릅니다. Geobits 코드의 립 오프 구현에 약간의 버그가 있어서 Camels vs Clairvoyants의 개별 사냥을보고 비교합니다.

그리고 프로그램은 오래 전부터 미안합니다.


편집 : 돌연변이 # 1. 섬은 방사능이 매우 강한 것으로 밝혀졌으며 (식생 부족과 '먹이'생물의 설명 할 수없는 성질을 설명합니다), 여기에 내 낙타의 첫 번째 돌연변이가 있습니다. 배가 고프거나 모든 사람을위한 자유 코너가 없다면 솔로 헌터가 될 수 있습니다. 헌터는 근처의 먹이를 적극적으로 추구합니다. 없는 경우 섬 중앙 주위에 넓은 원을 순찰 한 다음 가장 가까운 생물을 찾으면 쫓습니다. 불행히도 먹이의 방향이 무리에 가까워지면 예측할 수 없게되므로 솔로 체이스는 그다지 효율적이지 않습니다. 그러나 성공하면 낙타가 가장 가까운 자유 코너로 소화됩니다. 기아 수준이 특정 수준보다 낮 으면 모든 낙타가 모퉁이를 버립니다 (아마도 넷캣을 저주했을 것입니다 ( '음식이 어디에 있습니까?') )) 자체 로밍 무료로 제공됩니다. 등등.


같은 농담이 두 번이나 재미 있지 않다고 말했지만 (1) 나는 어딘가에서 시작해야했고 나는 이런 것들에 익숙하지 않다. 거미가 섬에 나타났습니다.

육식 동물 낙타에 대해 들어 본 적이 있습니까? 불쌍한 동물은 언젠가이 신이 버린 섬에서 풀이나 나무를 찾지 않기 위해 일어 났지만 식용으로 빠르게 움직일 수있는 작은 것들도 많지만 이상한 녹색이 많았습니다. 사냥 습관이 없지만 (그러나 그들은 곧 변할 것이라고 희망한다), 내 낙타는 생존하기 위해 매우 사악한 계획을 개발했다 : 그들은 각각 4 개의 모서리 중 하나로 갈라지고 5 번째는 중앙으로 간다 ( 그것은 밝혀졌습니다). 목적지에서 그들은 참을성있게 기다릴 때 낙타 전쟁 춤을 추거나 이미 존재하는 다른 동물, 거미 및 모든 것을 밟지 않으려 고합니다.

#!/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
이것은 내가이 사이트에서 지금까지 본 가장 읽기 쉬운 펄 스크립트 여야합니다.
Geobits

당신은, 그렇지 않으면 당신은 단지 실제로 Netcats의 도살에 참여되며, 효과적으로 버릴까 정확히 코너에 갈 필요가 하하
justhalf

@ justhalf, 내가 말한 것처럼 : 계획이 작동하지 않았습니다. 구석에 앉아있는 기생충도 먹이를 먹지 못했습니다. 흠-어쩌면 한 코너를 순찰하는 두 명 이상의 짐승이 도움이 될 것입니다.
user2846289

당신의 낙타는 실제로 꽤 좋습니다! 고맙게도 (나를 위해) 나는 Clairvoyants를 향상 시켰으므로, 대부분의 경우 (항상 그런 것은 아님) 마지막 팩에서 팩이 승리합니다. 흥미 롭습니다!
justhalf

1
만약 당신이 먹이에서 8 (20-2 * 6)보다 더 가까이 있다면, 현재 턴에서 먹이의 30 단위 내에있는 다른 모든 먹이의 움직임을 볼 수 있습니다. 그리고 vec속성은 기본적으로 이전 턴에서 현재 턴으로의 변위입니다. 내가 말했듯이, 우리는 이전 차례와 일치 하여 어떤 먹이가 어떤 식으로 진행되는지 알아냅니다. 우리는 먹이의 순서에 의존 할 수 없습니다. 일반적으로 먹이는 (일반적인 시나리오에서) 서로 충분한 거리를 유지하기 때문에 (> 12 단위), 대부분의 경우 우리는 이전 차례의 먹이를 현재 차례와 일치시킬 수 있습니다.
justhalf

4

AbleDogs-PHP

이 멋진 개들은 먹이 송아지를 물고 벽을 따라가는 법을 배웠습니다. 그들은 또한 새로운 먹이를 찾아 목초지를 돌아 다니는 것을 좋아합니다. 마지막으로, 그들은 칼로리가 실제로 필요하지 않으면 식사를 자제하도록 배웠습니다.

코드를 AbleDogs파일에 넣고php 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();
}
?>

일반적인 고려 사항

  • 중요한 것은 최종 게임입니다. 당신은 가장 똑똑한 사냥 알고리즘을 가질 수 있습니다. 만약 당신이 야당보다 더 빨리 마지막 몇 마리의 먹이를 발견하지 못하면, 당신은집니다.

  • 육식 동물이 혼자 먹이를 잡을 수 없다면 (적어도 쌍으로), 눈먼 운에 의존하거나 구석에 먹이를 막기에 충분한 먹이 밀도가 떨어지 자마자 축배됩니다.

  • 먹이 이동 예측기는 기본적으로 필수입니다. 자신의 예측 변수가 없으면 예측 자 기반 프로그램을 치는 것을 상상할 수 없습니다.

테일 체이스

먹이를 잡는 가장 비효율적 인 방법은 꼬리를 쫓는 것입니다. 단일 포식자가 단일 먹이를 쫓고 외부 영향 (벽, 다른 먹이 등)이 없다고 가정하면 꼬리 추적은 영원히 지속될 수 있습니다. 먹이 비전 반경을 30 단위로 입력하자마자, 먹이는 6.1의 속도 6으로 도망하므로 회 전당 .1 거리를 얻습니다. 직선으로 얻으려면 약 300 바퀴가 필요합니다.

경기장의 크기를 고려할 때, 먹이는 벽이나 모서리에 닿기 전에 500 단위의 정사각형의 최대 대각선까지 이동하며 최대 117 턴이 걸립니다.

이기는 전략은 분명히 먹이를 늦출 수있는 방법을 찾는 것입니다. 즉, 다른 포식자 나 벽 / 코너를 앞에 두는 것입니다.

예언자

먹이 속도가 6 인 먹이는 36 * pi 단위의 제곱 영역으로 이동할 수 있습니다. 잡기 반경이 1 인 경우, 다음에 먹이가 될 위치에 대한 맹목적인 추측은 1 / 36 * pi (약 1 %)의 성공 확률을 갖습니다. 분명히 그것을 향상시키기 위해 무언가가 이루어져야합니다!

시뮬레이션 엔진 코드를 보면 입력이 다음과 같은 것을 알 수 있습니다.

  • 눈에 보이는 먹이와 포식자 위치
  • 이전 먹이 속도

모든 위치를 사용할 수 있지만 이전 먹이 속도는 불가능합니다. 이 속도를 계산하는 유일한 방법은 한 차례에서 다음 차례까지 모든 단일 먹이를 추적하는 것입니다. 이는 매우 스마트 한 모션 추적 알고리즘을 구현하지 않는 한 거의 불가능합니다. 따라서 예측자는 추측해야하는 속도 기여를 제외하고 계산의 모든 항을 쉽게 재현 할 수 있습니다.

단일 먹이의 경우, 너무 많은 문제없이 속도를 추적 할 수있어, 무리로부터 격리 된 먹이를 잡을 수있는 "완벽한"예측자를 구축 할 수 있습니다. 먹이가 너무 적어서 서로 상호 작용할 수없는 최종 게임에는 기본적으로 필요한 것입니다. 먹이가 풍부하고 무리 효과가 예측자를 속일만큼 강할 때, 먹이의 깎아 지른듯한 밀도는 오류를 보상 할 것입니다. ).

가는 먹이

먹이 속도 계산에 대한 정확한 지식으로, 포식자의 위치를 ​​조정함으로써 주어진 먹이를 원하는 방향으로 "조향"할 수 있습니다.

이것은 먹이를 벽에 고정 시키거나 다른 팩 부재로 향하게한다. 나는 두 팩 구성원 사이에 먹이를 집어 넣는 것과 같은 세련된 전략을 시도했습니다. 불행히도, 이것은 두 마리의 포식자가 한 마리의 먹이를 쫓기 위해 바쁘게 유지하기 때문에 너무 많은 포식자가있는 야당이 목초지를 정찰 할 수 없기 때문에 현재의 "핀 앤 스캔"루틴보다 효율이 떨어졌습니다.

훔치는 먹이

먹이 행동의 특징 중 하나는 육식 동물의 영향이 먹이와의 거리에 비례하여 증가한다는 것입니다 (물고기가 먹이 비전 반경 내에있는 경우). 포식자가 먹이와 가장 가까울수록 먹이는 멀리 떨어집니다.

그것은 두 마리의 포식자가 먹이를 잡기 위해 경쟁 할 때 가장 가까운 것이 가장 먼저 잡아 먹는다는 것을 의미합니다. 체이서 / 프레이 축 바로 앞에 위치하는 슈퍼 스마트 컨텐더조차도 기본적으로 먹이를 컨텐더의 턱에 겁주게됩니다.

먹이를 훔치기 위해서는 최소한 한 쌍의 포식자가 필요합니다. 하나는 죽이고, 다른 하나는 먹이 비전 반경 내에서 유지되며, 가능한 한 먹이를 형성하여 영향을 극대화하고 먹이를 사냥꾼에게 향하게합니다.

게다가, 방향이 바뀔 때마다 경쟁자가 먹이를 향한 구석을자를 수있게되며, 경쟁자 뒤를 지키는 것은 행동이 시작될 때 "고어 더"가 먹이와 충분히 가까운 경우에만 가능합니다.

따라서 "도둑"의 초기 위치가 유리하고 적어도 두 번째 포식자를 구할 수 있다면 먹이 도둑질은 성공할 수있는 기회 일뿐입니다. 내 경험상 이것은 복잡할만한 가치가 없습니다.

제안 된 변경 사항

보다 복잡한 전략을 허용하기 위해 포식자를 먹이 최고 속도 이상으로 올리면 속도 초과에 비례하여 기아 포인트가 발생할 수 있습니다. 예를 들어 속도 6으로 올라가는 것은 자유롭고 6을 초과하는 모든 속도 포인트는 100 기아 포인트를 소비합니다. (6.3 비용은 1 회전 당 30 기아 포인트, 1 회 1000 기아 포인트를 태우면 1 턴 동안 16 속도에 도달 할 수 있습니다. 먹이를 잡을 수는 없습니다!).

둘 이상의 먹이를 먹기에 충분히 가까울 때 무작위 포식자에게 킬을주는 대신 게인을 나눌 것을 제안합니다 (예 : 3 명의 포식자가 각각 333.33의 기아 포인트를 얻습니다). 이로 인해 더 많은 게임 종료 전략이 가능해집니다 (예를 들어, 더 많은 기아 포인트가 있다고 생각하면 적 포식자의 그림자가 유용 할 것입니다).

첫 번째 팩의 특수 색상은보기가 다소 어렵습니다. 파란색 대신 청록색 또는 주황색을 제안합니다.


마지막으로 또 다른 경쟁자! 나는 현재의 부작용에 만족하는 먹이 도둑질을 제외하고 당신이 언급 한 모든 점을 의도적으로 구현했습니다. 당신의 의견에서 당신은 내 Clairvoyant에 대해이기는 것 같습니다? 흥미 롭습니다. 내일 확인하겠습니다. = D. 또한 GUI 업데이트를 시도하여 (적어도 나에게 따르면) 더 나은 그래픽을 볼 수 있습니다.
justhalf

나는 항상 이길 수 없습니다. 산란 할 때 마지막 먹이에 가장 가까운 사람에 따라 다릅니다. 내 능력있는 개는 통계적 우위를 가질 수 있습니다. 또한 시뮬레이션 엔진을 조정하여 각 팀을 다른 색상으로 표시했지만 결국 내 취향에 비해 너무 다채로운 것으로 판명되었으므로 첫 번째 팀에서는 파란색 대신 주황색으로, 다른 모든 빨간색으로 정착했습니다.

와우, 난 당신의 제출을 ​​방금 실행했습니다. 미친 짓입니다.기도를 계속 그렇게 할 수 있다는 것을 몰랐습니다. 먹이가 정확히 가장자리에있을 때 포식자가 탐지 할 수 없을 것이라는 생각이 들었습니다.
justhalf

그것은 당신을위한 벡터 산술입니다 :). 이것이 제가 글에서 설명하려고 한 것입니다 : 단일 먹이의 경우 모든 이동 매개 변수 (이전 속도 포함)를 알고 있으므로 적절한 먹이 속도를 생성하여 벽 근처에 서도록 포식자 위치를 계산할 수 있습니다.

1
방정식 솔루션은 하드 코딩되어 있습니다 (반복이 필요 없음). 포식자가없는 경우 기본적으로 다음 턴 속도에 먹이가 사용해야하는 벡터를 계산합니다. 먹이가 가고자하는 방향이 주어지면 먹이 속도를 그 방향으로 만드는 데 필요한 벡터 차이를 추정 할 수 있습니다. 이 벡터 차이는 먹이와 관련된 포식자 위치를 알려줍니다. 이것은 당신에게 (한도 내에서) 먹이로부터 거리를 선택할 수있는 자유의 정도를 남깁니다.

3

게으른 팩 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

이를 실행 하려면 haskell 플랫폼 이 필요합니다 . 그런 다음 runhaskell명령을 사용하여 실행하십시오. 내 팩은 먹이가 올 때까지 기다립니다.


새로운 언어의 스켈레톤 솔루션 +1 아마도 사람들이이 위에 새로운 전략을 세우는 것이 행복합니까?
trichoplax

물론이지 (그러나, 그것은 일정한 출력을 생성하고 "dead \ 0"에서 종료하는 것 외에는 아무 것도하지 않기 때문에 매우 유용할지 확실하지 않습니다.)
PyRulez

-silent그래도이 옵션 을 사용하는 사람에게이 옵션 을 사용하도록
권합니다

3

출품작이 아닙니다. 저는 항상 참여하는 각 출품작에 대해 맞춤형 색상을 추가하는 데 관심이 있습니다 .)

또한 식사 과정은 색상을 변경하여 시각화하지 않고 대신 크기를 변경하여 짧은 시간에 여러 식사 이벤트를 볼 수 있습니다.

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

Predator.java

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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.