Abenteurer in den Ruinen


27

TestfahrerHerausforderung DiskussionSubmit Adventurer

Schatzkammer ( Bildquelle )

Mehrere rivalisierende Abenteurer durchsuchen die Ruinen nach Schätzen, können aber nur so viel auf einmal tragen und haben ihre Grenzen der Ausdauer. Sie möchten den wertvollsten Schatz finden und hinausgehen, bevor sie zu müde werden, um fortzufahren. Sie versuchen, durch ihre Plünderungen so reich wie möglich zu werden.

Gameplay

Jeder Abenteurer beginnt im ersten Raum des Dungeons mit 1000 Ausdauerpunkten und 50 kg Platz im Rucksack.

Das Spiel funktioniert rundenbasiert, wobei alle Spieler gleichzeitig ihre Züge abwickeln. In jeder Runde kannst du eine der folgenden Aktionen ausführen:

  • Gehe in den nächsten Raum.
  • Zum vorherigen Raum gehen.
  • Bieten Sie Ausdauer, um einen Schatz zu nehmen.
  • Lass einen Schatz fallen.

Der Wechsel zwischen den Räumen erfordert 10 Ausdauer plus 1 Ausdauer pro 5 kg, die sich gerade in Ihrem Rucksack befinden. Zum Beispiel benötigt ein Abenteurer, der 3 kg Schatz trägt, 11 Ausdauer, und ein Abenteurer, der 47 kg trägt, benötigt 20 Ausdauer, um sich zu bewegen.

Das Fallenlassen eines Schatzes erfordert 1 Ausdauer, unabhängig davon, welcher Schatz gefallen ist.

Beim Verlassen der Ruinen werden vom Spieler keine Züge mehr ausgeführt.

Wenn ein Spieler keine dieser Aktionen ausführen kann (aufgrund mangelnder Ausdauer oder fehlender Schätze), stirbt sein Abenteurer vor Erschöpfung und verschüttet seinen gehaltenen Schatz in den aktuell besetzten Raum. Wenn ein Spieler versucht, eine ungültige Aktion auszuführen, wird sein Abenteurer stattdessen durch eine Falle getötet, was zur gleichen Verschwendung von Schätzen führt.

Bieten

Das Mindestgebot für einen Schatz beträgt 1 Ausdauer pro 1 kg, das der Schatz wiegt. Sie können auch zusätzliche Ausdauerpunkte bieten, um den Schatz mit größerer Wahrscheinlichkeit zu erhalten. Die gebotene Ausdauer wird unabhängig vom Ergebnis verbraucht.

Falls mehrere Spieler geboten haben, denselben Schatz zu nehmen, erhält der Spieler, der das höchste Gebot abgegeben hat, den Schatz. Wenn mehr als ein Spieler das höchste Gebot abgegeben hat, erhält keiner von ihnen den Schatz.

Gewinnbedingung

Der Spieler mit dem höchsten Gesamtwert an Schätzen ist der Gewinner. Im unwahrscheinlichen Fall eines Unentschieden gehen die Unentschieden zum kleinsten Gesamtgewicht, dann zur kleinsten Anzahl von Schätzen, dann zum Wert des wertvollsten Schatzes, zum zweitwertvollsten, zum dritten ... bis das Unentschieden gebrochen ist. Für den nahezu unmöglichen Fall, dass es an dieser Stelle noch ein Unentschieden gibt, sagt der Testfahrer "Screw it" und der Sieger wird dabei willkürlich ermittelt.

Im Rahmen des Turniers erhalten die Spieler den ersten Platz mit 10 Punkten, den zweiten Platz mit 9 Punkten, den dritten Platz mit 8 Punkten usw., wobei tote Spieler und Abenteurer ohne Schätze 0 Punkte erzielen.

Über die Ruinen

  • Jeder Raum enthält anfangs zwischen r3+3undr2+5Schätze. (Wobeirdie Zimmernummer ist)
  • Es gibt beliebig viele Räume, die nur durch die Ausdauer und Erkundungsbereitschaft der Abenteurer begrenzt sind.
  • Jeder Schatz hat einen Geldwert (in ganzen $) und ein Gewicht (in ganzen kg).
    • Schätze sind in der Regel wertvoller und zahlreicher, wenn Sie tiefer in die Ruinen vordringen.
  • Die spezifischen Formeln zum Erzeugen von Schätzen lauten wie folgt: (Verwenden der xdy -Notation für Würfelwürfe)
    • Das Gewicht wird zuerst mit der Formel 2d6-2 berechnet. D 6 - 2 (mindestens 1)
    • Der Schatzwert wird dann über 1d[10w]+2d[5r+10] (wobeir die Raumnummer undw das Gewicht ist).

Für Spieler sichtbare Informationen

In jedem Zug erhalten die Spieler die folgenden Informationen:

  • Die Nummer des Raums, in dem sie sich gerade befinden. Dies ist 1-indiziert, daher befindet sich der Ausgang konzeptionell bei "Raum 0".
  • Eine Liste der aktuell im Raum befindlichen Schätze
  • Eine Liste der anderen Spieler, die sich ebenfalls gerade im Raum befinden.
  • Ihr aktueller Schatzbestand
  • Ihre aktuelle Ausdauer

Codierung

