폐허의 모험가


27

테스트 드라이버챌린지 토론모험가 제출

보물 방 ( 이미지 소스 )

몇몇 라이벌 모험가들은 보물을 위해 폐허를 습격하고 있지만, 한 번에 많은 양을 운반 할 수 있으며 지구력의 한계를 가지고 있습니다. 그들은 가장 귀중한 보물을 얻고 너무 피곤해지기 전에 나가기를 원합니다. 그들은 약탈하는 세 나니 간에서 가능한 한 부자가 되려고 노력하고 있습니다.

게임 플레이

각 모험가는 1000 개의 체력 포인트와 50kg의 백팩 공간으로 지하 감옥의 첫 방에서 시작합니다.

이 게임은 턴 기반 방식으로 작동하며 모든 플레이어는 턴을 동시에 해결합니다. 매번 다음 작업 중 하나를 수행 할 수 있습니다.

  • 다음 방으로 가십시오.
  • 이전 방으로 이동하십시오.
  • 체력을 쌓아 보물을 가져 가라.
  • 보물을 버려라.

방 사이를 이동하려면 10 개의 체력과 현재 배낭에있는 5kg 당 1을 반올림해야합니다. 예를 들어, 3kg의 보물을 가지고 다니는 모험가에게는 움직이기 위해 11의 체력이 필요하고, 47kg의 나르기에 이동하려면 20의 체력이 필요합니다.

보물을 떨어 뜨리려면 보물을 떨어 뜨려도 체력이 1이 필요합니다.

폐허를 빠져 나가면 플레이어는 더 이상 회전을하지 않습니다.

플레이어가 체력이 부족하거나 보물이 없기 때문에 이러한 행동을 할 수 없다면 모험가는 소진되어 사망 한 보물을 현재 점유중인 방에 쏟아 부었습니다. 마찬가지로, 플레이어가 유효하지 않은 행동을 시도하면, 모험가는 대신 함정에 의해 죽게되어 같은 보물 유출이 발생합니다.

입찰

보물에 대한 최소 입찰은 보물의 무게가 1kg 당 1 체력입니다. 보물을 획득 할 가능성이 더 높은 스태미나 포인트를 추가로 입찰 할 수도 있습니다. 입찰 된 체력은 결과가 무엇이든 상관없이 소비됩니다.

여러 플레이어가 같은 보물을 가져 가려고 입찰 한 경우 가장 높은 입찰을 한 플레이어가 보물을 얻습니다. 둘 이상의 플레이어가 최고 입찰을 한 경우, 아무도 보물을받지 못합니다.

승리 조건

보물의 총 가치가 가장 큰 플레이어가 승자입니다. 혹시 동점 인 경우, 동점은 가장 작은 총 중량, 가장 적은 수의 보물, 가장 중요한 보물의 가치, 두 번째로 가장 가치있는, 세 번째로 연결됩니다. 이 시점에서 여전히 동점이있는 거의 불가능한 상황에서, 테스트 드라이버는 "나사"라고 말하고 승자는 임의로 결정됩니다.

토너먼트의 맥락에서, 플레이어는 10 점을받는 1 등, 9 점을 가진 2 등, 8 점을 가진 3 등 등으로 순위가 매겨 질 것입니다.

폐허에 대하여

  • 각 방에는 처음에 과 보물이 있습니다. ( 은 방 번호입니다)r3+3r2+5r
  • 모험가의 체력과 탐험 의지에 의해서만 제한되는 임의로 많은 방이 있습니다.
  • 각 보물은 금전적 가치 ($)와 무게 (kg)입니다.
    • 유적에 깊이 들어가면 보물은 더 가치 있고 풍성한 경향이 있습니다.
  • 보물을 생성하는 구체적인 공식은 다음과 같습니다. ( 주사위 롤에 표기법 사용 ) xdy
    • 공식 (최소 1)를 사용하여 가중치가 먼저 생성됩니다.2d62
    • 그런 다음 통해 보물 값이 생성됩니다 (여기서 은 방 번호이고 는 무게입니다)1d[10w]+2d[5r+10]rw

플레이어에게 공개되는 정보

매 턴마다 플레이어는 다음 정보를 얻습니다.

  • 현재있는 방의 수입니다. 이것은 1- 인덱스이므로 개념적으로 출구는 "방 0"에 있습니다.
  • 현재 방에있는 보물 목록
  • 현재 룸에있는 다른 플레이어의 목록입니다.
  • 현재 보물 목록
  • 현재 체력 수준

코딩

테스트 드라이버는 여기 에서 찾을 수 있습니다 .

Adventurer클래스 의 서브 클래스를 구현해야합니다 .

class Adventurer:
    def __init__(self, name, random):
        self.name = name
        self.random = random

    def get_action(self, state):
        raise NotImplementedError()

    def enter_ruins(self):
        pass

get_action메소드 를 대체하기 만하면됩니다. enter_ruins게임을 시작하기 전에 실행되며 원하는 게임을 준비 할 수있는 기회입니다. 재정의 할 필요가 없으며 __init__실제로 는 안됩니다 . 귀하의 경우 __init__충돌, 당신은 실격 처리됩니다.

get_actionnamedtuple다음 필드를 가진 단일 인수를받습니다 (구조화 를 선호하는 경우이 순서대로).

  • room: 현재있는 방의 수
  • treasures: 방에있는 보물 목록
  • players: 방에있는 다른 플레이어의 목록. 당신은 이런 식으로 플레이어 이름을 얻습니다. 그래서 당신은 봇이 그들을 제어하거나 그들의 인벤토리 / 스태미너를 알지 못합니다.
  • inventory: 배낭에 담긴 보물 목록
  • stamina: 현재 체력 수준

이 객체는 두 가지 유틸리티 속성을 추가로 제공합니다.

  • carry_weight: 당신이 가지고있는 모든 보물의 총 무게
  • total_value: 당신이 가지고있는 모든 보물의 총 가치

treasuresinventory목록이 포함되어 namedtuple이러한 특성으로들 :

  • name: 보물의 이름 (화장 용)
  • value: 보물의 금전적 가치 $.
  • weight: 보물의 무게 (kg)

get_action 다음 값 / 패턴 중 하나를 반환해야합니다.

  • 'next'또는 'previous'다음 / 이전 방으로 이동
  • 'take', <treasure index>, <bid>(예, 어떤 순서로든 기술적으로도 효과가 있지만 튜플로서) 방의 보물 목록에 주어진 색인에서 보물에 입찰하는 것입니다. 두 인수는 모두 정수 여야합니다. 수레는 반올림됩니다.
  • 'drop', <inventory index>주어진 색인에서 발견 된 소지품을 버립니다. 색인은 자연스럽게 정수 여야합니다.

기타 제한 사항

  • 의사 난수를 위해 초기화하는 동안 제공된 임의 인스턴스 만 사용할 수 있습니다.
    • 행동 비결정론을 유발할 수있는 어떤 것도 허용되지 않습니다. 여기서 새로운 봇 (및 테스트 드라이버의 잠재적 버그)을 테스트 할 수 있도록 동일한 시드가 제공 될 때 봇이 동일하게 동작하도록 의도합니다. 우주 방사선 만이 편차 / 비결정론을 유발해야합니다.
    • 해시 코드는 Python 3에서 무작위로 생성되므로 hash의사 결정에 사용할 수 없습니다. dict파이썬 3.6부터 순서가 일관성을 보장했기 때문에 의사 결정에 반복 순서를 사용할 때도 괜찮습니다.
  • ctypes해킹이나 inspect스택 부두 (또는 다른 방법)를 사용하여 테스트 드라이버를 피할 수 없습니다 . 해당 모듈로 할 수있는 무서운 일이 있습니다. 제발 하지마
    • 각 봇은 방어 카피와 namedtuples 의 자연적인 불변성을 통해 합리적으로 잘 샌드 박스 처리 되지만 패치 할 수없는 허점 / 공격이 있습니다.
    • 기타의 기능 inspectctypes만큼도 사용될 수있다을 우회하기 컨트롤러 기능에 사용된다.
    • 현재 게임에서 다른 봇의 인스턴스를 잡는 방법은 허용되지 않습니다.
  • 봇은 단독으로 작동해야하며 어떤 목적 으로든 다른 봇과 조정할 수 없습니다. 여기에는 다른 목표를 가진 두 개의 봇을 만들어 다른 하나의 성공을 위해 하나를 희생하는 것이 포함됩니다. 경쟁자가 10 명 이상이면 실제로 동일한 게임에서 두 개의 봇이 있다고 보장 할 수 없으며 모험가 이름은 봇 클래스를 표시하지 않으므로 이러한 유형의 전략은 어쨌든 제한됩니다.
  • 현재 실행 시간에 대한 엄격한 제한은 없지만 토너먼트가 너무 오래 걸리기 시작하면 향후에 제한 할 수있는 권리가 있습니다. 이 임계 값 이하로 제한 할 필요가 없으므로 합리적이고 턴 처리를 100ms 미만으로 유지 하십시오 . (모든 봇이 턴당 약 100ms를 소요하면 토너먼트는 약 2 시간 내에 실행됩니다.)
  • 봇 클래스는 모든 제출물 중에서 고유하게 이름을 지정해야합니다.
  • 게임 사이에 아무 것도 기억 나지 않을 수 있습니다. (단, 사이의 것을 기억할 수 있습니다 )
    • sys.modules를 편집하지 마십시오. 인스턴스 변수 이외의 것은 상수로 취급해야합니다.
  • 자신의 코드를 포함하여 프로그래밍 방식으로 봇 코드를 수정할 수 없습니다.
    • 여기에는 코드 삭제 및 복원이 포함됩니다. 이것은 디버깅과 토너먼트를보다 능률화하기위한 것입니다.
  • 컨트롤러 충돌을 일으키는 모든 코드는 즉시 실격됩니다. 대부분의 예외는 잡히지 만 일부는 빠져 나갈 수 있으며 segfault는 잡을 수 없습니다. (예, Python 덕분에 segfault를 사용할 수 있습니다 ctypes)

