廃inの冒険者


27

テストドライバーチャレンジディスカッション冒険家を提出する

トレジャールーム画像ソース

数人のライバルの冒険者が宝物のために廃insを襲撃していますが、彼らは一度に多くを運ぶことができるだけで、持久力の限界があります。彼らは最も価値のある宝物を手に入れ、疲れすぎて続行できない前に出たいと思っています。彼らは略奪するシェナンガンから可能な限り金持ちになろうとしています。

ゲームプレイ

それぞれの冒険者は、1000個のスタミナポイントとバックパックに50kgのスペースがあるダンジョンの最初の部屋から始まります。

ゲームはターンベースの方法で動作し、すべてのプレイヤーが同時にターンを解決します。ターンごとに、次のアクションのいずれかを実行できます。

  • 次の部屋に移動します。
  • 前の部屋に移動します。
  • 宝を取るためにスタミナを入札します。
  • 宝物をドロップします。

部屋間を移動するには、10スタミナに加えて、現在バックパックに入っている5 kgごとに1スタミナが必要です。たとえば、3kgの宝物を運ぶ冒険者の移動には11体のスタミナが必要で、47kgの宝物の移動には20体のスタミナが必要です。

宝をドロップするには、ドロップする宝に関係なくスタミナが1つ必要です。

廃insを出ると、プレイヤーはこれ以上ターンを行いません。

プレーヤーがこれらのアクションのいずれかを実行できない場合(スタミナの不足または宝物の欠如)、冒険者は疲弊して死に、保有している宝物を現在占有されている部屋にこぼします。同様に、プレイヤーが無効なアクションを行おうとすると、冒険者は代わりにトラップで殺され、同じ宝物が流出します。

入札

宝の最低入札価格は、宝の重さ1kgにつき1スタミナです。宝物を獲得する可能性が高くなるように、追加のスタミナポイントを入札することもできます。入札されたスタミナは、結果がどうであれ消費されます。

複数のプレイヤーが同じ宝を獲得するために入札した場合、最高額を入札したプレイヤーが宝を獲得します。複数のプレイヤーが最高入札を行った場合、誰も宝を受け取りません。

勝利条件

宝物の合計値が最大のプレイヤーが勝者です。まれにネクタイが発生した場合、タイは最小の総重量、次に最小の宝物数、次に最も価値のある宝物、2番目に貴重なもの、3番目...に至るまで続きます。ほぼ不可能なこの時点で引き分けがある場合、テストドライバーは「それをねじ込む」と言い、勝者はそれによって任意に決定されます。

トーナメントの文脈では、プレイヤーは1位で10ポイント、2位で9ポイント、3位で8ポイント、などとランク付けされます。

遺跡について

  • 各部屋は、最初の間に含まと宝物。(は部屋番号です)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次のフィールドを持つ1つの引数を受け取ります(構造化を希望する場合は、この順序で):

  • room:現在いる部屋の番号
  • treasures:部屋の宝物のリスト
  • players:ルーム内の他のプレイヤーのリスト。この方法でプレイヤー名を取得するだけなので、どのボットがプレイヤーやそのインベントリ/スタミナを制御しているかはわかりません。
  • inventory:バックパック内の宝物のリスト
  • stamina:現在のスタミナレベル

このオブジェクトは、さらに2つのユーティリティプロパティを提供します。

  • carry_weight:持っているすべての宝物の総重量
  • total_value:あなたが持っているすべての宝物の合計値

treasuresそしてinventoryリストは含まれてnamedtupleこれらの属性を持つ秒:

  • name:宝の名前(化粧用)
  • value:宝物の金銭的価値($)。
  • weight:宝物の重量(kg)

get_action 次の値/パターンのいずれかを返す必要があります。

  • 'next'または'previous'次/前の部屋に移動する
  • 'take', <treasure index>, <bid>(はい、タプルとして、どのシーケンスも技術的には機能しますが)ルームのトレジャーリスト内の指定されたインデックスでトレジャーに入札します。両方の引数は整数でなければなりません。フロートは切り捨てられます。
  • 'drop', <inventory index>指定されたインデックスで見つかったキャリートレジャーをドロップします。インデックスは(当然)整数でなければなりません。