Den Testfahrer finden Sie hier .

Sie sollten eine Unterklasse davon implementieren Adventurer Klasse :

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

Sie müssen nur die get_actionMethode überschreiben . enter_ruinswird ausgeführt, bevor ein Spiel beginnt, und ist Ihre Chance, alles vorzubereiten, was Sie für das Spiel bereithalten möchten. Sie müssen nicht überschreiben __init__, und Sie sollten es wirklich nicht tun . Wenn Sie __init__abstürzen, werden Sie disqualifiziert.

get_action erhält ein einziges Argument, das a ist namedtuple mit den folgenden Feldern (in dieser Reihenfolge, wenn Sie die Destrukturierung bevorzugen):

  • room: Die Nummer des Raumes, in dem Sie sich gerade befinden
  • treasures: die Liste der Schätze im Raum
  • players: die Liste der anderen Spieler im Raum. Auf diese Weise erhältst du nur den Spielernamen, sodass du nicht weißt, welcher Bot sie kontrolliert oder welches Inventar / welche Ausdauer sie haben.
  • inventory: die Liste der Schätze in Ihrem Rucksack
  • stamina: Ihre aktuelle Ausdauer

Dieses Objekt bietet zusätzlich zwei Utility-Eigenschaften:

  • carry_weight: das Gesamtgewicht aller Schätze, die Sie tragen
  • total_value: der Gesamtwert aller Schätze, die Sie tragen

Die treasuresund inventoryListen enthalten namedtuples mit diesen Attributen:

  • name: der Name des Schatzes (für kosmetische Zwecke)
  • value: der Geldwert des Schatzes in $.
  • weight: das Gewicht des Schatzes in kg

get_action sollte einen der folgenden Werte / Muster zurückgeben:

  • 'next'oder 'previous', um zum nächsten / vorherigen Raum zu wechseln
  • 'take', <treasure index>, <bid>(Ja, als Tupel, obwohl jede Sequenz auch technisch funktioniert), um auf den Schatz an dem angegebenen Index in der Schatzliste des Raums zu bieten. Beide Argumente sollten Ganzzahlen sein. Schwimmer werden abgerundet.
  • 'drop', <inventory index>den getragenen Schatz fallen zu lassen, der bei dem gegebenen Index gefunden wurde. Der Index sollte (natürlich) eine ganze Zahl sein.

Andere Einschränkungen

  • Sie dürfen nur die zufällige Instanz verwenden, die Ihnen während der Initialisierung für Pseudozufälligkeit zur Verfügung gestellt wurde.
    • Alles andere, was einen verhaltensbedingten Nichtdeterminismus hervorrufen könnte, ist nicht erlaubt. Die Absicht hierbei ist, dass Bots sich identisch verhalten, wenn sie denselben Startwert erhalten, um beim Testen neuer Bots (und potenziellen Bugs im Testtreiber) zu helfen. Nur kosmische Strahlung sollte Abweichungen / Nichtdeterminismus verursachen.
    • Denken Sie daran, dass Hash-Codes in Python 3 zufällig angeordnet sind, sodass die Verwendung hashfür Entscheidungen nicht zulässig ist. dicts sind in Ordnung, auch wenn die Iterationsreihenfolge für Entscheidungen verwendet wird, da die Reihenfolge seit Python 3.6 garantiert konsistent ist.
  • Sie dürfen den ctypesTesttreiber nicht mit Hacks oder inspectStack-Voodoo (oder einer anderen Methode) umgehen . Es gibt einige beeindruckend beängstigende Dinge, die Sie mit diesen Modulen tun können. Bitte nicht
    • Jeder Bot ist durch defensive Kopien und die natürliche Unveränderlichkeit von einigermaßen gut in Sandkästen eingeschlossen namedtuple s , aber es gibt einige unverbesserliche Schlupflöcher / Exploits.
    • Andere Funktionen von inspectund ctypeskönnen verwendet werden, sofern keine davon zur Umgehung der Steuerungsfunktionen verwendet wird.
    • Es ist nicht zulässig, Instanzen der anderen Bots in Ihrem aktuellen Spiel zu erfassen.
  • Bots sollten alleine agieren und dürfen sich in keiner Weise für irgendeinen Zweck mit anderen Bots koordinieren. Dies beinhaltet das Erstellen von zwei Bots mit unterschiedlichen Zielen, sodass sich einer für den Erfolg des anderen opfert. Sobald es mehr als 10 Konkurrenten gibt, kann nicht garantiert werden, dass die beiden Bots im selben Spiel sind, und die Namen der Abenteurer geben keinen Hinweis auf die Bot-Klasse. Daher sind diese Arten von Strategien ohnehin begrenzt.
  • Derzeit gibt es keine feste Beschränkung der Ausführungszeit. Ich behalte mir jedoch das Recht vor, die Ausführungszeit in Zukunft zu beschränken, wenn Turniere zu lange dauern. Seien Sie vernünftig und versuchen Sie, die Verarbeitung unter 100 ms zu halten , da ich nicht damit , sie unter diesen Schwellenwert einschränken zu müssen. (Turniere dauern ungefähr 2 Stunden, wenn alle Bots ungefähr 100 ms pro Runde benötigen.)
  • Ihre Bot-Klasse muss unter allen Einsendungen eindeutig benannt sein.
  • Sie können sich an nichts zwischen Spielen erinnern. (Sie können sich jedoch an Dinge zwischen den Runden erinnern )
    • Bearbeiten Sie keine sys.modules. Alle externen Instanzvariablen sollten als Konstante behandelt werden.
  • Sie dürfen den Code eines Bots einschließlich Ihres eigenen Codes nicht programmgesteuert ändern.
    • Dies beinhaltet das Löschen und Wiederherstellen Ihres Codes. Dies soll das Debuggen und die Turniere rationalisieren.
  • Jeder Code, der zum Absturz des Controllers führt, wird sofort disqualifiziert. Während die meisten Ausnahmen abgefangen werden, können einige durchrutschen und Fehler können nicht abgefangen werden. (Ja, Sie können in Python dank segfault ctypes)