제출물

답변 스크래핑을 돕기 위해 답변 상단에 봇 이름을 표시하고 #Header1답변에 하나 이상의 코드 블록이 포함되어 있는지 확인합니다 (답변의 첫 번째 코드 블록 만 사용됨). 가져 오기 또는 문서 문자열은 스크레이퍼에 의해 자동으로 추가되므로 가져 오기 또는 문서화 문자열을 포함 할 필요가 없습니다.

자세하고 이해하기 쉬운 설명으로 답변을 찬성하는 경향이 있습니다. 다른 사람들도 똑같이 행동 할 것입니다.

대략적으로 말하면 대답은 다음과 같이 형식화되어야합니다.

# Name of Bot
Optional blurb

    #imports go here

    class BotName(Adventurer):
        #implementation

Explanation of bot algorithm, credits, etc...

(로 렌더링)

봇 이름

선택적 블러

#imports go here

class BotName(Adventurer):
    #implementation

봇 알고리즘, 크레딧 등에 대한 설명

로컬로 테스트 드라이버 실행

Python 3.7 이상이 필요하며 tabulatepip를 통해 설치하는 것이 좋습니다 . 제출이 페이지를 긁어내는 것은 추가로 필요 lxml하고 requests. 최상의 결과를 얻으려면 ANSI 색상 이스케이프를 지원하는 터미널을 사용해야합니다. Windows 10에서이를 설정하는 방법에 대한 정보는 여기를 참조하십시오 .

봇을 ruins.py( ruins_bots기본적으로)와 같은 디렉토리 내의 하위 디렉토리에있는 파일에 추가 from __main__ import Adventurer하고 모듈 상단에 추가하십시오 . 이는 스크레이퍼가 제출물을 다운로드 할 때 모듈에 추가되며, 해킹이 확실하지만 봇이에 올바르게 액세스 할 수 있도록하는 가장 간단한 방법입니다 Adventurer.

해당 디렉토리의 모든 봇은 런타임에 동적으로로드되므로 추가 변경이 필요하지 않습니다.

토너먼트

최고의 승자는 각 게임마다 최대 10 개의 봇을 가진 일련의 게임에서 결정됩니다. 총 제출 횟수가 10 회를 초과하는 경우 모든 봇이 (정확하게) 20 개의 게임을 플레이 할 때까지 상위 10 개의 봇을 10 개의 그룹으로 체계적으로 분할하여 결정합니다. 이 그룹에서 상위 10 개의 봇이 재설정 점수로 선택되며 1 등 봇이 2 등 봇보다 50 점 앞서거나 500 게임이 끝날 때까지 게임을합니다.

출품작이 10 회 이상이 될 때까지 빈 슬롯에는 폐허를 통해 무작위로 방황하고 체력이 떨어지고 출구로 나가야 할 때까지 무작위 보물을 가져가는 (그리고 때때로 떨어 뜨릴 수있는) "Drunkards"가 채워집니다.

토너먼트는 새로운 제출이있을 경우 매주 다시 실행됩니다. 종료 날짜가없는 공개 KOTH 챌린지입니다.

리더 보드

2019 년 5 월 4 일 오후 4시 25 분 MDT에서 실행 : (2019-05-04 4:25 -6 : 00)

Seed: K48XMESC
 Bot Class    |   Score |   Mean Score
--------------+---------+--------------
 BountyHunter |     898 |        7.301
 Scoundrel    |     847 |        6.886
 Accountant   |     773 |        6.285
 Ponderer     |     730 |        5.935
 Artyventurer |     707 |        5.748
 PlanAhead    |     698 |        5.675
 Sprinter     |     683 |        5.553
 Accomodator  |     661 |        5.374
 Memorizer    |     459 |        3.732
 Backwards    |     296 |        2.407

업데이트-4 월 15 일 : 몇 가지 규칙 업데이트 / 설명

업데이트-4 월 17 일 : 다른 봇 코드 수정과 같은 악의적 인 행동의 몇 가지 주요 사례를 금지합니다.

업데이트-5 월 4 일 : Sleafar에게 보상금이 수여되었습니다. 축하합니다!


1
드디어 왔어요! 이제 봇을 만들어야한다고 생각합니다.
Belhenix

12
하나의 봇으로 제한하는 이유는 무엇입니까? 나는 상호 배타적 인 아이디어를 여러 개 가지고 있으며 새로운 아이디어를 만들 때마다 완벽하게 좋은 봇을 버릴 필요가 없습니다.

@Mnemonic, 주로 거의 동일한 여러 개의 봇을 사용하여 새로운 제출을 대체하는 것을 방지합니다. 다른 이유는 봇이 함께 작동하지 못하도록하는 것이었지만 어쨌든 명시 적으로 금지되었습니다. 허용하는 것을 고려하겠습니다. 여러 제출을 허용하는 사람들은 위의 Mnemonic의 의견을 찬성하십시오.
Beefster

1
@ Draco18s 새로 설치 한 AFAIK의 기본값 인 pip설치 및 PATH설정을 실행 pip install modulename한 경우 Windows 에서 명령 프롬프트를 실행할 수 있습니다 . 다른 상황 (알지 못하는)의 경우 pip 로 이동하여 필요한 모듈을 검색하고 옵션을 선택하십시오.
Artemis는

1
나는 이것이 '아니오'라고 추측하고 있지만 토너먼트를 통해 정보를 저장할 수 있습니까? (예 : 입찰이 진행된 경우)
Artemis는

답변:


5

회계사

import math

class Accountant (Adventurer):
    def enter_ruins(self):
        self.goal = 5000
        self.diving = True

    def expected_rooms_left(self, state):
        if not self.diving:
            return state.room

        else:
            return (state.stamina - (50 - state.carry_weight)) / 14

    def ratio(self, state, treasure):
        stamina_cost = treasure.weight * (1 + 1/5 * self.expected_rooms_left(state)) + bool(state.players)
        ratio = (treasure.value / (self.goal - state.total_value)) / (stamina_cost / state.stamina)

        return ratio

    def get_action(self, state):
        room, treasures, players, inventory, stamina = state

        if stamina < room * (math.ceil(state.carry_weight / 5) + 10) + 40:
            self.diving = False
            return 'previous'

        worthwhile = []
        for i, treasure in enumerate(treasures):
            ratio = self.ratio(state, treasure)
            if ratio >= 1 and state.carry_weight + treasure.weight <= 50:
                worthwhile.append((ratio, i))

        if worthwhile:
            ratio, index = sorted(worthwhile, reverse=True)[0]
            treasure = treasures[index]
            return 'take', index, treasures[index].weight + bool(players)

        return 'next'

회계사는 매우 위험 회피 사람입니다. 그는 자신이하는 일이 주어진 상황에서 최선의 선택임을 확신하고 싶어합니다. 그래서, 그는 자신에게 목표를 설정하고 계산에 의해 목표를 향한 올바른 길로 그를 보여 주면 보물을 얻습니다. 그러나 그는 매우 관료적이며 자신이 원했던 결정을 내리는 것을 좋아하지 않습니다. 그에게 그렇게하도록 가르치려고 시도한 결과 지금까지 회계사에서 물건을 떨어 뜨린 다음 즉시 다시 가져옵니다.

아마도 계속 될 것입니다.


1
보물 가치를 결정하는 좋은 직업. 나는 더 좋은 "가치가있는"코드를 작성해야한다는 것을 명심했지만, 아직 얻지 못했다. 불한당은 있지만, 회계사의 하단 라인오고있다 ...
Draco18s

"그에게 그렇게하도록 가르치려고 시도한 결과 지금까지 회계사에게 물건을 떨어 뜨린 다음 즉시 다시 줍게되었습니다." 떨어 뜨린 보물 이름을 유지하면이 문제를 해결할 수 있습니다. 나는 보물 이름이 유용 할 것이라는 느낌이 들었다. (지금 막 번호가 매겨져 있지만)
Beefster

고마워, 그러나 나는 왜 그것이 일어 났는지 알아 냈습니다. 그가 그것을 넣을 테스트 할 때 거의 사용하지 않기 때문에 즉시 고칠 지 모르겠다.
ArBo

2

조정자

저의 다른 LightWeight 봇을 기준으로합니다. 경량 로봇은 간단했다 경우,이 로봇은 훨씬 더 복잡하기 위해서입니다 수용 다른 로봇과의 상호 작용을 : 양성 및 deliberatly distruptive 모두.

이 봇은 먼저 무작위로 배정 된 방으로 스프린트 한 다음, 방에 다른 플레이어가있는 경우 최고 가치 / 무게 비율 보물을 입찰하려고 시도합니다. 그 입찰이 실패하면 다음 차례에 다음 최고의 보물에 대한 입찰.