その他の制限

  • 擬似乱数の初期化中に提供されたランダムインスタンスのみを使用できます。
    • 行動の非決定性を引き起こす可能性のあるものは許可されません。ここでの目的は、新しいボット(および潜在的にテストドライバーのバグ)のテストを支援するために、同じシードが与えられたときにボットを同じように動作させることです。宇宙放射線のみが逸脱/非決定論を引き起こすはずです。
    • Python 3ではハッシュコードがランダム化されるhashため、意思決定に使用することは許可されていません。dictPython 3.6以降、一貫性が保証されているため、決定に反復順序を使用する場合でもsは問題ありません。
  • ctypesハックまたはinspectスタックブードゥー(またはその他の方法)を使用してテストドライバーを回避することはできません。これらのモジュールを使用すると、驚くほど恐ろしいことができます。しないでください。
    • 各ボットは、防御的なコピーとnamedtuplesの自然な不変性によって合理的にサンドボックス化されますが、パッチ適用できない抜け穴/エクスプロイトがいくつかあります。
    • 他の機能inspectとはctypes限り回避制御機能に使用されないどちらとして使用することができます。
    • 現在のゲームで他のボットのインスタンスを取得する方法は許可されていません。
  • ボットは単独で動作する必要があり、他のボットと何らかの目的で調整することはできません。これには、一方が他方の成功のために犠牲になるような、異なる目標を持つ2つのボットの作成が含まれます。10を超える競合他社が存在する場合、実際には同じゲームに2つのボットがいることは保証されず、冒険者の名前はボットクラスを示すものではありません。
  • 現在、実行時間に厳しい制限はありませんが、トーナメントに時間がかかりすぎる場合は、将来的にそれを強く制限する権利を留保します。合理的であり、ターン処理を100ms未満に保つようにしてください。これは、そのしきい値以下に制限する必要があるとは思わないからです。(すべてのボットが1ターンあたり約100ミリ秒かかる場合、トーナメントは約2時間で実行されます。)
  • ボットクラスには、すべての提出物の中で一意の名前を付ける必要があります。
  • ゲームの間に何も覚えていないかもしれません。(ただし、ターン間で物事覚えることができます)
    • sys.modulesを編集しないでください。インスタンス変数以外は、定数として扱う必要があります。
  • 独自のコードを含め、プログラムでボットのコードを変更することはできません。
    • これには、コードの削除と復元が含まれます。これは、デバッグとトーナメントをより合理化するためです。
  • コントローラーをクラッシュさせるコードはすぐに失格となります。ほとんどの例外がキャッチされますが、いくつかは抜け落ち、セグメンテーション違反はキャッチできません。(はい、おかげでPythonでセグメンテーション違反を起こすことができますctypes

提出

回答のスクレイピングを支援するために、回答の上部にボットの名前を指定し、#Header1回答に少なくとも1つのコードブロックが含まれていることを確認します(回答の最初のブロックのみが使用されます)。スクレーパーによって自動的に追加されるため、インポートやdocstringを含める必要はありません。

私は、詳細で理解可能な説明で回答を支持する傾向があります。他の人も同じように振る舞います。

大まかに言えば、あなたの答えは次のような形式にする必要があります。

# 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+が必要になります。pipを使用してインストールすることもお勧めしますtabulate。提出のためにこのページをスクレイピングするにはlxml、さらにとが必要requestsです。最良の結果を得るには、ANSIカラーエスケープをサポートする端末も使用する必要があります。Windows 10でこれを設定する方法については、こちらをご覧ください

ruins.pyruins_botsデフォルトで)と同じディレクトリ内のサブディレクトリ内のファイルにボットを追加from __main__ import Adventurerし、モジュールの一番上に追加してください。これは、スクレーパーが提出物をダウンロードするときにモジュールに追加されます。これは間違いなくハックですが、これはボットが適切にアクセスできるようにする最も簡単な方法ですAdventurer

そのディレクトリ内のすべてのボットは実行時に動的にロードされるため、これ以上変更する必要はありません。

トーナメント

最終的な勝者は、各ゲームに最大10個のボットがある一連のゲームで決定されます。合計10件を超える提出がある場合、上位10個のボットは、すべてのボットが(正確に)20ゲームをプレイするまで、10個のグループに体系的に分割することによって決定されます。上位10のボットがリセットスコアでこのグループから選択され、1位のボットが2位のボットに対して50ポイントのリードを獲得するまで、または500ゲームがプレイされるまでゲームをプレイします。