Einreichungen

Geben Sie oben in der Antwort den Namen Ihres Bots mit einem an, um die Antwortverwaltung zu vereinfachen #Header1 und stellen Sie sicher, dass Ihre Antwort mindestens einen Codeblock enthält (nur der erste in Ihrer Antwort wird verwendet). Sie müssen keine Importe oder Dokumentzeichenfolgen einfügen, da diese vom Scraper automatisch hinzugefügt werden.

Ich werde eher geneigt sein, Antworten mit detaillierten und verständlichen Erklärungen abzustimmen. Andere werden sich wahrscheinlich genauso verhalten.

Grob gesagt sollte Ihre Antwort so formatiert sein:

# Name of Bot
Optional blurb

    #imports go here

    class BotName(Adventurer):
        #implementation

Explanation of bot algorithm, credits, etc...

(gerendert als)

Name des Bots

Optionaler Klappentext

#imports go here

class BotName(Adventurer):
    #implementation

Erklärung von Bot-Algorithmen, Credits, etc ...

Lokales Ausführen des Testtreibers

Sie benötigen Python 3.7+ und ich empfehle, dass Sie auch tabulateüber Pip installieren . Das Scrappen dieser Seite für Einreichungen erfordert zusätzlich lxmlund requests. Sie sollten auch ein Terminal mit Unterstützung für ANSI-Farbverluste verwenden, um optimale Ergebnisse zu erzielen. Informationen zum Einrichten in Windows 10 finden Sie hier .

Fügen Sie Ihren Bot zu einer Datei in einem Unterverzeichnis in demselben Verzeichnis wie ruins.py( ruins_botsstandardmäßig) hinzu und stellen Sie sicher, dass Sie ihn from __main__ import Adventureroben im Modul hinzufügen . Dies wird zu den Modulen hinzugefügt, wenn der Scraper Ihre Einreichung herunterlädt, und obwohl es definitiv hacky ist, ist dies die einfachste Möglichkeit, um sicherzustellen, dass Ihr Bot richtig Zugriff hat Adventurer.

Alle Bots in diesem Verzeichnis werden zur Laufzeit dynamisch geladen, sodass keine weiteren Änderungen erforderlich sind.

Turnier

Der endgültige Sieger wird in einer Reihe von Spielen mit bis zu 10 Bots in jedem Spiel ermittelt. Bei mehr als 10 Einsendungen werden die 10 besten Bots ermittelt, indem sie systematisch in 10er-Gruppen aufgeteilt werden, bis jeder Bot (genau) 20 Spiele gespielt hat. Die Top 10 Bots werden aus dieser Gruppe mit zurückgesetzten Punktzahlen ausgewählt und spielen solange, bis der Erstplatzierte einen Vorsprung von 50 Punkten vor dem Zweitplatzierten erreicht hat oder bis 500 Spiele gespielt wurden.

Bis mindestens 10 Einsendungen vorliegen, werden leere Slots mit "Säufer" gefüllt, die nach dem Zufallsprinzip durch die Ruinen wandern und zufällige Schätze einsammeln (und gelegentlich fallen lassen), bis ihnen die Ausdauer ausgeht und sie geradewegs zum Ausgang müssen.

Turniere werden wöchentlich wiederholt, wenn neue Einreichungen vorliegen. Dies ist eine offene KOTH-Challenge ohne festgelegtes Enddatum.

Bestenliste

Vom Lauf am 4. Mai 2019 um 16:25 Uhr MEZ: (04.05.2019 16:25 - 06:00 Uhr)

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

Update - 15. April: Einige Regel-Updates / Klarstellungen

Update - 17. April: Verbot einiger bemerkenswerter Fälle schändlicher Handlungen wie das Ändern des Codes anderer Bots.

Update - 4. Mai: Sleafar erhält Kopfgeld, weil er Rückwärts absolut zerstört hat. Herzliche Glückwünsche!


1
Es ist endlich da! Ich schätze, ich muss jetzt anfangen, meinen Bot zu machen.
Belhenix,

12
Warum die Begrenzung auf einen Bot? Ich habe mehrere Ideen, die sich gegenseitig ausschließen, und ich möchte nicht jedes Mal, wenn ich mir einen neuen einfallen lasse, einen perfekten Bot rausschmeißen.