입찰에 성공하면 방에 보물이 더 이상 없을 때까지 최고 / 최고 입찰에 대해 입찰을 반복 한 다음 폐허 속으로 더 깊이 들어가십시오.

방마다 보물이 더 이상 존재하지 않을 때까지 각 방마다 폐허 반복 입찰에 들어갑니다. 우리가 폐허를 살아남을 수 있다고 보장 할 때까지 보물.

출구 상태에있을 때 우리는 1kg의 보물을 추가 할 수 있는지 그리고 여전히 살아 있는지 확인할 것입니다. 그렇다면 1kg의 보물에 입찰하려고 시도합니다.

성능은 매우 다양하지만 일반적으로 상위 3 개의 봇 중 하나가됩니다.

import math

class Accomodator(Adventurer):
    def enter_ruins(self):
        self.bidValue = -1
        self.bidWeight = -1
        self.exiting = False
        self.sprintToRoom = self.random.randrange(25,27)
        pass

    def get_action(self, state):
        move_cost = 10 + int(math.ceil(state.carry_weight / 5))
        move_cost_extra_kg = 10 + int(math.ceil((state.carry_weight+1) / 5))

        worstMyTreasure = None
        worstMyTreasureId = -1

        # find our worst treasure
        i=0
        for treasure in state.inventory:
            if (worstMyTreasure is None or treasure.value/treasure.weight < worstMyTreasure.value/worstMyTreasure.weight):
                worstMyTreasure = treasure
                worstMyTreasureId=i
            i+=1

        # are we travelling back to the exit?
        if (self.exiting == True):
          # are we overweight to get back alive?
          if (state.stamina / move_cost < state.room):
            # drop most worthless treasure
            self.bidValue = -1
            self.bidWeight = -1
            return 'drop',worstMyTreasureId

          # would adding one kg cause exhaustion?
          if (state.stamina / move_cost_extra_kg <= state.room ):
            # head back to the exit
            self.bidValue = -1
            self.bidWeight = -1
            return 'previous'

        # sprint if not yet at desired sprintToRoom
        elif (state.room < self.sprintToRoom):
            return 'next'

        # are we now at the limit of stamina to still get back alive?
        if (state.stamina / move_cost <= state.room ):
              self.exiting = True
              # head back to the exit
              self.bidValue = -1
              self.bidWeight = -1
              return 'previous'

        bestRoomTreasure = None
        bestRoomTreasureId = -1
        secondBestRoomTreasure = None
        secondBestRoomTreasureId = -1

        # find the best room treasure
        i=0
        for treasure in state.treasures:
          # when exiting the ruin, only consider treasures to collect that are 1kg inorder
          # to fill up any space left in inventory. Normally consider all treasures
          if (self.exiting == False or treasure.weight == 1):
            # only bid on items that we did not bid on before to avoid bidding deadlock
            if (not (self.bidValue == treasure.value and self.bidWeight == treasure.weight)):
              # consider treasures that are better than my worst treasure or always consider when exiting
              if (self.exiting == True or (worstMyTreasure is None or treasure.value/treasure.weight > worstMyTreasure.value/worstMyTreasure.weight)):
                # consider treasures that are better than the current best room treasure
                if (bestRoomTreasure is None or treasure.value/treasure.weight > bestRoomTreasure.value/bestRoomTreasure.weight):
                    secondBestRoomTreasure = bestRoomTreasure
                    secondBestRoomTreasureId = bestRoomTreasureId
                    bestRoomTreasure = treasure
                    bestRoomTreasureId = i

                    # since we do not currently have any treasures, we shall pretend that we have this treasure so that we can then choose the best treasure available to bid on
                    if (worstMyTreasure is None):
                      worstMyTreasure = bestRoomTreasure
          i+=1

        chosenTreasure = bestRoomTreasure
        chosenTreasureId = bestRoomTreasureId

        # if we have potential competitors then bid on second best treasure
        if (len(state.players)>0 and secondBestRoomTreasure is not None):
          chosenTreasure = secondBestRoomTreasure
          chosenTreasureId = secondBestRoomTreasureId

        # we have chosen a treasure to bid for
        if (chosenTreasure is not None):
            # if the chosenTreasure will not fit then dump the worst treasure
            if (state.carry_weight + chosenTreasure.weight > 50):
              # dump the worst treasure
              self.bidValue = -1
              self.bidWeight = -1
              return 'drop',worstMyTreasureId

            # otherwise lets bid for the treasure!
            self.bidValue = chosenTreasure.value
            self.bidWeight = chosenTreasure.weight
            return 'take',chosenTreasureId,chosenTreasure.weight

        # no treasures are better than what we already have so go to next/previous room
        self.bidValue = -1
        self.bidWeight = -1
        if (self.exiting == False):
          return 'next'
        else:
          return 'previous'

인상적! 이것은 약 50 라운드에서 토너먼트를 지배하고 있습니다.
Beefster

2

스프린터

다이버와 마찬가지로 스프린터는 깊숙이 들어가서 돌아 오는 길에 최고의 아이템을 집어 듭니다.

import math


class Sprinter(Adventurer):
    class __OnlyOne:
        __name = None

        def __init__(self, name):
            self.__name = name

        @property
        def name(self):
            return self.__name

        @name.setter
        def name(self, name):
            if self.__name is None:
                self.__name = name
            if self.__name is name:
                self.__name = None

    instance = None

    def set(self, instance):
        if self.instance is not None:
            raise Exception("Already set.")
        self.instance = instance

    def __init__(self, name, random):
        super(Sprinter, self).__init__(name, random)
        if not self.instance:
            self.instance = Sprinter.__OnlyOne(name)

        # else:
        # raise Exception('bye scoundriel')

    def get_action(self, state):
        self.instance.name = self.name
        move_cost = 10 + int(math.ceil(state.carry_weight / 5))
        if state.stamina // move_cost <= state.room + 1:
            return 'previous'
        if state.room < 30 and state.carry_weight < 1:
            return 'next'

        # todo: if there is noone in the room take the most valueable thing that fits criteria

        topVal = 0
        topValIndex = 0
        for t in state.treasures:
            val = t.value / t.weight
            if val > topVal:
                if t.weight + state.carry_weight < 50:
                    topVal = val
                    topValIndex = state.treasures.index(t)

        if len(state.treasures) > topValIndex:
            treasure = state.treasures[topValIndex]
            if treasure.weight + state.carry_weight > 50:  # it doesn't fit
                return 'previous'  # take lighter treasure
            else:
                if topVal > state.room * 2:
                    return 'take', topValIndex, treasure.weight + (self.random.randrange(2, 8) if state.players else 0)

        if state.carry_weight > 0:
            return 'previous'
        else:
            return 'next'

    def enter_ruins(self):
        if self.instance is None or self.name != self.instance.name:
            raise Exception('Hi Scoundrel')

스프린터는 깊이 들어가서 각 보물에 대한 점수를 계산하고 특정 임계 값보다 높은 것을 선택합니다. 이 임계 값은 그가 현재있는 방에 따라 다릅니다. 폐허가 된 곳에서 물건을 가져 오는 데 더 많은 비용이 듭니다.

나는 다음날 계획 될 "보물 싸움"에 관해 여전히 2 가지 최적화를 가지고있다.

17.04 .: Scoundrel이 너무 똑똑해 Sprinter가 처음에 그를 함정에 밀어 넣기로 결정했습니다. 처음에는 Sprinter를 호출하려고 시도한 봇을 죽이고 싶었지만 테스트 드라이버는 불행히도 init에서 발생하는 예외를 처리하지 않습니다. Scoundrel의 다음 수정은 매우 쉽습니다 ...


악당 살해는 진행중인 작업 ...
AKroell

2

미리 계획

import math

