废墟中的冒险家


27

测试驾驶员挑战讨论提交冒险家

宝藏室图片来源

几个竞争对手冒险者正在废墟中寻宝,但他们一次只能携带这么多东西,而且承受力有限。他们希望获得最有价值的宝藏,并在他们变得疲倦而无法继续之前走出去。他们正设法从掠夺的恶作剧中致富。

游戏玩法

每个冒险家都从地牢的第一个房间开始,并获得1000点耐力点和背包中50公斤的空间。

游戏以回合制为基础,所有玩家同时解决回合。每转一圈,您可以执行以下操作之一:

  • 移到下一个房间。
  • 移到上一个房间。
  • 竞买耐力拿宝。
  • 丢下宝藏。

在各个房间之间移动需要10个耐力,而背包中当前每5公斤要加1个耐力。例如,一名冒险者携带3公斤宝藏需要移动11体力,而一名携带47公斤宝藏的冒险者需要移动20体力。

掉落宝藏需要1个耐力,无论掉落的宝藏如何。

离开废墟后,玩家将不再转弯。

如果玩家不能采取任何这些行动(由于缺乏耐力或缺少宝物),则冒险者会死于精疲力尽,将其持有的宝物洒入当前占用的房间。同样,如果玩家尝试执行无效操作,则冒险者将被陷阱杀死,从而导致相同的宝藏溢出。

投标

宝物的最低出价是每1公斤宝物称重1个耐力。您也可以出价额外的耐力点,以更有可能获得宝藏。不管结果如何,被消耗的耐力都会被消耗掉。

如果有多个玩家竞标购买同一宝藏,则出价最高的玩家将获得该宝藏。如果有一个以上的玩家出价最高,那么他们都将不会获得宝藏。

获胜条件

总资产最大的玩家是赢家。如果发生平局,领带的总重量最小,然后是最小数量的宝物,然后是最有价值的珍宝的价值,其次是最有价值的,第三...直到领带被打破。如果在这种情况下仍然有平局,测试驾驶员会说“拧”,从而任意确定获胜者。

在锦标赛中,玩家将排名第一,获得10分,第二名获得9分,第三名获得8分,依此类推,死者和没有宝藏的冒险家获得0分。