@Mnemonic, hauptsächlich, um zu verhindern, dass neue Einsendungen durch die Verwendung mehrerer nahezu identischer Bots ersetzt werden. Der andere Grund war zu verhindern, dass Bots zusammenarbeiten, aber das ist sowieso ausdrücklich verboten. Ich werde darüber nachdenken, es zuzulassen. Diejenigen, die mehrere Einreichungen zulassen möchten, stimmen dem obigen Kommentar von Mnemonic zu.
Beefster

1
@Draco18s Wenn Sie und pipinstalliert haben PATH(was bei neueren Installationen AFAIK Standard ist), können Sie über Windows pip install modulenamein der Eingabeaufforderung ausführen . Für andere Umstände (von denen ich nichts weiß) gehe zu pip , suche nach dem benötigten Modul und wähle eine Option.
Artemis unterstützt Monica

1
Ich vermute, dies wird ein Nein sein, aber dürfen wir Informationen während des Turniers speichern? (zB wenn ein Gebot funktioniert hat)
Artemis unterstützt Monica

Antworten:


5

Buchhalter

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'

Der Buchhalter ist eine sehr risikoaverse Person. Er möchte sicher sein, dass das, was er tut, in der gegebenen Situation die beste Option ist. Also setzt er sich ein Ziel und sammelt nur dann Schätze, wenn seine Berechnungen zeigen, dass er auf dem richtigen Weg zu diesem Ziel ist. Er ist jedoch sehr bürokratisch und mag es nicht, Gegenstände fallen zu lassen, für die er sich bereits entschieden hat; Alle Versuche, ihm dies beizubringen, führten bisher dazu, dass der Buchhalter einen Gegenstand fallen ließ und ihn unmittelbar danach wieder aufnahm.

Möglicherweise wird fortgesetzt.


1
Gute Arbeit, um den Wert eines Schatzes zu bestimmen. Ich hatte definitiv vor, einen besseren "ist es das wert" -Code zu schreiben, war aber noch nicht dort angekommen. Der Schurke kommt, um das Fazit des Buchhalters zu ziehen ...
Draco18s,

"Jeder Versuch, ihm dies beizubringen, führte bisher dazu, dass der Buchhalter einen Gegenstand fallen ließ und ihn unmittelbar danach wieder aufnahm." Sie können dies umgehen, indem Sie eine Reihe von abgelegten Schatznamen aufbewahren. Ich hatte das Gefühl, dass sich
Schatznamen als nützlich erweisen

Danke, aber ich habe schon herausgefunden, warum es passiert ist. Ich weiß nicht, ob ich das Problem sofort beheben werde, da er es selten verwendet, als ich es getestet habe.
ArBo,

2

Accomodator

Basiert locker auf meinem anderen LightWeight-Bot. Wo Lightweight bietet einfach war, dann ist dies bietet viel komplexer , um zu empfangen Wechselwirkungen mit anderen Bots: sowohl gutartige als auch deliberatly distruptive.

Dieser Bot sprintet zuerst in einen zufällig zugewiesenen Raum und versucht dann, nach dem besten Wert / Gewicht-Verhältnis-Schatz zu bieten. Wenn sich noch andere Spieler im Raum befinden, wird davon ausgegangen, dass sie ebenfalls bieten, statt nach dem zweitbesten Schatz zu bieten. Wenn dieses Gebot fehlschlägt, bieten Sie in der nächsten Runde für den nächstbesten Schatz.

Sobald ein Gebot erfolgreich war, wiederholen Sie das Bieten um das beste / zweitbeste, bis keine Schätze mehr im Raum vorhanden sind, und bewegen Sie sich dann tiefer in die Ruine

Geben Sie für jeden Raum in die Ruine ein und wiederholen Sie das Bieten um das Beste / Zweitbeste, bis keine Schätze mehr im Raum vorhanden sind, und bewegen Sie sich dann tiefer in die Ruine. Wenn wir feststellen, dass wir nicht tiefer gehen können, wechseln Sie in den Status "Verlassen" und beginnen Sie, das Schlimmste fallen zu lassen Schatz, bis wir garantieren können, dass wir die Ruine lebend verlassen können.

Wenn wir den Status verlassen, werden wir prüfen, ob wir 1 kg Schatz hinzufügen können und es trotzdem lebend herausfinden. Wenn dies der Fall ist, werden wir versuchen, auf einen 1 kg Schatz zu bieten. Wenn nicht, werden wir in den vorherigen Raum gehen.

Seine Leistung ist sehr unterschiedlich ... normalerweise wird er jedoch einer der drei besten Bots sein.

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'

Beeindruckend! Dieser dominiert das Turnier in rund 50 Runden.
Beefster

2

Sprinter

Ähnlich wie der Taucher geht Sprinter in die Tiefe und sammelt die besten Gegenstände auf dem Rückweg ein.

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')

Sprinter geht in die Tiefe, berechnet dann eine Punktzahl für jeden Schatz und nimmt alles auf, was eine bestimmte Schwelle überschreitet. Diese Schwelle hängt von dem Raum ab, in dem er sich gerade befindet, da die Entnahme von Gegenständen aus tiefer gelegenen Räumen mehr kostet.

Ich habe noch 2 Optimierungen in Bezug auf "Fighting for Treasure", die für die nächsten Tage geplant sind.