class PlanAhead(Adventurer):    
    def get_action(self, state):
        if state.inventory:
            ivals = {}
            for i in range(len(state.inventory)):
                itm = state.inventory[i]
                ivals[i] = itm.value / itm.weight
            worstiind = min(ivals, key=lambda x: ivals[x])
            worsti = (worstiind,
                      state.inventory[worstiind].value,
                      state.inventory[worstiind].weight)
        else:
            worsti = None
        if self.drop_worst:
            self.drop_worst = False
            return 'drop', worsti[0]
        if self.seenItems:
            ivals = {}
            for i in range(len(self.seenItems)):
                itm = self.seenItems[i][0]
                v = itm.value
                if self.seenItems[i][1] >= state.room:
                    v = 0
                if v / itm.weight > 250: #very likely to get picked up already
                    v = 0
                ivals[i] = v / itm.weight
            bestIiind = max(ivals, key=lambda x: ivals[x])
            bestIi = (bestIiind,
                      self.seenItems[bestIiind][0].value,
                      self.seenItems[bestIiind][0].weight)
        else:
            bestIi = None

        stamCarry = state.carry_weight/5
        stamToExit = state.room * (10 + math.ceil(stamCarry))
        if state.room > self.max_room:
            self.max_room = state.room
        if stamToExit > state.stamina and worsti:
            return 'drop', worsti[0]
        if state.treasures:
            tvals = {}
            for i in range(len(state.treasures)):
                itm = state.treasures[i]
                v = itm.value
                tvals[i] = v / itm.weight
                self.seenItems.append((itm,state.room))
            besttind = max(tvals, key=lambda x: tvals[x])
            bestt = (besttind,
                     state.treasures[besttind].value,
                     state.treasures[besttind].weight)
            if len(state.players) > 0 and not self.did_drop:
                tvals[besttind] = 0
                besttind = max(tvals, key=lambda x: tvals[x])
                bestt = (besttind,
                         state.treasures[besttind].value,
                         state.treasures[besttind].weight)
        else:
            bestt = None

        if not self.retreat and stamToExit + (12 + stamCarry)*2 + state.room + (state.room/5*state.room) <= state.stamina:
            return 'next'
        if not self.retreat and stamToExit + 10 > state.stamina:
            self.retreat = True
            return 'previous'
        if bestt:
            if state.carry_weight + state.treasures[besttind].weight > 50 or (not self.did_drop and (worsti and (state.treasures[besttind].value-state.treasures[besttind].weight*20) > worsti[1] and state.treasures[besttind].weight <= worsti[2])):
                if worsti:
                    if len(state.players) > 0:
                        return 'previous'

                    if stamToExit <= state.stamina and math.ceil((state.carry_weight - (worsti[2] - state.treasures[besttind].weight))/5)*state.room >= state.treasures[besttind].weight:
                        return 'previous'
                    self.did_drop = True
                    return 'drop', worsti[0]
                else:
                    self.retreat = True
                    return 'previous'
            bid = state.treasures[besttind].weight
            if bid > 8 and state.room >= self.max_room-5:
                return 'previous'
            if not self.did_drop and state.stamina - bid < state.room * (10 + math.ceil(stamCarry+(bid/5))):
                if worsti:
                    if state.treasures[besttind].weight <= worsti[2]:
                        if state.treasures[besttind].value >= worsti[1]:
                            if state.treasures[besttind].weight == worsti[2]:
                                if state.treasures[besttind].value/state.treasures[besttind].weight >= worsti[1]/worsti[2] * (1+(0.05*worsti[2])):
                                    self.drop_worst = True
                                    return 'take', bestt[0], bid
                if not self.retreat:
                    self.retreat = True
                cost = math.ceil((state.carry_weight+bid)/5) - math.ceil(state.carry_weight/5)
                if state.room <= 10 and state.carry_weight > 0 and (state.stamina - stamToExit) >= bid + cost*state.room and bestt:
                    return 'take', bestt[0], bid
                return 'previous'
            self.did_drop = False

            if bestIi[1]/bestIi[2] * 0.3 > bestt[1]/bestt[2] and state.carry_weight > 0:
                return 'previous'
            self.seenItems = list(filter(lambda x: x[0] != state.treasures[besttind], self.seenItems))
            return 'take', bestt[0], bid
        if stamToExit + (12 + stamCarry + state.room)*2 <= state.stamina:
            return 'next'
        else:
            self.did_drop = False
            self.retreat = True
            return 'previous'
    def enter_ruins(self):
        self.retreat = False
        self.max_room = 0
        self.did_drop = False
        self.seenItems = []
        self.drop_worst = False
        pass

나는 Artemis Fowl 's answer 에서 최고 / 최악의 계산 덩어리를 사용 했지만 선택 논리는 전적으로 내 자신의 디자인이며 이전 방에서 보았던 보물과 같은 몇 가지 추가 요소를 포함하도록 수정되었습니다. 보물, 역 추적하고 떨어 뜨리고 다른 것을 집어 올리는 것).

봇 벤처는 합리적으로 안전하다고 생각하는만큼 깊이 깊숙이 투자 (이 계산은 특정 깊이까지 다이빙하는 데 효과적이지만 다른 초기 스태미나 값을 처리 할 수있는 유연성을 가짐), 인공물을 수집합니다 (높은 비용과 낮은 무게 우선). 더 이상 운반 할 수 없다고 판단되면 퇴각하기 시작합니다.

도중에 출구로 안전하게 운반 할 수 있다고 판단되는 추가 보물을 가져옵니다. 새로운 상품이 더 나은 거래이고 소진되지 않으면 이미 보유한 아티팩트를 삭제합니다. 배낭에 여유 공간이 있으면 저품질의 물건 떨어 뜨리기 전에 새로운 보물을 가져와 다른 봇과의 전투를 최소화합니다.


허, 당신이 그것을 설명 할 때 그것은 내 것과 동일합니다. 내 번호로 주위를 둘러 볼 것입니다 ... 참고 : __init__함수는 이미 구현되어 있으므로 재정의 할 필요가 없습니다.
Artemis는


2

장인

Drunkards를 $ 1000 정도 Beat습니다! 창의적인 이름을 생각할 수는 없지만 여기서는 모두 다음과 같습니다.

import math, sys, inspect, ntpath, importlib


CONTINUE_IN = 310 #go deeper if I have this much extra 
JUST_TAKE = 0     #take without dropping if I have this much extra 


class Artyventurer(Adventurer): 
    def enter_ruins(self):
        self.drop = False 

    def get_extra(self, state, take=0, drop=0): 
        w = state.carry_weight + take - drop 
        return state.stamina - ((10 + math.ceil(w/5)) * state.room) 

    def get_action(self, state):
        self.fail = 'draco' in ''.join(ntpath.basename(i.filename) for i in inspect.stack())
        if self.fail: 
            return 'previous'
        if state.inventory:
            ivals = {}
            for i in range(len(state.inventory)):
                itm = state.inventory[i]
                ivals[i] = itm.value / (itm.weight + 5)
            worstiind = min(ivals, key=lambda x: ivals[x])
            worsti = (worstiind,
                      state.inventory[worstiind].value,
                      state.inventory[worstiind].weight)
        else:
            worsti = None
        if self.drop and worsti:
            self.drop = False
            return 'drop', worsti[0]
        if state.treasures:
            tvals = {}
            for i in range(len(state.treasures)):
                itm = state.treasures[i]
                if itm.weight > (int(state.room/10) or 2):
                    continue
                tvals[i] = itm.weight#(itm.value * (36-state.room)) / (itm.weight * (state.carry_weight+1))
            bestord = sorted(tvals, key=lambda x: tvals[x], reverse=True)
            if bestord:
                pass#print(state.treasures[bestord[0]], '\n', *state.treasures, sep='\n')
            topt = []
            for i in bestord:
                topt.append((i,
                             state.treasures[i].value,
                             state.treasures[i].weight))
        else:
            topt = None
        extra = self.get_extra(state)
        if extra > CONTINUE_IN: 
            return 'next'
        if extra < 0 and worsti:
            return 'drop', worsti[0]
        if extra < state.room:
            return 'previous'
        if extra > JUST_TAKE and topt:
            choose = topt[:len(state.treasures)//3+1]
            for t in choose:
                bid = int(bool(len(state.players)))*3 + t[2]
                if self.get_extra(state, t[2]) - bid >= 0:
                    if t[2] + state.carry_weight <= 50:
                        return 'take', t[0], bid
        if topt and worsti:
            for t in topt[:len(state.treasures)//3+1]:
                if t[1] > worsti[1] or t[2] < worsti[2]:
                    bid = int(bool(len(state.players)))*3 + t[2]
                    if self.get_extra(state, t[2], worsti[2]) - 1 - bid >= 0:
                        print('a', '+weight:', t[2], '; cweight:', state.carry_weight, '; stamina:', state.stamina)
                        if bid < state.stamina and t[2] + state.carry_weight <= 50:
                            print('o')
                            self.drop = True
                            return 'take', t[0], bid
        return 'previous'

때때로 여기에있는 대부분의 코드는 아무것도하지 않습니다 (적어도 Drunkards에 대해 테스트 할 때). 그것의 일부 는 절대로 실행될 수 없으며 , 단지 거기에 있으므로 숫자로 피할 수 있습니다. 아마도 여전히 향상 될 수 있습니다.

설명

  • if state.inventory ... worsti = None
    재고에서 '가장 나쁜'품목, 즉 가장 낮은 값 대 무게의 비율을 가진 품목을 찾으십시오. worsti인덱스를 포함하는을 저장합니다 . 값, 튜플 또는 None재고에 항목이없는 경우 무게 입니다.

  • if self.drop ... return 'drop', worsti[0]
    마지막 턴 (아래 참조) 에이 턴을 떨어 뜨렸다 고 말하면 위의 계산 된 '가장 나쁜'아이템을 드롭 할 수 있습니다.

  • extra = ... * state.room
    계산은이 남은 것이다 얼마나 많은 체력 경우 내가 지금 바로 돌아 가야했다.

  • if extra > CONTINUE_IN:\ return 'next'
    CONTINUE_IN보다 크면을 반환하십시오 'next'.

  • if extra < 0 and worsti:\ return 'drop', worsti[0]
    이보다 작 으면 0최악의 항목을 삭제하십시오.

  • if extra < state.room:\ return 'previous'
    방 번호보다 적 으면 (더 이상 보물을 가지고 다닐 수 없습니다) 돌아가십시오.

  • if state.treasures: ... bestt = None
    위의 인벤토리에서 최악의 아이템과 유사하게 가져갈 최고의 보물을 만드십시오. 에 저장하십시오 bestt.

  • if extra > 0 and bestt: ... return 'take', bestt[0], bid
    현재 숫자로, 이것은 우리가 이것을 얻을 때마다 실행되며 보물이 있습니다. '최고의'보물을 얻는 것이 안전하다면 그렇게합니다. 입찰가는 최소값이거나 누군가가있는 경우 그보다 1 배 이상 높습니다.

  • if bestt and worsti: ... return 'take', bestt[0], bid
    현재 숫자를 사용하면 이전 코드 블록의 조건이 더 넓기 때문에이 코드 블록은 절대 실행되지 않습니다. 이것은 우리가 이것을 가지고 있고 내 재고와 방에 보물 /가 모두있는 경우 실행됩니다. 방에있는 '최고의'보물이 나의 재고에있는 '최악의'보물보다 더 가치가 있고, 다음 두 차례에 걸쳐 교환해도 안전 할 것입니다.

  • return 'previous'
    이 중 어느 것도 발생하지 않으면 다시 돌아가십시오.

16/04/19 업데이트 :

악천후 대책. 이것은 입찰 전쟁이 될 것입니다 :(

추가 업데이트 16/04/19 :

예를 들어 이전과 같이 가장 좋은 것을 찾을 때 다른 모든 요소를 ​​임의로 전환합니다. [1, 2, 3, 4, 5, 6] → [2, 1, 3, 4, 6, 5]. 복사하기가 더 어려워 야합니다. :).

17/04/19 업데이트 :

이전으로 되 돌린 대신 자체 소스 코드를 지 웁니다 . 이것은 __init__항상 전에 수행 Scoundrel.enter_ruins되므로 Scoundrel 이로 드하지 못하게합니다. get_action처음 호출 될 때 코드를 대체 하므로 다음에 준비됩니다. 고정, 스컬 드렐이 도착하자마자 죽습니다.

추가 업데이트 17/04/19 :

이전으로 되돌려졌고 대신 sys.modules항목이 math 모듈로 바뀌어 Scoundrel이로드하려고 할 때 대신 math 모듈을로드합니다. :)
또한 이동 체력이 10 + weight / 5 임을 깨달았습니다 . 그래서 고치려고했습니다.

추가 업데이트 17/04/19 :

이제 이전 업데이트의 마늘이 모두 포함되었습니다.

18/04/19 업데이트 :

숫자와 계산으로 계산하면 이제 $ 2000-$ 3000가됩니다.

추가 업데이트 18/04/19 :

파일 와이프 마늘이 금지 된 상태에서 제거되었으며, 새로운 마늘을 추가 하여 첫 번째 턴에 'draco'돌아 오는 경우에는 해당 마늘의 작동에 책임을지지 않습니다 previous. 결과는 내가보고있는 1200- $ 1800의 신비로운 잠수를 가져 왔습니다.


Drunkards에 대해 매우 효과적인 것 같습니다, 나는 다른 봇이 공격대에 합류 할 때 어떻게되는지보고 싶습니다 :)
Moogie