少なくとも10件の提出があるまで、空のスロットは「Drunkards」で満たされ、廃whichをランダムにさまよい、スタミナを使い果たして出口に向かうまでランダムな宝物を奪います(場合によってはドロップします)。

新しいサブミッションがある場合、トーナメントは毎週再実行されます。これは、終了日が設定されていないオープン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日:Backwardsを完全に破壊したことで、Sleafarに賞金が授与されました。おめでとうございます!


1
ついに来ました!今すぐボットの作成を開始する必要があると思います。
Belhenix

12
なぜ1つのボットに制限があるのですか?相互に排他的なアイデアがいくつかあり、新しいものを思い付くたびに完全に良いボットを捨てる必要はありません。

@Mnemonicは、主に、複数のほぼ同一のボットを使用して、新しいサブミッションの置き換えを防ぐことです。もう1つの理由は、ボットが一緒に動作するのを防ぐことでしたが、いずれにしても明示的に禁止されています。許可することを検討します。複数の投稿を許可することに賛成の方は、上記のニーモニックのコメントに賛成です。
ビーフスター

1
あなたがいる場合はDraco18s @ pipインストールし、上のPATH窓から、あなたが実行することができます(新しいインストール私の知る限りでのデフォルトである)pip install modulenamepromptコマンドでは。他の状況(私は知らない)については、pipに移動して、必要なモジュールを検索し、オプションを選択します。
アルテミスはモニカを

1
これは「いいえ」になると思いますが、トーナメントを通じて情報を保存することは許可されていますか?(例:入札が機能したとき)
ArtemisはMonica

回答:


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

「彼にそうするように教えようとする試みは、これまで会計士がアイテムを落とし、その後すぐにそれを拾い上げることになりました。」ドロップされたトレジャー名のセットを保持することで、これを回避できます。宝物の名前が
役に立つと思う(

ありがとう、しかし、私はすでにそれが起こった理由を見つけました。私はそれを入れてテストしたとき、彼はめったにそれを使用しないので、私は、かかわらず、すぐにそれを修正しますかどうかを知るしないでください。
ARBO

2

宿泊者

私の他のLightWeightボットにゆるやかに基づいています。軽量ボットは簡単だった場合は、このボットはするために、はるかに複雑である収容良性とdeliberatly distruptive両方:他のロボットとの対話を。

このボットは、まずランダムに割り当てられた部屋にスプリントし、次に部屋に他のプレイヤーがいる場合は最高の価値/重量比の宝物に入札しようとします。その入札が失敗した場合、次のターンで次の最高の宝物のために入札します。

入札が成功したら、部屋に宝物がなくなるまでベスト/セカンドベストの入札を繰り返し、それから廃intoの奥深くに移動します

各部屋について、部屋に宝物がなくなるまでベスト/セカンドベストの入札を繰り返してから、さらに深く遺跡に移動するか、さらに深く入ることができないことを検出して「終了」状態に切り替えて、最悪のドロップを開始します生きている廃ruから抜け出すことができると保証できるまで宝物。

終了状態では、1kgの宝物を追加しても生き残ることができるかどうかを確認し、そうであれば、1kgの宝物を入札しようとします。そうでない場合は、前の部屋に向かいます。

そのパフォーマンスはかなり変わりますが、通常は上位3つのボットの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ラウンドでトーナメントを支配しています。
ビーフスター

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は彼をtrapに押し込むことにしました。最初はSprinterを呼び出そうとしたボットを殺したいのですが、残念ながらテストドライバーはinitで発生する例外を処理しません。したがって、Scoundrelの次の修正は非常に簡単です...


悪党

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__関数は既に実装されています。オーバーライドする必要はありません。
アルテミスは


2

アーティベンチュラー

Drunkardsを約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
    現在の数値では、これまでに取得した宝物が利用可能な場合はいつでもこれが実行されます。「最高の」宝物をとることが安全であれば、そうします。入札単価は最小値であり、誰かが参加している場合はそれより1つ多くなります。

  • if bestt and worsti: ... return 'take', bestt[0], bid
    現在の数値では、前のコードブロックの条件がより広いため、このコードブロックは実行されません。これは、ここまで持っていて、インベントリと部屋に宝物がある場合に実行されます。部屋の「最高の」宝物が私のインベントリの「最悪の」宝物よりも価値があり、次の2ターンでそれらを交換しても安全だとしたら、そうします。

  • return 'previous'
    これらのいずれも発生しない場合は、戻ってください。

更新日16/04/19:

悪党対策。これは入札戦争になります:(

さらに更新16/04/19:

以前の状態に戻し、代わりに、最良のものを見つけるときに他のすべての要素をランダムに切り替えます。[1, 2, 3, 4, 5, 6] → [2, 1, 3, 4, 6, 5]。コピーするのが難しいはずです:)。

更新17/04/19:

以前の状態に戻し、代わりに独自のソースコードを消去します。これは、__init__常にbefore Scoundrel.enter_ruinsであるため、Scoundrelのロードを停止します。get_action最初に呼び出されたときにコードを置き換え、次回の準備ができるようにします。修正済み、Scoundrelが到着時に死亡するようになりました。

さらなる更新17/04/19:

前に戻し、代わりにそのsys.modulesエントリを数学モジュールに置き換えます。そのため、Scoundrelがロードしようとすると、代わりに数学モジュールがロードされます。:)
また、私は移動スタミナが10 +重量/ 5であることに気付いたので、それを修正しようとしました。