17.04 .: Scoundrel wurde zu schlau, Sprinter entschloss sich, ihn in eine Falle zu schieben. Anfangs wollte ich jeden Bot töten, der versuchte, Sprinter aufzurufen, aber der Testfahrer handhabt leider keine Ausnahmen, die bei Init auftreten. Das nächste Update für Scoundrel ist also ganz einfach ...


Das Töten von Schurken ist in Arbeit ...
AKroell

2

Vorausplanen

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

Ich habe die besten / schlechtesten Berechnungsmerkmale von Artemis Fowl verwendet , aber die Auswahllogik ist ganz von mir selbst und wurde seitdem geändert, um einige zusätzliche Faktoren zu berücksichtigen, z Schatz, nur um zurückzugehen, ihn fallen zu lassen und etwas anderes aufzuheben).

Bot Ventures, die so tief gehen, wie es für einigermaßen sicher gehalten wird (diese Berechnung funktioniert effektiv bis zu einer bestimmten Tiefe, hat jedoch die Flexibilität, mit anderen anfänglichen Ausdauerwerten umzugehen), sammeln dann Artefakte (priorisieren hohe Kosten und geringes Gewicht) beginnt sich zurückzuziehen, sobald es feststellt, dass es nicht mehr tragen kann.

Auf dem Weg nach draußen sammelt es zusätzliche Schätze, die es sicher zum Ausgang tragen kann. Es wird sogar bereits vorhandene Artefakte fallen lassen, wenn das neue Angebot besser ist und nicht zu Erschöpfung führt. Wenn es Platz im Rucksack hat, wird der neue Schatz aufgesammelt, bevor der niedrigere abgeworfen wird, wodurch der Kampf mit anderen Bots minimiert wird.


Huh, wenn du es beschreibst, ist es genauso wie meins. Ich werde mit meinen Zahlen herumspielen ... Hinweis: Die __init__Funktion ist bereits implementiert, Sie müssen sie nicht überschreiben.
Artemis unterstützt Monica


2

Artyventurer

Schlägt die Säufer um etwa 1000 Dollar! Konnte nicht an einen kreativen Namen denken, aber hier sind Sie:

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'

Manchmal macht der größte Teil des Codes hier nie etwas (zumindest wenn ich ihn gegen Drunkards teste), das Programm versucht nur den besten Zug zu finden, einschließlich der Verarbeitung für Situationen, in denen es versucht, sich nicht selbst zu versetzen. Einiges davon kann nie laufen, es ist nur da, damit ich mit den Zahlen herumspielen kann, die wahrscheinlich noch verbessert werden können.

Erläuterung

  • if state.inventory ... worsti = None
    Finden Sie den 'schlechtesten' Gegenstand im Inventar, dh den Gegenstand mit dem niedrigsten Verhältnis von Wert zu Gewicht. Es speichert worstiden Index, den Wert und das Gewicht als Tupel oder Nonewenn sich keine Gegenstände im Inventar befinden.

  • if self.drop ... return 'drop', worsti[0]
    Wenn ich ihm sagte, dass er diese Runde in der letzten Runde fallen lassen soll (siehe unten), kann er den oben berechneten "schlimmsten" Gegenstand fallen lassen.

  • extra = ... * state.room
    Berechnen Sie, wie viel Ausdauer es übrig geblieben wäre, wenn ich gesagt hätte, dass es sofort zurückgehen soll.

  • if extra > CONTINUE_IN:\ return 'next'
    Wenn es mehr als CONTINUE_IN ist, kehren Sie zurück 'next'.

  • if extra < 0 and worsti:\ return 'drop', worsti[0]
    Wenn es kleiner als ist 0, lass das schlechteste Objekt fallen.

  • if extra < state.room:\ return 'previous'
    Wenn es weniger als die Zimmernummer ist (kann keinen Schatz mehr tragen), gehe zurück.

  • if state.treasures: ... bestt = None
    Finde den besten Schatz, den du nehmen kannst, ähnlich dem schlechtesten Gegenstand im Inventar oben. Speichern Sie es in bestt.

  • if extra > 0 and bestt: ... return 'take', bestt[0], bid
    Bei aktuellen Zahlen wird dies ausgeführt, sobald wir soweit sind und ein Schatz verfügbar ist. Wenn es sicher ist, den 'besten' Schatz zu nehmen, tut es das auch. Das Gebot ist das Minimum oder eines mehr, wenn jemand anwesend ist.

  • if bestt and worsti: ... return 'take', bestt[0], bid
    Bei aktuellen Nummern wird dieser Codeblock niemals ausgeführt, da der vorherige Codeblock eine umfassendere Bedingung aufweist. Dies wird ausgeführt, wenn wir so weit sind und beide Schätze in meinem Inventar und im Raum sind. Wenn der 'beste' Schatz im Raum wertvoller ist als der 'schlechteste' Schatz in meinem Inventar und es sicher ist, sie in den nächsten zwei Runden zu tauschen, ist dies der Fall.

  • return 'previous'
    Wenn nichts davon passiert, gehe einfach zurück.

Update 16.04.19:

Anti-Schurken-Maßnahmen. Dies wird zu einem Bieterkrieg :(

Weiteres Update 16.04.19:

Zurückgesetztes vorheriges, stattdessen wechselt zufällig jedes andere Element, wenn es das beste findet, z. [1, 2, 3, 4, 5, 6] → [2, 1, 3, 4, 6, 5]. Sollte schwerer zu kopieren sein :).