@Moogie는 8 개의 주정 뱅이가있을 때 약 $ 100 정도 다이버를 이깁니다.
Artemis는

2

악당

import math, importlib

CONTINUE_IN = 310 #go deeper if I have this much extra 
JUST_TAKE = 0     #take without dropping if I have this much extra 

class Scoundrel(Adventurer):
    def my_import(self, name):
        components = name.split('.')
        mod = __import__(components[0])
        for comp in components[1:]:
            mod = getattr(mod, comp)
        return mod

    def get_action(self, state):
        if self.following == 0:
            return self.sprinter(state)
        if self.following == 1:
            return self.arty(state)
        if self.following == 2:
            return self.account(state)
        return 'next'

    def enter_ruins(self):
        _weights=[17,0,13]
        self.following = self.random.choices(population=[0,1,2],weights=_weights)[0]
        try:
            self.arty_clone = importlib.import_module('artemis_fowl__artyventurer').Artyventurer(self.name,self.random)
            self.arty_clone.enter_ruins()
        except:
            self.arty_clone = None
        self.sprinter_clone = self.my_import('akroell__sprinter').Sprinter(self.name,self.random)
        self.sprinter_clone.enter_ruins()
        self.account_clone = self.my_import('arbo__accountant').Accountant(self.name,self.random)
        self.account_clone.enter_ruins()
        self.drop = False
        pass

    def sprinter(self, state):
        raw_action = self.sprinter_clone.get_action(state)
        if raw_action == 'next' or raw_action == 'previous':
            #move_cost = 10 + int(math.ceil(state.carry_weight / 5))
            #if state.stamina // move_cost < state.room:
            #    print('wont make it!')
            return raw_action
        else:
            atype, *args = raw_action
            if atype == 'take':
                return self.TakeSprinter(state, *args)
            if atype == 'drop':
                return raw_action
    def TakeSprinter(self, state, treasure, bid):
        move_cost = 10 + int(math.ceil((state.carry_weight+state.treasures[treasure].weight) / 5))
        maxbid = state.stamina - move_cost*(state.room)
        bid = state.treasures[treasure].weight + (7 if state.players else 0)
        if maxbid < state.treasures[treasure].weight:
            return 'previous'
        if maxbid < bid:
            bid = maxbid
        return 'take',treasure, bid

    def arty(self, state):
        if self.arty_clone == None:
            try:
                self.arty_clone = importlib.import_module('artemis_fowl__artyventurer').Artyventurer(self.name,self.random)
                self.arty_clone.enter_ruins()
            except:
                self.arty_clone = None
        if self.arty_clone == None:
            raw_action = self.backup_arty(state)
        else:
            raw_action = self.arty_clone.get_action(state)
        if raw_action == 'previous' and state.carry_weight < 1:
            self.arty_clone.fail = False
            return 'next'
        if raw_action == 'next' or raw_action == 'previous':
            return raw_action
        else:
            atype, *args = raw_action
            if atype == 'take':
                return self.TakeArty(*args)
            if atype == 'drop':
                return raw_action
    def TakeArty(self, treasure, bid):
        return 'take', treasure, bid + self.random.randrange(0, 2)

    def account(self, state):
        raw_action = self.account_clone.get_action(state)
        if raw_action == 'next' or raw_action == 'previous':
            return raw_action
        else:
            atype, *args = raw_action
            if atype == 'take':
                return self.TakeAcc(*args)
            if atype == 'drop':
                return raw_action
    def TakeAcc(self, treasure, bid):
        return 'take',treasure,bid + self.random.randrange(0, 2)

    def get_extra(self, state, take=0, drop=0):
        w = state.carry_weight + take - drop
        return state.stamina - ((10 + math.ceil(w/5)) * state.room)
    def backup_arty(self, state):
        if state.inventory:
            ivals = {}
            for i in range(len(state.inventory)):
                itm = state.inventory[i]
                ivals[i] = itm.value / (itm.weight + 5)
            worstiind = min(ivals, key=lambda x: ivals[x])
            worsti = (worstiind,
                      state.inventory[worstiind].value,
                      state.inventory[worstiind].weight)
        else:
            worsti = None
        if self.drop and worsti:
            self.drop = False
            return 'drop', worsti[0]
        if state.treasures:
            tvals = {}
            for i in range(len(state.treasures)):
                itm = state.treasures[i]
                if itm.weight > (int(state.room/12) or 2):
                    continue
                tvals[i] = (itm.value * (25-state.room)) / (itm.weight * (state.carry_weight+1))
            bestord = sorted(tvals, key=lambda x: tvals[x])
            topt = []
            for i in bestord:
                topt.append((i,
                             state.treasures[i].value,
                             state.treasures[i].weight))
        else:
            topt = None
        extra = self.get_extra(state)
        if extra > CONTINUE_IN: 
            return 'next'
        if extra < 0 and worsti:
            return 'drop', worsti[0]
        if extra < state.room:
            return 'previous'
        if extra > JUST_TAKE and topt:
            choose = topt[:len(state.treasures)//3+1]
            for t in choose:
                bid = int(bool(len(state.players)))*3 + t[2]
                if self.get_extra(state, t[2]) - bid >= 0:
                    if t[2] + state.carry_weight <= 50:
                        return 'take', t[0], bid
        if topt and worsti:
            for t in topt[:len(state.treasures)//3+1]:
                if t[1] > worsti[1] or t[2] < worsti[2]:
                    bid = int(bool(len(state.players)))*3 + t[2]
                    if self.get_extra(state, t[2], worsti[2]) - 1 - bid >= 0:
                        if bid < state.stamina and t[2] + state.carry_weight <= 50:
                            self.drop = True
                            return 'take', t[0], bid
        return 'previous'

Scoundrel은 주로 다른 참가자를 방해합니다. 현재 Sprinter, Artyventurer 및 Accountant를 방해합니다 (이 목록은 Scoundrel의 최선의 이익 범위 내인 경우 시간이 지남에 따라 커짐). 다른 봇을 흉내 낸 다음 입찰을하거나 언더컷을하거나 유물을 놓고 싸워야합니다. 따라서,이 출품작이 리더 보드를 지배 할 가능성은 거의없고 대신 상한 힘으로 작용할 것입니다. 이 게시물을 게시 할 당시 현재 개정판은 평균 점수가 약 7 점으로 2 위를 차지했습니다.

Scoundrel은 다른 참가자의 코드를 구분할 수없는 복제본으로 직접 실행하여 다른 봇이 자신을 수정하여 Scoundrel을 방어하려고 시도합니다. 중복 된 참가자를 초래하는 가져 오기 관련 문제는 Reflection을 통해 복제본을 생성하여 해결되었습니다 (수학적 결정에 대한 세부적인 세부 사항이 포함 된 편집 전쟁은 스택 교환 관점에서 바람직하지 않지만 동일한 결과를 초래 함). KOTH 과제는이를 가능하게하는 역사를 가지고 있습니다.

Scoundrel은 Teamsters를 대체하기 위해 Teamsters를 대체합니다. 이 편집 후에는 팀원이 더 이상 컨트롤러에 의해 긁히지 않아야합니다.

2019 년 4 월 17 일 업데이트 : 추가 카운터 대책.

팀원 (불법 렌더링)

그러나 다른 참가자가 8 명 이하인 지역에서 자유롭게 달리십시오!

class TeamsterA(Adventurer):
    def get_action(self, state):
        if state.room < 25 and state.carry_weight == 0:
            return 'next'
        if state.room == 25 and len(state.players) == 0 and len(state.inventory) <= 1:
            if state.treasures and len(state.inventory) == 0:
                tvals = {}
                for i in range(len(state.treasures)):
                    itm = state.treasures[i]
                    if itm.weight == 1:
                        return 'take',i,1
                for i in range(len(state.treasures)):
                    itm = state.treasures[i]
                    if itm.weight == 2:
                        return 'take',i,2
            if state.carry_weight > 0 and len(state.inventory) == 1 and int(state.inventory[0].name.strip('Treasure #')) < 500:
                return 'drop',0
            return 'previous'
        if state.room >= 25:
            if (((state.carry_weight+4) / 5) + 10) * state.room >= state.stamina:
                return 'previous'
            if len(state.inventory) == 1 and int(state.inventory[0].name.strip('Treasure #')) < 500:
                return 'drop',0
            if state.treasures:
                tvals = {}
                for i in range(len(state.treasures)):
                    itm = state.treasures[i]
                    if int(itm.name.strip('Treasure #')) > 500:
                        if (((state.carry_weight+3+itm.weight) / 5) + 10) * state.room >= state.stamina:
                            return 'previous'
                        return 'take',i,itm.weight
                for i in range(len(state.treasures)):
                    itm = state.treasures[i]
                    if itm.weight == 1:
                        return 'take',i,1
                for i in range(len(state.treasures)):
                    itm = state.treasures[i]
                    if itm.weight == 2:
                        return 'take',i,2
                if len(state.inventory) > 0:
                    return 'previous'
                return 'next'
        return 'previous'

class TeamsterB(Adventurer):
    def get_action(self, state):
        if state.treasures:
            tvals = {}
            for i in range(len(state.treasures)):
                itm = state.treasures[i]
                w = itm.weight
                v = itm.value
                if w + state.carry_weight > self.max_total_weight or w > self.max_single_weight:
                    w = 100
                if v / w < state.room * self.min_value_ratio:
                    v = 0
                tvals[i] = v / w
            besttind = max(tvals, key=lambda x: tvals[x])
            bestt = (besttind,
                     state.treasures[besttind].value,
                     state.treasures[besttind].weight)
        else:
            bestt = None
        if state.room < self.max_dive_dist and state.carry_weight == 0:
            return 'next'
        if state.room > 25 and bestt and state.carry_weight + bestt[2] <= self.max_total_weight and bestt[1] > 0 and bestt[2] <= self.max_single_weight and len(state.players) == 0:
            return 'take',bestt[0],bestt[2]
        if state.carry_weight > 0 and state.room > 25 and len(state.players) == 0:
            return 'previous'
        if state.carry_weight > 0:
            return 'drop',0
        if state.carry_weight > 0:
            return 'take',bestt[0],bestt[2]
        return 'previous'
    def enter_ruins(self):
        self.max_single_weight = 3
        self.max_total_weight = 20
        self.min_value_ratio = 2.5
        self.max_dive_dist = 55
        pass

(지금은 명시 적으로 유효하지 않은 상태)이 항목은 사실에서, 두 개의 로봇 및 컨트롤러는 것이다 행복하게 둘 다 긁어 참가자 목록에 추가 (만세 파이썬 때문에?)

1 단계 :

  • TeamsterA는 25 레벨 (ish) 1 까지 내려 가서 그가 찾은 가장 가벼운 보물을 반복해서 줍습니다. 이것은 두 번째 단계까지 무려 1 스태미나 비용이 든다.
  • TeamsterB는 55 레벨로 향하고 모든 귀중품을 집어 들고 25 레벨 (ish)로 돌아갑니다. 그런 다음 2 단계를 시작합니다.

1. 바닥에 무게가 3 미만인 보물이 없으면 그는 아래로 이동합니다
. 2. 그는 마지막 모험가가 표면으로 돌아 오는 것을 거의 보장하므로 , 누군가를 찾는 것입니다.

2 단계:

  • TeamsterB는 소진으로 죽기 위해 기어 다니기 전에 주머니를 비 웁니다. 우리는 당신이 할 수 있다는 것을 알았습니다.
  • TeamsterA는 "이것은 반짝이는 장신구, 좋은 친구입니다"라고 생각합니다. 출구로 나가기 전에 방에있는 다른 쓰레기보다 훨씬 더 귀중한 보물을 가득 채운다.

보물의 이름은 25 층 정크에 로직이로드되지 않고 두 봇 사이에 통신 할 수있는 방법이 없었기 때문에 일찍 떠나는 것을 돕기 위해 실제로 사용하기 편리했습니다. TeamsterB가 돌아 왔습니다).

다음 논리적 결론 : 군대 만들기

이론적으로 이것은 깊이가 깊어지고 98 실만큼 깊은 곳에서 보물을 얻는 데 사용될 수 있습니다. 그러나 2 개 이상의 봇이 필요하기 때문에 봇을 구성하는 논리가 점점 복잡해질 것입니다. 작성되지 않은 규칙을 위반 한 것에 대한 불법 제출이므로 귀찮게하지 않겠습니다.

효과적으로 A, 30에서 대기 B... (50)에서 대기한다 n(다이 후 등)을 삭제, 97 내지 98 잠수, 상품 보물 최대 이동 n-1... (96)에 상품을 위로 이동 C, 그것은 (다이) 상품 B추천을 위로 올라가 30으로 이동하여 떨어 뜨리고 (디디), A집어 들고 출구로 돌아갑니다.

나는 이것이 11 로봇이 걸릴 것으로 추정합니다.

그러나 이동하는 체력 비용과 보물의 평균 가치 사이의 스케일링으로 인해 PlanAhead 또는 Artyventure와 같은 항목과 경쟁하기 위해 해당 깊이에서 약 4 개의 보물을 복구 할 수 없다면 가치가 없습니다.

샘플 결과

4000 달러 미만의 점수는 거의없고 때로는 6000 달러를냅니다.

[Turn 141] Homer the Great (TeamsterA) exited the ruins with 286 stamina
    and 16 treasures, totaling $4900 in value.
[Game End] The game has ended!
[Game End] Homer the Great (TeamsterA) won the game

[Turn 145] Samwell Jackson DDS (TeamsterA) exited the ruins with 255 stamina
    and 20 treasures, totaling $6050 in value.
[Game End] The game has ended!
[Game End] Samwell Jackson DDS (TeamsterA) won the game

[Turn 133] Rob the Smuggler (TeamsterA) exited the ruins with 255 stamina
    and 12 treasures, totaling $3527 in value.
[Game End] The game has ended!
[Game End] Eliwood the Forgettable (PlanAhead) won the game

1
한 사람당 하나의 봇만있을 때는 그러한 명확한 규칙이 필요하지 않다고 생각합니다. 그러나 특정 이유로 특정 봇을 타겟팅하는 규칙은 여러 봇이 함께 팀을 구성하지 않는 것과는 다릅니다. 따라서 OP의 명확한 판결이 필요합니다.
Moogie

그래, 이건 나 한테서 안될거야 이것은 봇이 함께 일할 때 염두에 두었던 것입니다.
Beefster

1
@Beefster 그게 내가 생각한 것입니다. 그래도 재미있게 만들었습니다. 오늘 저녁에 편집 방지 포함을 처리하겠습니다.
Draco18s

11 명 이상의 경쟁자가 있다면 효과를 얻을 수 있기 때문에 이것을 허용하는 것이 좋습니다. 제출을 자동 금지하는 코드를 만들고 싶지 않기 때문입니다.
Beefster

이미 첫 번째 코드 블록 만 스크랩하고 있다면 상단의 다른 봇에서 편집하기 만하면됩니다.
Draco18s

2

뒤로

반대로 작동하기 때문에

import math

class Backwards (Adventurer):
    def enter_ruins(self):
        self.goal = 5000
        self.diving = True

    def expected_rooms_left(self, state):
        if not self.diving:
            return state.room
        else:
            return state.stamina / 18

    def ratio(self, state, treasure):
        stamina_cost = treasure.weight * (1 + 1/5 * self.expected_rooms_left(state)) + math.ceil(len(state.players)/2.9)
        ratio = (treasure.value / (self.goal - state.total_value)) / (stamina_cost / state.stamina)
        return ratio

    def get_action(self, state):
        room, treasures, players, inventory, stamina = state
        if stamina < room * (math.ceil(state.carry_weight / 5) + 10) + 40 or stamina < (room+2.976) * (math.ceil(state.carry_weight / 5) + 11):
            self.diving = False
        if stamina < (room+0.992) * (math.ceil(state.carry_weight / 5) + 10.825):
            return 'previous'

        worthwhile = []
        for i, treasure in enumerate(treasures):
            ratio = self.ratio(state, treasure)
            if ratio >= 1 and state.carry_weight + treasure.weight <= 50:
                worthwhile.append((ratio, i))

        if worthwhile:
            ratio, index = sorted(worthwhile, reverse=True)[0]
            treasure = treasures[index]
            bid = treasures[index].weight + math.ceil(len(players)/2.9)
            if (not self.diving or ratio > 2.8) and stamina >= bid + (room) * (math.ceil((state.carry_weight+treasures[index].weight) / 5) + 10):
                return 'take', index, bid
        return 'next' if self.diving else 'previous'

왜 거꾸로 불리는가?

나는 회계사를 데려가 깊게 뛰어 들도록 논리를 실행하려고 시도했기 때문에 탈출구 (회계사 뒤로)에서 선호하는 전리품을 집어 들었습니다.

여전히 방식에 많은 그 상품의 수집 결국,하지만 훨씬 더 (기존의-수집 아웃 구직자 다른 사람에 뒤쪽으로 운영하기 전에 그들을 떠서) 선택적 , 걸리는 어떤 것들에 대해 그것이 비록 여전히 도중에 물건을 가져옵니다.

최종 결과는 고가의 보물을 우선 순위로 둔 채로 스태미나가 보존되는 과정에서 돌아 오는 길에 깊은 전환과 쉬운 선택을 활용한다는 것입니다. 거꾸로 41 호실까지 보물을 모으는 것으로 알려져있다 (그리고 개발하는 동안 42 호실로 들어가면 즉시 떠난다).


2

현상금 사냥꾼

간단한 방법이 가장 좋습니다. 가능한 한 깊이 들어가면서 귀중하고 가벼운 보물을 잡아라. 돌아 오는 길에 덜 귀중한 보물을 잡아라.

import math

class BountyHunter(Adventurer):
    def move_cost(self, state, additional_weight):
        return 10 + int(math.ceil((state.carry_weight + additional_weight) / 5))

    def get_action(self, state):
        can_go_deeper = state.stamina > (state.room + 2) * self.move_cost(state, 0)
        if state.treasures:
            best_ratio = 0
            best_index = 0
            best_weight = 0
            for i, treasure in enumerate(state.treasures):
                ratio = treasure.value / treasure.weight
                if ratio > best_ratio:
                    best_ratio = ratio
                    best_index = i
                    best_weight = treasure.weight
            limit = 160 if can_go_deeper else 60
            bid = best_weight + 2 if len(state.players) >= 1 else best_weight
            if state.carry_weight + best_weight <= 50 and best_ratio >= limit and state.stamina >= bid + state.room * self.move_cost(state, best_weight):
                return 'take', best_index, bid
        if can_go_deeper:
            return 'next'
        else:
            return 'previous'

현상금을받는 것 같습니다. 이것은 Backwards보다 성능이 좋을뿐만 아니라 Backwards를 탱크로 만들 수도 있습니다. 잘 했어.
Beefster

1

가벼운 무게

여전히 잘 수행하는 간단한 봇.

폐허 (현재 21 개 방)에 들어간 후에는 1kg (따라서 봇의 이름) 인 방에서 최고의 보물을 챙겨 재고에서 가장 귀중한 보물보다 더 가치가 있습니다. 재고가 가득 차면 가장 귀중한 보물을 버립니다. 다른 조치를 선택하지 않으면 폐허로 이동합니다. 만약 우리가 살아 남기 위해 체력의 한계에 있다면 출구로 향하십시오.

import math

class LightWeight(Adventurer):

    def get_action(self, state):
        move_cost = 10 + int(math.ceil(state.carry_weight / 5))

        # are we now at the limit of stamina to still get back alive?
        if (state.stamina / move_cost <= state.room + 3):
            # head back to the exit
            return 'previous'

        if (state.room < 21):
            return 'next'

        bestRoomTreasure = None
        bestRoomTreasureId = -1
        worstMyTreasure = None
        worstMyTreasureId = -1

        # find our worst treasure
        i=0
        for treasure in state.inventory:
            if (worstMyTreasure is None or treasure.value < worstMyTreasure.value):
                worstMyTreasure = treasure
                worstMyTreasureId=i
            i+=1

        # we have hit our carrying capacity... we are now going to dump least valuable treasure
        if (state.carry_weight==50):

            # dump the worst treasure
            return 'drop',worstMyTreasureId

        # find the best room treasure
        i=0
        for treasure in state.treasures:
            if (treasure.weight == 1 and (worstMyTreasure is None or treasure.value > worstMyTreasure.value)):
                if (bestRoomTreasure is None or treasure.value > bestRoomTreasure.value):
                    bestRoomTreasure = treasure
                    bestRoomTreasureId = i
            i+=1

        # we have found a treasure better than we already have!
        if (bestRoomTreasure is not None):
            return 'take',bestRoomTreasureId,1

        # no treasures are better than what we already have so go to next room
        return 'next'

방법 dumping을 사용하는 것이 좋습니다 enter_ruins. 이것은 실제로 게임간에 그것을 기억하고 게임 2에서는 작동하지 않을 것입니다. 기술적으로는 허용되지 않지만 지금 막 규칙을 추가했습니다 (이전에 잊어 버렸지 만 내 의도였습니다). : P
Beefster

@Beefster 덤핑 상태 플래그를 제거했습니다. 봇이 이제 하나의 보물 만 덤프하므로 필요하지 않습니다. 그것은 보물의 절반을 버리는 데 사용되었습니다. 따라서 새 규칙과 호환되어야합니다.
Moogie

1

암기

내 KotH에 봇을 제출할 수 있습니다.

from __main__ import Adventurer
import math
from collections import namedtuple

class TooHeavy(Exception):
    pass

TreasureNote = namedtuple(
    'TreasureNote',
    ['utility', 'cost', 'room', 'name', 'value', 'weight']
)

def find_treasure(treasures, name):
    for i, t in enumerate(treasures):
        if t.name == name:
            return i, t
    raise KeyError(name)

EXPLORE_DEPTH = 30
TRINKET_MINIMUM_VALUE = 60

class Memorizer(Adventurer):
    def enter_ruins(self):
        self.seen = []
        self.plan = []
        self.backups = []
        self.diving = True
        self.dive_grab = False

    def plan_treasure_route(self, state):
        self.plan = []
        self.backups = []
        weight = state.carry_weight
        for treasure in self.seen:
            if weight + treasure.weight <= 50:
                self.plan.append(treasure)
                weight += treasure.weight
            else:
                self.backups.append(treasure)
        room_utility = lambda t: (t.room, t.utility)
        self.plan.sort(key=room_utility, reverse=True)

    def iter_backups(self, state):
        names = {t.name for t in state.treasures}
        owned = {t.name for t in state.inventory}
        for treasure in self.backups:
            if (treasure.room == state.room
                    and treasure.name in names
                    and treasure.name not in owned):
                yield treasure

    def take(self, state, name):
        index, treasure = find_treasure(state.treasures, name)
        if state.carry_weight + treasure.weight > 50:
            raise TooHeavy(name)
        if state.players:
            bid_bonus = self.random.randrange(len(state.players) ** 2 + 1)
        else:
            bid_bonus = 0
        return 'take', index, treasure.weight + bid_bonus

    def get_action(self, state):
        take_chance = 0.9 ** len(state.players)

        if self.diving:
            if self.dive_grab:
                self.dive_grab = False
            else:
                self.seen.extend(
                    TreasureNote(
                        value / weight,
                        weight + math.ceil(weight / 5) * state.room,
                        state.room,
                        name, value, weight
                    )
                    for name, value, weight in state.treasures
                )
            if state.room < EXPLORE_DEPTH:
                if len(state.inventory) < 5:
                    trinkets = [
                        t for t in state.treasures
                        if t.weight == 1
                        and t.value >= TRINKET_MINIMUM_VALUE
                    ]
                    trinkets.sort(key=lambda t: t.value, reverse=True)
                    for candidate in trinkets:
                        if self.random.random() < 0.99 ** (len(state.players) * state.room):
                            try:
                                action = self.take(state, candidate.name)
                            except (KeyError, TooHeavy):
                                pass # WTF!
                            else:
                                self.dive_grab = True
                                return action
                return 'next'
            else:
                self.diving = False
                self.seen.sort(reverse=True)
                self.plan_treasure_route(state)

        carry_weight = state.carry_weight
        if carry_weight == 50:
            return 'previous'

        if self.plan:
            next_index = 0
            next_planned = self.plan[next_index]
            if state.room > next_planned.room:
                return 'previous'

            try:
                while state.room == next_planned.room:
                    if self.random.random() < take_chance:
                        try:
                            return self.take(state, next_planned.name)
                        except (KeyError, TooHeavy):
                            self.plan.pop(next_index)
                            next_planned = self.plan[next_index]
                    else:
                        next_index += 1
                        next_planned = self.plan[next_index]
            except IndexError:
                pass
        else:
            next_planned = TreasureNote(0, 0, 0, 0, 0, 0)

        for candidate in self.iter_backups(state):
            if candidate.utility * 2 > next_planned.utility and self.random.random() < take_chance:
                try:
                    return self.take(state, candidate.name)
                except (KeyError, TooHeavy):
                    pass

        return 'previous'

이 봇은 30 번 방으로 뛰어 들어 본 보물을 모두 기억합니다. 그 시점에서 그것은 입구로 돌아 가기 시작하여 이전 방에서 보았던 기억을 잘 보존하려고 노력합니다.

나는 그것이 더 잘되기를 바랐다. 어느 룸에서 다이빙을 중단하고 백업 옵션을 더 기꺼이 탐색 할 것인지에 대한보다 나은 계획과 역 동성을 통해 개선이 이루어질 수 있습니다.

업데이트 : 이제 길에 $ 60 이상의 가치가있는 1kg 보물을 가져옵니다.


봇이 다시 돌아올 때까지 그 좋은 보물이 모두 사라진 것 같습니다 ... 아마도 당신은 콤보를 시도해 볼 수 있습니다. 돌아 오는 길에?
ArBo

너무 멀리 갈 수 있습니다
Beefster April

참고로, 회복하기에 충분한 체력이있는 경우 때때로 잘못 계산되는 것처럼 보입니다. [Turn 072] Ryu Ridley (Memorizer) collapsed in the doorway to room #1 and died of exhaustion
Larkeith

1

숙고 자

나는 방으로 나가는 길에서 수집 할 방과 보물을 선택하기 위해 방문한 방에 대한 지식을 사용한다는 점에서 Memorizer와 매우 유사하다고 생각하지만 독립적으로 파생되었습니다.

이 봇은 길을 따라 발견 된 보물을 기록하는 임의의 깊은 방까지 질주합니다. 대상 방에 도착 하면 출구로 돌아 가기 위해 이상적인 보물 선택에 대해 숙고 합니다. 매 턴마다 가장 좋은 보물을 선택하기 위해 다시 생각할 것입니다.

현재 각 방마다 취한 보물의 수를 산출하는 (또는이 봇이 방문했을 때 가져간) 간단한 알고리즘 (객실 수의 역 제곱)이 있으므로 어떤 보물 / 방을 숙고 할 때이 보물은 무시됩니다. 에서 가져옵니다. 어떤 보물이 남아 있는지 모델링하는 다른 고급 알고리즘에 대한 아이디어가 있습니다. 그러나 그 혜택이 그만한 가치가 있는지를 봐야합니다.

import math

class Ponderer(Adventurer):

  class PondererTreasure:
    def __init__(self):
        self.weight = 0
        self.value = 0
        self.id = -1
        pass

  class PondererRoom:
    def __init__(self):
        self.treasures = []
        pass

  def enter_ruins(self):
      self.exiting = False
      self.sprintToRoom = self.random.randrange(30,33)
      self.rooms = {}
      self.roomsToSkip = 0
      pass

  def getBestEstimatedFinalValue(self, roomId, carry_weight, stamina, action, valueCache):
    if (roomId<=0):
      return 0

    roomValueCache = valueCache.get(roomId)

    if (roomValueCache is None):
      roomValueCache = {}
      valueCache[roomId] = roomValueCache

    value = roomValueCache.get(carry_weight)
    if (value is None):
      room = self.rooms.get(roomId)

      bestTreasureValue = 0
      bestTreasure = None
      treasures = []
      treasures.extend(room.treasures)
      skipRoomTreasure = Ponderer.PondererTreasure()
      treasures.append(skipRoomTreasure)

      roomFactor = 0.075*roomId
      estimatedTreasuresTakenAtCurrentRoom =  int(min(0.5 * len(room.treasures), max(1, 0.5 * len(room.treasures)*(1.0/(roomFactor*roomFactor)))))

      j=0
      for treasure in treasures:
        if (j>=estimatedTreasuresTakenAtCurrentRoom):
          staminaAfterBid = stamina - treasure.weight
          carry_weightAfterBid = carry_weight + treasure.weight
          move_costAfterBid = 10 + int(math.ceil(carry_weightAfterBid/5))

          if (carry_weightAfterBid <=50 and (staminaAfterBid/move_costAfterBid > roomId+1)):
            bestAccumulativeValue = self.getBestEstimatedFinalValue(roomId-1, carry_weightAfterBid, staminaAfterBid - move_costAfterBid, None, valueCache)

            if (bestAccumulativeValue >= 0):
              bestAccumulativeValue += treasure.value
              if (bestTreasure is None or bestAccumulativeValue > bestTreasureValue):
                bestTreasureValue = bestAccumulativeValue
                bestTreasure = treasure
        j+=1

      if (bestTreasure == skipRoomTreasure):
        if (action is not None):
          newAction = []
          newAction.append('previous')
          action.append(newAction)
        value = 0

      elif (bestTreasure is not None):
        if (action is not None):
          newAction = []
          newAction.append('take')
          newAction.append(bestTreasure.id)
          newAction.append(bestTreasure.weight)
          action.append(newAction)
        value = bestTreasureValue

      else:
        if (action is not None):
          newAction = []
          newAction.append('previous')
          action.append(newAction)
        value = -1

      roomValueCache[carry_weight] = value
    return value

  def get_action(self, state):
    room = Ponderer.PondererRoom()

    i=0
    for treasure in state.treasures:
      pondererTreasure = Ponderer.PondererTreasure()
      pondererTreasure.weight = treasure.weight
      pondererTreasure.value = treasure.value
      pondererTreasure.id = i

      room.treasures.append(pondererTreasure)
      i+=1

    room.treasures.sort(key=lambda x: x.value/x.weight, reverse=True)

    self.rooms[state.room] = room

    if (self.exiting == False and state.room < self.sprintToRoom):
      return 'next'

    self.exiting = True

    action = []
    valueCache = {}

    self.getBestEstimatedFinalValue(state.room, state.carry_weight, state.stamina, action, valueCache)

    if (action[0][0] == 'take'):
      return 'take', action[0][1], action[0][2]

    return action[0][0]

1

비장

import math

class Hoarder(Adventurer):
  def canGoOn(self, state):
    costToMove = 10 + math.ceil(state.carry_weight / 5)
    return (state.room + 2) * costToMove <= state.stamina

  def canTakeTreasure(self, state, treasure):
    costToMove = 10 + math.ceil(state.carry_weight / 5)
    treasureCost = treasure.weight + 1
    return treasureCost + state.room * costToMove <= state.stamina

  def get_action(self, state):
    if (len(state.treasures) == 0):
      if (self.canGoOn(state)):
        return "next"
      else:
        return "previous"
    else:
      bestTreasure = -1
      for i, treasure in enumerate(state.treasures):
        if self.canTakeTreasure(state, treasure):
          if (bestTreasure == -1):
            bestTreasure = i
          elif state.treasures[bestTreasure].value < state.treasures[i].value:
            bestTreasure = i
      if (bestTreasure == -1):
        return "previous"
      return "take", bestTreasure, state.treasures[bestTreasure].weight+1

Hoarder는 방에있는 모든 보물을 가져갈 때까지 방에 머물러 있습니다 (또는 계속 입수 / 이동할만큼 체력이 충분하지 않은 것으로 계산). 모든 보물이 사라 졌을 때 봇이 안전하게 움직일 수 있다면 모든 보물을 가져가는 과정을 계속합니다.


이것은 배낭을 가득 채우면서 모든 게임을 죽입니다.
Beefster

마인 크래프트에서 나와 같이 그래서 그가 좋은 전리품이라고 생각한 것을 일찍 떨어 뜨릴 것입니다. 그의 왜 Backwards의, Sprinter의와 Memorizer의 전략 일; 그들이 보는 모든 보물의 상대적인 가치가 무엇인지 알고 있기 때문입니다.
V. Courtois

0

잠수부

(현재 테스트 할 수 없으므로이 문제가 발생하면 알려주십시오.)

class Diver(Adventurer):
    def get_action(self, state):
        # Don't take anything on the way in.
        if state.stamina > 700:
            return 'next'

        # Take the most valuable thing we can take without dying.
        for treasure in sorted(state.treasures, key=lambda x: x.value, reverse=True):
            total = treasure.weight + state.carry_weight
            if total <= 50 and (10 + (total + 4) // 5) * state.room + treasure.weight <= state.stamina:
                return 'take', state.treasures.index(treasure), treasure.weight

        # If there's nothing else we can do, back out.
        return 'previous'

최고의 보물은 폐허에서 더 깊고, 깊숙히 뛰어 들어 나가는 도중에 우리가 할 수있는 것을 잡아라.


나는 파이썬에 익숙하지 않지만 어디에 diving정의되어 있습니까?
무지의 실시

1
@EmbodimentofIgnorance enter_ruins ()에서 게임이 실행되고 동작이 수행되기 전에 호출됩니다.

Jacob the Orphan (Diver) was sliced in half by a swinging blade trap.당신이 무엇을 잘못했는지 확실하지 않지만, 그것은 '잘못된 반환'AFAIK를 의미합니다.
Artemis는 Monica

@ArtemisFowl 그는 보물을 위해 너무 낮게 입찰했다. 보물을 사려면 돈이 필요합니다.
Beefster

@ 비프 스터
Artemis는
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.