さらなる更新17/04/19:

以前のアップデートの両方からニンニクが含まれるようになりました。

更新日18/04/19:

数字と計算をいじると、2000ドルから3000ドルになります。

さらに更新18/04/19:

削除されたファイルワイプニンニクそれが禁止されているように、確認します追加された新しいニンニクが'draco'、それはそれだけのリターンであるならば、それは実行するための責任を負いませんprevious、その最初のターンに。結果は$ 1200- $ 1800への不思議な飛び込みを見せています。


Drunkardsに対して非常に効果的であるように見えます。他のボットが襲撃に参加したときの
運命を見てみたいです

@Moogieは、8人の酔っぱらいがいるとき、ダイバーを約100ドル打ちます。
アルテミスはモニカを

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'

悪党は主に他の競技者を妨害する働きをします。現在、Sprinter、Artyventurer、およびAccountantに干渉します(このリストは、Scoundrelの最善の利益の範囲内であれば、時間とともに増加します)。これは、他のボットを模倣して、入札、アンダーカット、またはその他の方法で遺物をめぐって戦うことによって行われます。そのため、このエントリがリーダーボードを支配する可能性は低く、代わりにネタバレ力として機能します。この投稿時点での現在の改訂では、平均スコアが約7で2位になりました。

Scoundrelは、他の参加者のコードを区別できないクローンコピーとして直接実行することにより、Scoundrelから身を守るために自分自身を変更しようとする他のボットの試みを阻止します。重複したエントリをもたらすインポートの問題は、Reflectionを介してクローンを作成することで解決されました(数学的な決定の詳細を含む編集戦争はStack Exchangeの観点からは望ましくありませんが、同じ結果になります)。KOTHの課題には、これを許可した歴史もあります。

Scoundrelは、面白かったためにTeamsterを維持するために、Teamsterを置き換えます。この編集後、Teamstersはコントローラーによってスクレイピングされなくなります。

更新4/17/2019:カウンターカウンター対策。

チームスター(違法にレンダリングされた)

ただし、他の参加者が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

このエントリ(現在は明示的に無効ですが)は実際には2つのボットであり、コントローラーは喜んでそれらの両方をスクレイピングし、競技者リストに追加します(hooray Python?)

フェーズ1:

  • TeamsterAはレベル25(ish)1に向かい、見つけられる最も軽い宝物を繰り返し拾い上げ、落とします。これは、第2フェーズまで1スタミナ1ターンかかります。
  • TeamsterBはレベル55に向かい、そこに横たわっている貴重品をすべて拾い上げ、レベル25(ish)に戻ります。2次にフェーズ2を開始します。

1.床に3未満の重さの宝物がない場合、彼は下に移動します。2. 彼が最後に水面に戻る冒険家である
ことがほとんど保証されているので、彼がしなければならないことは誰かを見つけることです。

フェーズ2:

  • TeamsterBはクロールして疲れ果てて死ぬ前にポケットを空にします。私たちはあなたがそれをできると知っていました。
  • TeamsterAは「これは光沢のある装身具であり、良き仲間だ!」そして、出口に進む前に、部屋にある他のジャンクよりもはるかに貴重な宝物を積み込みます。ポケットは金でいっぱいです。