Update 17/04/19:

Vorher zurückgesetzt, löscht es stattdessen seinen eigenen Quellcode . Es tut dies, __init__was immer vorher sein wird Scoundrel.enter_ruins, und hindert Scoundrel daran, es zu laden. Es ersetzt seinen Code, wennget_action ersten Aufruf, damit er für das nächste Mal bereit ist. BEHOBEN, Scoundrel stirbt jetzt bei der Ankunft.

Weiteres Update 17.04.19:

Vorher zurückgesetzt, ersetzt es stattdessen seinen sys.modulesEintrag durch das Mathematikmodul, sodass es stattdessen das Mathematikmodul lädt, wenn Scoundrel versucht, es zu laden. :)
Außerdem habe ich gerade erst gemerkt, dass die Bewegungsausdauer 10 + Gewicht / 5 beträgt , also habe ich versucht, das zu beheben.

Weiteres Update 17.04.19:

Enthält jetzt Knoblauch aus beiden vorherigen Updates.

Update 18.04.19:

Wenn Sie mit Zahlen und Berechnungen experimentieren, erhalten Sie jetzt $ 2000 - $ 3000.

Weitere Aktualisierung 18.04.19:

Entfernt den verbotenen Knoblauch zum Löschen von Dateien und fügt neuen Knoblauch hinzu, der sicherstellt, dass er 'draco'nicht dafür verantwortlich ist, dass er ausgeführt wird, wenn er gerade previousbeim ersten Zug zurückkehrt. Die Ergebnisse haben einen mysteriösen Sprung auf 1200 bis 1800 US-Dollar gemacht, auf den ich mich gerade konzentriere.


Scheint sehr effektiv gegen Säufer zu sein. Ich würde gerne sehen, wie es läuft, wenn sich andere Bots dem Raid anschließen :)
Moogie,

@ Moogie schlägt den Taucher um etwa 100 $, wenn auch 8 Säufer anwesend sind.
Artemis unterstützt Monica

2

Der Schurke

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'

Der Schurke arbeitet hauptsächlich, um andere Teilnehmer zu stören. Derzeit stört es Sprinter, Artyventurer und Accountant (diese Liste wird im Laufe der Zeit erweitert, sofern dies im Interesse des Schurken liegt). Dies geschieht, indem die anderen Bots nachgeahmt werden und dann entweder überboten, unterschnitten oder auf andere Weise um Relikte gekämpft wird. Insofern ist es unwahrscheinlich, dass dieser Eintrag jemals die Rangliste dominiert und stattdessen als verwöhnende Kraft wirkt. Die aktuelle Version zum Zeitpunkt der Veröffentlichung belegt den 2. Platz mit einer durchschnittlichen Punktzahl von ca. 7.

Scoundrel vereitelt den Versuch eines anderen Bots, sich gegen den Scoundrel zu verteidigen, indem er den Code des anderen Teilnehmers direkt als nicht unterscheidbare Klonkopie ausführt. Probleme mit Importen, die zu doppelten Einträgen führten, wurden behoben, indem die Klone über Reflection erstellt wurden (Bearbeitungskriege mit genauen Details der mathematischen Bestimmung sind aus Sicht des Stapelaustauschs nicht wünschenswert, würden jedoch zum gleichen Ergebnis führen). Die KOTH-Herausforderungen haben es in der Vergangenheit auch zugelassen.

Schurke ersetzt die Teamsters, um die Teamsters interessant zu halten. Nach dieser Bearbeitung sollten Teamsters nicht mehr vom Controller geschabt werden.

Update 17.04.2013: Weitere Gegenmaßnahmen.

Die Teamsters (illegal gemacht)

Aber fühle dich frei, vor Ort zu rennen, wo es nicht mehr als 8 andere Teilnehmer gibt!

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

Dieser Eintrag (der jetzt ausdrücklich ungültig ist) ist tatsächlich zwei Bots, und der Controller freut sich schabt sie beide und fügt sie der Kandidatenliste hinzu (weil Hurra, Python?).

Phase 1:

  • TeamsterA erreicht Level 25 (ish) 1 und nimmt wiederholt den leichtesten Schatz, den er finden kann, und lässt ihn fallen. Dies kostet bis zur zweiten Phase satte 1 Ausdauer pro Runde.
  • TeamsterB geht zu Level 55 und nimmt alle herumliegenden Wertsachen auf und geht dann zurück zu Level 25 (ish). 2 Dann beginnt Phase 2.

1. Wenn es keinen Schatz gibt, der weniger als 3 wiegt, bewegt er sich nach unten.
2. Da er so ziemlich garantiert der letzte ist Abenteurer ist, der an die Oberfläche zurückkehrt, muss er nur jemanden finden.

Phase 2:

  • TeamsterB leert seine Taschen, bevor er vor Erschöpfung davonkriecht. Wir wussten, dass Sie es schaffen könnten.
  • TeamsterA denkt, "das sind ein paar glänzende Schmuckstücke, guter Kumpel, alter Kumpel!" und beladen Sie die viel wertvolleren Schätze als den anderen Müll im Raum, bevor Sie mit Taschen voller Gold zum Ausgang gehen.