关于废墟

  • 每个房间最初包含之间[R3+3[R2+5珍品。([R是房间号)
  • 有任意多个房间,仅受冒险者的耐力和探索意愿的限制。
  • 每件宝物将具有货币价值(总计$)和重量(总计kg)。
    • 当您深入废墟时,珍宝往往更有价值和更多。
  • 生成宝物的具体公式如下:(对骰子使用Xdÿ表示法)
    • 重量首先产生利用下式2d6-2(最小的1)
    • 然后通过1个d[10w]+2d[5[R+10]生成宝物值(其中[R是房间号,w是重量)

玩家可见的信息

在每个回合中,玩家都会获得以下信息:

  • 他们当前所在房间的数量。它是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_action接收到一个namedtuple具有以下字段的单个参数(如果您更喜欢解构,请按此顺序):

  • room:您当前所在房间的数量
  • treasures:房间中的宝藏清单
  • players:房间中其他玩家的名单。您只能以这种方式获得玩家的名字,因此您不知道哪个机器人控制着他们或他们的库存/耐力。
  • inventory:背包中的宝藏清单
  • stamina:您当前的耐力水平

此对象还提供了两个实用程序属性:

  • carry_weight:您携带的所有宝贝的总重量
  • total_value:您携带的所有宝藏的总价值

treasuresinventory列表包含namedtuples的这些属性:

  • name:宝藏名称(用于装饰目的)
  • value:以美元为单位的宝物的货币价值。
  • weight:宝物的重量(公斤)

get_action 应该返回以下值/模式之一:

  • 'next''previous'移至下一个/上一个房间
  • 'take', <treasure index>, <bid>(是的,作为一个元组,尽管从技术上讲,任何顺序都可以使用)以房间宝藏列表中给定索引的宝藏出价。两个参数都应为整数。浮点数将四舍五入。
  • 'drop', <inventory index>丢弃在给定索引处发现的携带财宝。索引(自然)应该是整数。

其他限制

  • 您只能使用初始化期间为伪随机性提供的随机实例。
    • 禁止任何其他可能引起行为不确定性的事情。目的是使机器人在获得相同的种子时具有相同的行为,以帮助测试新的机器人(以及测试驱动程序中可能存在的错误)。仅宇宙辐射应引起任何偏差/不确定性。
    • 请记住,哈希码在Python 3中是随机的,因此hash不允许将其用于任何决策。dict即使在使用迭代顺序进行决策时也可以使用s,因为自Python 3.6起就保证了顺序的一致性。
  • 您不得使用ctypes黑客或inspect伏都教徒(或任何其他方法)规避测试驱动程序。这些模块可以做一些令人震惊的事情。请不要。
    • 每个bot都可以通过防御性副本和namedtuples 的自然不变性被很好地沙盒化,但是存在一些无法修补的漏洞/漏洞。
    • 或可以使用来自inspect和的其他功能,ctypes只要它们都不用来规避控制器功能。
    • 不允许使用任何方法来获取当前游戏中其他机器人的实例。
  • 机器人应单独操作,不得出于任何目的与任何其他机器人进行任何协调。这包括创建两个具有不同目标的机器人,以便一个机器人为另一个机器人的成功而牺牲自己。一旦有10个以上的竞争对手,您实际上将无法保证两个机器人都在同一游戏中,冒险家的名字也无法说明机器人的类别,因此无论如何,这些策略都是有限的。
  • 目前对执行时间没有硬性限制,但是如果比赛开始的时间太长,我保留在将来硬性限制执行时间的权利。保持合理,尝试将转弯处理时间保持在100ms以下,因为我预计不需要将其限制在该阈值以下。(如果所有漫游器每转大约花费100ms,则比赛将在2个小时内运行。)
  • 您的机器人程序类别必须在所有提交中唯一地命名。
  • 您可能不记得游戏之间的任何事情。(但是,您可以记住转弯之间的情况)
    • 不要编辑sys.modules。实例变量之外的任何内容都应视为常量。
  • 您不得以编程方式修改任何漫游器的代码,包括您自己的代码。
    • 这包括删除和还原您的代码。这是为了使调试和比赛更加简化。
  • 任何导致控制器崩溃的代码将立即被取消资格。尽管将捕获大多数异常,但有些异常可能会漏掉,并且段错误无法捕获。(是的,由于,您可以在Python中进行段错误处理ctypes

意见书

为了帮助抓取答案,请在答案顶部用指示机器人的名称,#Header1并确保答案至少包含一个代码块(仅使用答案中的第一个)。您不需要包括任何导入或文档字符串,因为它们将由刮板自动添加。

我将更倾向于通过详细且易于理解的解释来支持答案。其他人的行为可能相同。

粗略地说,您的答案应采用以下格式:

# Name of Bot
Optional blurb

    #imports go here

    class BotName(Adventurer):
        #implementation

Explanation of bot algorithm, credits, etc...

(呈现为)

机器人名称

可选的blurb

#imports go here

class BotName(Adventurer):
    #implementation

机器人算法,积分等的说明...

在本地运行测试驱动程序

您将需要Python 3.7+,我建议您也tabulate通过pip 安装。刮除此页面的提交内容还需要lxmlrequests。您还应该使用支持ANSI彩色转义的终端,以获得最佳效果。有关如何在Windows 10中进行设置的信息,请参见此处

将您的漫游器添加到与ruins.pyruins_bots默认情况下)相同目录下的子目录中的文件中,并确保将其添加from __main__ import Adventurer到模块顶部。当抓取工具下载您的提交内容时,它会添加到模块中,尽管它肯定是很hacky的,但这是确保您的机器人正确访问的最直接的方法Adventurer

该目录中的所有bot都会在运行时动态加载,因此无需进一步更改。

比赛

最终胜利者将在一系列游戏中确定,每个游戏中最多有10个机器人。如果提交的总数超过10个,则将系统将它们划分为10个组,直到每个机器人玩了(完全)20个游戏,从而确定排名前10位的机器人。将从该组中选择重置得分最高的10个机器人,直到第一名机器人比第二名机器人领先50分或玩了500场游戏为止。

直到至少有10个提交项目为止,空的插槽中将充满“醉汉”,它们会随机漫步在废墟中,并(偶尔掉落)随机的宝藏,直到耗尽了耐力并不得不直奔出口。

如果有新的参赛作品,比赛将每周重新举行。这是公开的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,因为他们彻底摧毁了Backwards。恭喜你!


1
终于到了!猜猜我现在必须开始制作我的机器人。
Belhenix

12
为什么要限制一个机器人?我有几个相互排斥的想法,每次我想出一个新机器人时,我都不想扔出一个完美的机器人。

@Mnemonic,主要是通过使用多个接近完全相同的机器人来防止替换新提交的内容。另一个原因是为了防止漫游器协同工作,但是无论如何都明确禁止了它。我会考虑允许的。赞成允许多次提交的意见书支持上面助记词的评论。
Beefster

1
@ Draco18s如果已pip安装并打开PATH(对于新安装的AFAIK,这是默认设置),则可以从Windows运行pip install modulename在命令提示符下。对于其他情况(我不知道),请转到pip,搜索所需的模块并选择一个选项。
Artemis

1
我猜这将是“否”,但是我们可以通过比赛保存信息吗?(例如,在竞标成功时)
Artemis支持Monica

Answers:


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
确定财富价值的好工作。我绝对是想编写一些更好的“值得”代码,但是还没有到那儿。不过,这个流氓正为会计师的底线
而来

“到目前为止,任何试图教他这样做的尝试都导致会计师丢下一个项目,然后立即将其重新拾起。” 您可以通过保留一组掉落的宝藏名称来解决此问题。我感到宝藏名称会派上用场(即使现在
才对

谢谢,但是我已经弄清楚了为什么会发生。不知道我是否会虽然马上修补,因为他很少使用它时,我测试投入进去。
ARBO

2

调节器

松懈地基于我的另一个LightWeight机器人。LightWeight bot很简单,但为了适应与其他bot的交互,该bot更复杂:良性和故意破坏性。

该机器人将首先冲刺到一个随机分配的房间,然后尝试竞标最佳价值/重量比例宝藏,如果房间中还有其他玩家,则假定他们也将竞标,从而竞标第二好的宝藏。如果该竞标失败,则在下一轮竞标下一个最佳珍宝。

竞标成功后,重复竞标以获得最佳/次优,直到房间中不再存在任何宝藏,然后再深入废墟中

对于每个进入废墟的房间,重复竞标以获取最佳/次佳出价,直到房间中不再存在任何宝藏,然后再深入废墟中;或者如果我们检测到无法深入,则交换为“退出”状态并开始丢弃最差的状态珍惜,直到我们可以保证我们可以活着退出废墟。

处于退出状态时,我们将检查是否可以添加1公斤重的宝藏并仍然活着,如果可以,那么我们将尝试竞标1公斤重的宝藏,如果没有,那么我们将前往先前的房间。

它的性能变化很大……但是通常它将成为前三名机器人之一。

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

短跑选手

与潜水员相似,Sprinter深入并在返回途中捡起了最好的物品。

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的回答中最好/最坏的计算技巧,但选择逻辑完全是我自己设计的,此后进行了修改,以包括一些其他因素,例如在较早的房间看到的财宝(以最大程度地减少捡拾东西)。珍惜,只是为了回溯,丢弃它并捡起其他东西)。

Bot冒险进行尽可能合理的深度冒险(此计算可有效地跳至特定深度,但具有处理其他初始耐力值的灵活性),收集人工制品(优先考虑高成本和低重量),然后一旦确定不再携带它,便开始撤退。

在出门时,它将发现自己确定仍可以安全携带到出口的所有其他珍宝。如果新的协议是更好的协议,它甚至会丢弃已经持有的工件,并且不会导致筋疲力尽。如果背包中有足够的空间,它将丢弃质量较低的珍宝之前先捡起新的珍宝,以最大程度地减少与其他机器人的战斗。


呵呵,当你形容它和我的一样。我会弄弄我的数字...注意:该__init__功能已经实现,您无需覆盖它。
Artemis


2

冒险家

击败醉汉约$ 1000!想不出一个创意名称,但在这里,你们都是:

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
    使用当前数字,只要我们到此为止并且有可用的财富,就会执行此操作。如果可以安全地拿出“最好的”宝藏,那就可以了。它的出价是最低的,或者比任何人在场的出价高一个。

  • if bestt and worsti: ... return 'take', bestt[0], bid
    使用当前编号,此代码块将永远不会执行,因为前一个代码块的条件更广。如果我们到此为止并且我的库存和房间中都有宝物,则执行此操作。如果房间中的“最好”宝藏比我清单中的“最坏”宝藏更有价值,并且可以在接下来的两个回合中安全地交换它们,那就可以了。

  • return 'previous'
    如果这些都不发生,请返回。

更新19/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首次调用,以便下次可以使用。已修正,Scoundrel现在在抵达时死亡。

进一步更新17/04/19:

还原为先前版本,而是将其sys.modules条目替换为math模块,以便当Scoundrel尝试加载它时,它将改为加载math模块。:)
另外,我只是意识到移动耐力是10 +体重/ 5,所以试图解决这个问题。

进一步更新17/04/19:

现在包括先前更新中的大蒜。

更新18/04/19:

摆弄数字和计算,现在可以得到$ 2000-$ 3000。

进一步更新18/04/19:

删除了禁止擦拭的大蒜,并添加了新的大蒜,以确保'draco'对其运行不承担任何责任,如果它只是previous在第一圈就返回。结果令人难以置信的跳水到$ 1200- $ 1800,我正在研究。


似乎对Drunkards非常有效,我想看看其他机器人加入突袭时的情况如何:)
Moogie

@Moogie当8名醉汉也出席时击败潜水员约100美元。
Artemis

2

Sc徒

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'

混蛋主要是为了干扰其他参赛者。目前,它会干扰Sprinter,Artyventurer和Accountant(此列表会随着时间的推移而增长,只要它符合Scoundrel的最大利益)。它通过模仿其他机器人然后竞标,砍价或以其他方式争夺文物来做到这一点。因此,该条目不太可能在排行榜中占据主导地位,反而会起到破坏作用。在发布本文时,当前的修订版本将其排在第二位,平均得分约为7。

Scoundrel通过直接执行其他参赛者的代码作为无法区分的克隆副本来挫败其他机器人进行自我修改以抵御Scoundrel的尝试。通过使用反射创建克隆解决了进口导致重复进入的问题(从Stack Exchange的角度来看,涉及数学确定的精细细节的编辑大战是不希望的,但会导致相同的结果)。KOTH挑战也有允许这样做的历史。

为了保持队友的兴趣,Scoundrel代替了队友。完成此编辑后,控制器将不再刮除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

该条目(虽然现在明显无效)实际上是两个机器人,控制器将高兴地它们都抓取并添加到参赛者列表中(因为hooray Python?)

阶段1:

  • TeamsterA降级到25(ish)1并反复捡起并丢下他能找到的最轻的宝藏。直到第二阶段,这都要花费高达1的耐力。
  • TeamsterB朝第55级走去,捡起周围所有的贵重物品,然后回到第25级(ish)。2然后开始阶段2。

1.如果地板上没有一件重量小于3的宝物,他会下移
2.因为他几乎可以保证是最后一个回到地面的冒险家,所以他要做的就是找到一个人。

阶段2:

  • TeamsterB先清空自己的口袋,然后爬过去死于筋疲力尽。我们知道您可以做到。
  • TeamsterA认为“那是一些闪亮的小饰品,好朋友!” 并且在驶向出口之前,装载了比房间中其他垃圾更有价值的宝藏,里面装满了金子。

宝藏的名称实际上派上了用场,以帮助逻辑不堆积在25楼的垃圾上并提早离开,因为两个机器人之间无法进行通信(而TeamsterA总是会发现自己和其他人在一个房间里TeamsterB已返回)。

下一个合乎逻辑的结论:建立一支军队

从理论上讲,它可以用来探查深度并从98号房间的深处获取财富,但是,由于这将需要2个以上的漫游器,因此构成这些漫游器的逻辑将变得越来越复杂,并且我确定这是违反不成文规则的非法提交,因此我不会打扰。

有效地A等待30,B等待50 ... n潜入98,捡起宝物,移至97,掉落(然后死亡),n-1捡起并移至96 ... C掉落(死),B捡起向上移动到30,将其丢弃(死),将其A捡起并返回出口。

我估计这将需要11个机器人。

但是,除非您能从该深度回收约4件宝藏,以便与PlanAhead或Artyventure等产品竞争,否则这样做是不值得的,因为移动的耐力成本和宝藏的平均价值之间存在着比例关系。

样品结果

很少有低于$ 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
我认为,每人只有一个机器人时,就不需要这样一个明确的规则。但是出于种种原因而针对特定的bot的规则与禁止多个bot组合在一起实际上并不相同。因此,需要OP做出明确的裁定。
Moogie

是的,我会拒绝我的,宝贝。我与机器人一起工作时想到的就是这种事情。
Beefster

1
@Beefster这就是我的想法。不过我很开心。今天晚上,我将处理“编辑为防止包含”。
Draco18s

我会考虑在有超过11个竞争对手的情况下允许这样做,因为无论如何它的有效性都会下降。主要是因为我不想创建代码来自动禁止提交。
Beefster

如果您只刮了第一个代码块,我要做的就是在顶部的另一个Bot中进行编辑。
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'

为什么称其为Backwards?

因为我选了《会计》,并试图使其行之有效,以便深入研究,然后在退出时(会计的倒退)捡起首选的战利品。

最终,它仍沿途收集了很多奖品(在传统的内藏式寻宝者之前将其sco取,向其他所有人倒带操作),但其选择性更高大概需要哪些,虽然它仍然捡回途中的东西。

最终结果是,在保持耐力的同时仍优先考虑高价值的宝藏,然后在转弯的途中利用深度转弯和轻松采摘的优势。众所周知,向后收集的物品可以追溯到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个房间)后,它将抓住房间中最好的宝藏,重量只有1公斤(因此为机器人的名字),并且比库存中价值最低的宝藏更有价值。如果库存已满,请丢掉最不值钱的宝藏。如果没有选择其他动作,则进入废墟。如果我们处于耐力极限,可以活着退出,那就前往出口

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'

我建议放入dumpingenter_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美元或更多的1公斤珍宝。


我想所有这些好的宝藏都到了机器人回到那里的地步...也许您可以尝试一个组合,它将真正好的东西拿走,同时要记住它可以捡到的平庸宝藏在回来的路上?
ArBo

可能进得太远了
Beefster

仅供参考,如果它具有足够的耐力来恢复时,似乎有时会误算: [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

ard积者

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

ard积者一直待在房间里,直到它拿走了房间里的所有宝藏(或计算出它没有足够的耐力来继续吸收/移动)。当所有宝藏都消失时,如果机器人可以安全地前进,它将继续并继续获取所有宝藏的过程。


这会使每个游戏因背包过满而死亡。
Beefster

像我在《我的世界》中一样(This°͜ʖ°°),该机器人会抢劫,深入挖掘,然后找到有价值的赃物。因此,它将早些时候删除他认为是很好的战利品。这就是为什么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'

最好的宝藏在废墟中更深,所以请潜入深处,然后在出路时抓住我们能拿到的东西。


我对python不太了解,但是在哪里diving定义?
无知的体现

1
@EmbodimentofIgnorance在enter_ruins()中,在运行游戏并执行动作之前调用它。

Jacob the Orphan (Diver) was sliced in half by a swinging blade trap.不确定您做错了什么,但这意味着“无效回报” AFAIK。
Artemis支持Monica

@ArtemisFowl他为宝藏出价太低。拾起它需要花费宝藏的重量。
Beefster

@Beefster哦,是的。
Artemis支持Monica
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.