宝物の名前は、2つのボット間で通信する方法がないため、ロジックが25階のジャンクに積み込まれずに早く去るのを助けるために実際に役に立ちました(そして、TeamsterAは常に前に誰かと部屋にいるでしょうTeamsterBが戻りました)。

次の論理的結論:軍隊を作成する

理論的には、これを使用して深さを調べ、部屋98ほどの深さから宝物を取得できますが、2つ以上のボットが必要になるため、これらのボットを構成するロジックはますます複雑になります。書かれていないルールに違反したという違法な投稿なので、私は気にしません。

効果的にA、30で待機B50 ...で待機n98ダイブ、宝物ピックアップ、97に移動し、それをドロップ(そして死ぬ)、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
一人あたりボットが1つだけになる場合、そのような明示的なルールは必要ないと思います。しかし、さまざまな理由で特定のボットを標的にすることに関するルールは、複数のボットが一緒にチーム化することを禁止することと実際には同じではありません。したがって、OPからの明示的な裁定が必要です。
Moogie

ええ、これは私からノーになるだろう、dawg。これは、ボットが連携して動作することを念頭に置いたものです。
ビーフスター

1
@Beefsterそれは私が考えたことです。でもそれを作るのは楽しかったです。今晩、編集から予防までの編集を処理します。
Draco18s

いずれにせよ有効性が低下するため、11を超える競合他社が存在する場合、これを許可することを検討します。主に、投稿を自動禁止するコードを作成したくないためです。
ビーフスター

既に最初のコードブロックのみをスクレイピングしている場合は、上部の別のボットで編集するだけです。
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と呼ばれるのはなぜですか?

私は会計士を雇い、深く潜るようにロジックを実行させようとしましたが、その途中でその優先戦利品を拾い上げました(会計士の後方)。

最終的には、まだ途中で賞品の多くを収集します(従来のインコレクトシーカーが行う前にそれらをすくい上げ、他のすべての人に逆行します)が、それはまだどれをとるかについてより選択的です帰りに物を拾います。

最終的な結果として、価値の高い宝物を優先しつつ、途中でスタミナが保存され、その後の深いターンアラウンドと簡単なピッキングを活用しています。後方は、部屋41から宝物を収集することが知られています(開発中は部屋42に入り、すぐに部屋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にタンクを引き起こします。よくやった。
ビーフスター

1

軽量

まだ非常によく機能するシンプルなボット。

廃into(現在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'

メソッドを入れることdumpingをお勧めしenter_ruinsます。これはゲーム間で実際に記憶され、ゲーム2では機能しません。技術的には許可されていませんが、今はルールを追加しました(以前は忘れていましたが、意図していました)。:P
ビーフスター

@Beefsterダンプ状態フラグを削除しました。ボットは現在1つの宝物をダンプするだけなので、必要ありません。以前は宝の半分を捨てていました。したがって、新しいルールとの互換性が必要です。
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

[Turn 072] Ryu Ridley (Memorizer) collapsed in the doorway to room #1 and died of exhaustion
参考

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は、部屋にある宝物をすべて奪取するまで部屋に留まります(または、服用/移動を続けるのに十分なスタミナがないと計算します)。すべての宝物がなくなったとき、ボットが安全に移動できれば、宝物をすべて手に入れるプロセスを続行します。


これは、バックパックをいっぱいにすることですべてのゲームを終了させます。
ビーフスター

Minecraftでの私のように(͡°͜ʖ͡°)このボットは略奪し、より深く、そして貴重な略奪品を見つけます。だから、彼が以前に良い戦利品だと思ったものを落とすでしょう。だからこそ、だBackwardsの、SprinterさんとMemorizerの戦略の仕事; 彼らが見るすべての宝物の相対的な価値は何かを知っているからです。
V.クルトワ

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'

最高の宝物は廃inの奥深くにありますので、深く潜り、途中でできることをつかみます。


私はPythonの経験はあまりありませんが、どこでdiving定義されていますか?
無知の具現化

1
@EmbodimentofIgnorance enter_ruins()では、ゲームが実行されアクションが実行される前に呼び出されます。

Jacob the Orphan (Diver) was sliced in half by a swinging blade trap.あなたが何を間違えたかはわかりませんが、それは「無効な返品」という意味です。
アルテミスはモニカを

@ArtemisFowl彼は宝物には低すぎる入札しました。それを拾うには宝の重さがかかります。
ビーフスター

@Beefsterああ、うん。
アルテミスはモニカを
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.