Der Name der Schätze erwies sich als nützlich, um der Logik zu helfen, keinen Müll auf Etage 25 zu laden und früh zu verschwinden, da es keine Möglichkeit gab, zwischen den beiden Bots zu kommunizieren (und TeamsterA befand sich vorher immer in einem Raum mit jemand anderem TeamsterB war zurückgekehrt).

Die nächste logische Schlussfolgerung: Eine Armee aufstellen

Theoretisch könnte dies verwendet werden, um die Tiefen auszuloten und Schätze aus der Tiefe von Raum 98 zu sammeln. Da dies jedoch mehr als zwei Bots erfordern würde, würde die Logik, die diese Bots umfasst, immer komplexer, und da ich mir sicher bin, dass dies der Fall ist eine illegale Einreichung wegen Verstoßes gegen eine ungeschriebene Regel, also werde ich mich nicht darum kümmern.

AWartet effektiv um 30, Bwartet um 50 ... nspringt auf 98, nimmt einen Schatz auf, geht auf 97, lässt ihn fallen (und stirbt dann), n-1nimmt ihn auf und geht auf 96 ... Clässt ihn fallen (stirbt), Bholt ihn ab auf und geht auf 30, lässt es fallen (stirbt),A nimmt es auf und kehrt zum Ausgang zurück.

Ich schätze, dass dies 11 Bots dauern würde.

Es lohnt sich jedoch nicht, es sei denn, Sie können ungefähr 4 Schätze aus dieser Tiefe bergen, um mit Einträgen wie PlanAhead oder Artyventure zu konkurrieren. Dies liegt an der Skalierung zwischen den Ausdauer-Bewegungskosten und dem Durchschnittswert der Schätze.

Probenergebnisse

Erzielt selten weniger als 4000 US-Dollar und erreicht gelegentlich 6000 US-Dollar.

[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
Ich denke, wenn es nur einen Bot pro Person geben würde, wäre eine so explizite Regel nicht erforderlich. Die Regel, aus gefährlichen Gründen auf einen bestimmten Bot abzuzielen, ist jedoch nicht gleichbedeutend damit, dass mehrere Bots in einem Team zusammengefasst werden. Daher ist eine ausdrückliche Entscheidung des OP erforderlich.
Moogie

Ja, das wird ein Nein von mir, dawg. Dies ist die Art von Dingen, die ich im Sinn hatte, wenn Bots zusammenarbeiten.
Beefster

1
@Beefster Das habe ich mir gedacht. Ich hatte Spaß daran. Ich werde mich heute Abend um die Bearbeitung kümmern, um die Aufnahme zu verhindern.
Draco18s

Ich werde in Betracht ziehen, dies zuzulassen, sobald es mehr als 11 Konkurrenten gibt, da seine Wirksamkeit ohnehin nachlässt. Hauptsächlich, weil ich keinen Code für Autoban-Übermittlungen erstellen möchte.
Beefster

Wenn Sie bereits nur den ersten Codeblock abkratzen, muss ich nur in einem anderen Bot oben bearbeiten.
Draco18s

2

Rückwärts

Weil es umgekehrt funktioniert

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'

Warum heißt es Rückwärts?

Weil ich den Buchhalter mitgenommen habe und versucht habe, ihn so zu logisieren, dass er tief taucht und dann seine bevorzugte Beute auf dem Weg nach draußen aufnimmt (rückwärts vom Buchhalter).

Am Ende sammelt es immer noch einen Großteil seiner Preise auf dem Weg hinein (es schöpft sie aus, bevor es die traditionellen In-Collect-Out-Sucher tun, die rückwärts zu allen anderen agieren), aber es ist viel selektiver welche es nimmt, obwohl es immer noch nimmt Dinge auf dem Rückweg auf.

Das Endergebnis ist, dass die Ausdauer auf dem Weg nach innen erhalten bleibt, während immer noch hochwertige Schätze im Vordergrund stehen. Auf dem Weg nach oben profitieren Sie dann von einer tiefen Wendung und einer leichten Ernte. Es ist bekannt, dass Rückwärts Schätze aus der Tiefe von Raum 41 sammelt (und während der Entwicklung Raum 42 betritt und dann sofort verlässt).


2

Kopfgeldjäger

Die einfache Methode ist die beste. Sammle wertvolle und leichte Schätze und gehe dabei so tief wie möglich. Besorgen Sie sich auf dem Rückweg weniger wertvolle Schätze.

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'

Sieht so aus, als würdest du das Kopfgeld bekommen. Dies ist nicht nur leistungsfähiger als Rückwärts, sondern bewirkt auch, dass Rückwärts tankt. Gut gemacht.
Beefster

1

Leicht

Ein einfacher Bot, der immer noch recht gut abschneidet.

Nach dem Betreten der Ruinen (derzeit 21 Räume) wird der beste Schatz im Raum gefunden, der nur 1 kg wiegt (daher der Name des Bots) und wertvoller ist als der am wenigsten wertvolle Schatz im Inventar. Wenn das Inventar voll ist, lasse den am wenigsten wertvollen Schatz fallen. Wenn keine andere Aktion ausgewählt ist, erfolgt der Einzug in die Ruinen. Wenn wir an der Grenze unserer Ausdauer sind, um lebend herauszukommen, begeben wir uns zum Ausgang

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'

Ich würde empfehlen, dumpingdie enter_ruinsMethode einzufügen. Dies wird sich tatsächlich zwischen den Spielen daran erinnern und wird bei Spiel 2 nicht funktionieren. Technisch nicht erlaubt, aber ich habe die Regel erst jetzt hinzugefügt (ich habe es vorher vergessen, aber es war meine Absicht), also werde ich ein wenig nachlassen. : P
Beefster

@Beefster Ich habe das Dumping-Status-Flag entfernt, es wird nicht benötigt, da der Bot jetzt nur einen Schatz ablegt. Früher hat es die Hälfte seines Schatzes abgeladen. Sollte also mit der neuen Regel kompatibel sein.
Moogie

1

Auswendiglernen

Ich kann Bots an meine eigene KotH senden, oder?

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'

Dieser Bot taucht in Raum 30 und merkt sich alle Schätze, die er gesehen hat. An diesem Punkt beginnt er seinen Rückweg zum Eingang und versucht, gute Schätze zu sammeln, an die er sich in früheren Räumen erinnert hatte.

Ich hoffte, es würde besser gehen. Mögliche Verbesserungen ergeben sich möglicherweise aus einer besseren Planung und einer dynamischeren Ermittlung des Raums, in dem nicht mehr getaucht wird, sowie aus einer größeren Bereitschaft, die Backup-Optionen zu prüfen.

Update: Schnappt euch jetzt 1 kg Schätze im Wert von 60 USD oder mehr.


Ich stelle mir vor, dass all dieser gute Schatz einfach verschwunden ist, wenn der Bot zurückkommt ... Vielleicht können Sie eine Combo ausprobieren, bei der das wirklich gute Zeug auf dem Weg dorthin ist, wenn Sie den mittelmäßigen Schatz bedenken, den es aufheben könnte auf dem Rückweg?
ArBo

Es könnte zu weit gehen
Beefster

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

1

Ponderer

Ich denke, es ist Memorizer ziemlich ähnlich, da es die Kenntnis der besuchten Räume verwendet, um auszuwählen, welche Räume und Schätze auf dem Weg zurück zum Ausgang gesammelt werden sollen. Es wurde jedoch unabhängig abgeleitet.

Dieser Bot sprintet, bis ein zufälliger tiefer Raum eine Aufzeichnung der unterwegs gefundenen Schätze aufnimmt. Sobald es im Zielraum angekommen ist, wird es über die ideale Auswahl an Schätzen nachdenken , die es auf dem Weg zurück zum Ausgang findet. Jede Runde wird es erneut nachgedacht , um die wahrscheinlich beste Auswahl an Schätzen zu finden.

Derzeit gibt es einen einfachen Algorithmus (Potenz der Raumnummer umgekehrt), der die angenommene Anzahl an Schätzen ergibt, die für jeden Raum genommen wurden (oder genommen wurden, wenn dieser Bot ihn besucht). Diese Schätze werden ignoriert, wenn über welche Schätze / Räume nachgedacht wird zu nehmen aus. Ich habe Ideen für andere fortgeschrittene Algorithmen, um zu modellieren, welche Schätze noch vorhanden sind. Aber ich werde sehen müssen, ob sich der Nutzen lohnt.

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

Hamsterer

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

Der Hamsterer bleibt in einem Raum, bis er alle Schätze im Raum genommen hat (oder berechnet, dass er nicht genug Ausdauer hat, um weiterzumachen). Wenn alle Schätze verschwunden sind und der Bot sich sicher fortbewegen kann, wird er den Prozess fortsetzen, alle Schätze zu nehmen.


Dies stirbt bei jedem Spiel, indem der Rucksack überfüllt wird.
Beefster

wie ich in Minecraft (͡ ° ͜ʖ ͡ °) Dieser Bot wird plündern, tiefer gehen und dann wertvolle Beute finden. Also wird es fallen, was er früher für gute Beute hielt. Das ist, warum Backwards's, Sprinter' s und Memorizer's Strategie arbeiten; weil sie wissen, was die relativen Werte jedes Schatzes sind, den sie sehen.
V. Courtois

0

Taucher

(Kann im Moment nicht testen, lass es mich wissen, wenn dies kaputt ist.)

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'

Der beste Schatz liegt tiefer in den Ruinen. Tauchen Sie also tief ein und holen Sie sich auf dem Weg nach draußen, was wir können.


Ich bin nicht sehr erfahren mit Python, aber wo ist das divingdefiniert?
Verkörperung der Ignoranz

1
@EmbodimentofIgnorance In enter_ruins (), das aufgerufen wird, bevor das Spiel ausgeführt und Aktionen ausgeführt werden.

Jacob the Orphan (Diver) was sliced in half by a swinging blade trap.Ich bin mir nicht sicher, was Sie falsch gemacht haben, aber das bedeutet, dass die AFAIK ungültig ist.
Artemis unterstützt Monica

@ArtemisFowl bot er zu niedrig für den Schatz. Es kostet das Gewicht des Schatzes, ihn aufzuheben.
Beefster

@ Beefster Oh yup.
Artemis unterstützt Monica
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.