Petualang di Reruntuhan


27

Test DriverDiskusi TantanganKirim Adventurer

Kamar Harta Karun ( Sumber Gambar )

Beberapa petualang saingan menggerebek reruntuhan untuk harta, tetapi mereka hanya bisa membawa begitu banyak pada suatu waktu dan memiliki batas daya tahan mereka. Mereka ingin mendapatkan harta yang paling berharga dan keluar sebelum mereka menjadi terlalu lelah untuk melanjutkan. Mereka berusaha menjadi sekaya mungkin dari kejahatan mereka yang menjarah.

Gameplay

Setiap petualang mulai di ruang pertama ruang bawah tanah dengan 1000 poin stamina dan 50kg ruang di ransel mereka.

Permainan beroperasi berdasarkan mode giliran, dengan semua pemain menyelesaikan giliran secara bersamaan. Setiap belokan, Anda dapat melakukan salah satu tindakan berikut:

  • Pindah ke kamar sebelah.
  • Pindah ke kamar sebelumnya.
  • Tawaran stamina untuk mengambil harta.
  • Jatuhkan harta.

Bergerak di antara kamar membutuhkan 10 stamina, ditambah 1 untuk setiap 5kg saat ini di ransel Anda, dibulatkan. Misalnya, seorang petualang yang membawa 3kg harta membutuhkan 11 stamina untuk bergerak dan satu yang membawa 47kg membutuhkan 20 stamina untuk bergerak.

Menjatuhkan harta membutuhkan 1 stamina terlepas dari harta yang dijatuhkan.

Saat keluar dari reruntuhan, tidak ada lagi belokan yang akan diambil oleh pemain.

Jika seorang pemain tidak dapat mengambil tindakan ini (karena kekurangan stamina atau tidak adanya harta), petualang mereka meninggal karena kelelahan, menumpahkan harta yang mereka miliki ke ruang yang saat ini diduduki. Demikian pula, jika seorang pemain mencoba melakukan tindakan yang tidak valid, petualang mereka akan terbunuh oleh jebakan, yang mengakibatkan tumpahan harta yang sama.

Menawar

Tawaran minimum untuk harta adalah 1 stamina per 1kg yang beratnya ditimbang. Anda juga dapat mengajukan poin stamina tambahan untuk lebih mungkin mendapatkan harta. Stamina yang dicoba dikonsumsi tidak peduli apa hasilnya.

Jika ada beberapa pemain yang memiliki tawaran untuk mengambil harta yang sama, pemain yang menawar tertinggi akan mendapat harta. Jika lebih dari satu pemain melakukan penawaran tertinggi, tidak ada dari mereka yang akan menerima harta.

Kondisi menang

Pemain dengan nilai total harta terbesar adalah pemenangnya. Dalam hal yang tidak mungkin terjadi dasi, ikatan pergi ke berat total terkecil, kemudian jumlah harta terkecil, kemudian nilai harta paling berharga, kedua paling berharga, ketiga ... sampai dasi rusak. Dalam hal yang hampir mustahil bahwa masih ada ikatan pada titik ini, pengemudi tes mengatakan "sekrupkan" dan pemenangnya ditentukan secara sewenang-wenang.

Dalam konteks turnamen, pemain akan diperingkat dengan tempat pertama menerima 10 poin, tempat kedua dengan 9 poin, tempat ketiga dengan 8 poin, dll ..., dengan pemain mati dan petualang tanpa harta mencetak 0 poin.

Tentang Reruntuhan

  • Setiap kamar awalnya berisi antara r3+3danr2+5harta. (Di manaradalah nomor kamar)
  • Ada banyak kamar yang sewenang-wenang, hanya dibatasi oleh stamina dan keinginan petualang untuk menjelajah.
  • Setiap harta akan memiliki nilai moneter (dalam seluruh $) dan berat (dalam seluruh kg).
    • Harta karun cenderung lebih berharga dan berlimpah saat Anda masuk lebih dalam ke reruntuhan.
  • Rumus khusus untuk menghasilkan harta adalah sebagai berikut: (menggunakan xdy notasi untuk dadu gulungan)
    • Berat dihasilkan pertama kali menggunakan rumus 2d6-2 (minimal 1)
    • Nilai harta kemudian dihasilkan melalui 1d[10w]+2d[5r+10] (di mana r adalah nomor kamar dan w adalah berat)

Informasi Yang Terlihat oleh Pemain

Di setiap belokan, pemain mendapatkan informasi berikut:

  • Jumlah kamar mereka saat ini. Ini adalah 1-diindeks, jadi secara konseptual pintu keluarnya ada di "kamar 0"
  • Daftar harta saat ini di dalam ruangan
  • Daftar pemain lain yang juga saat ini ada di dalam ruangan.
  • Persediaan harta karun Anda saat ini
  • Level stamina Anda saat ini

Coding

Driver tes dapat ditemukan di sini .

Anda harus mengimplementasikan subkelas dari Adventurerkelas ini :

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

Anda hanya perlu mengganti get_actionmetode. enter_ruinsdijalankan sebelum permainan dimulai dan merupakan kesempatan Anda untuk mempersiapkan apa pun yang Anda ingin siapkan untuk permainan. Anda tidak perlu menimpa __init__, dan Anda benar - benar tidak boleh melakukannya . Jika __init__macet, Anda akan didiskualifikasi.

get_actionmenerima argumen tunggal yang namedtupledengan bidang berikut (dalam urutan ini, jika Anda lebih suka merusak):

  • room: jumlah kamar tempat Anda berada saat ini
  • treasures: daftar harta karun di dalam ruangan
  • players: daftar pemain lain di dalam ruangan. Anda hanya mendapatkan nama pemain dengan cara ini, jadi Anda tidak tahu bot apa yang mengendalikan mereka atau inventaris / stamina mereka.
  • inventory: daftar harta di ransel Anda
  • stamina: tingkat stamina Anda saat ini

Objek ini juga menyediakan dua properti utilitas:

  • carry_weight: total berat semua harta yang Anda bawa
  • total_value: nilai total semua harta yang Anda bawa

Daftar treasuresdan inventoryberisi namedtuples dengan atribut ini:

  • name: nama harta karun (untuk keperluan kosmetik)
  • value: nilai moneter dari harta dalam $.
  • weight: berat harta dalam kg

get_action harus mengembalikan salah satu dari nilai / pola berikut:

  • 'next'atau 'previous'untuk pindah ke kamar berikutnya / sebelumnya
  • 'take', <treasure index>, <bid>(ya, sebagai tuple, meskipun urutan apa pun secara teknis akan berfungsi juga) untuk menawar harta pada indeks yang diberikan dalam daftar harta karun kamar. Kedua argumen harus bilangan bulat. Mengapung akan dibulatkan ke bawah.
  • 'drop', <inventory index>untuk menjatuhkan harta yang dibawa ditemukan pada indeks yang diberikan. Indeks harus (secara alami) menjadi bilangan bulat.

Batasan Lainnya

  • Anda hanya dapat menggunakan contoh acak yang diberikan kepada Anda selama inisialisasi untuk pseudorandomness.
    • Hal lain yang mungkin memperkenalkan perilaku non-determinasi tidak diperbolehkan. Maksudnya di sini adalah untuk membuat bot berperilaku identik ketika diberi benih yang sama untuk membantu dalam menguji bot baru (dan berpotensi bug di driver tes). Hanya radiasi kosmik yang harus menyebabkan penyimpangan / nondeterminisme.
    • Perlu diingat bahwa kode hash diacak dalam Python 3, jadi menggunakan hashuntuk pengambilan keputusan apa pun tidak diperbolehkan. dicts baik-baik saja bahkan ketika menggunakan perintah iterasi untuk keputusan karena pesanan telah dijamin konsisten sejak Python 3.6.
  • Anda tidak boleh mengelak dari driver tes menggunakan ctypeshacks atau inspectstack voodoo (atau metode lain). Ada beberapa hal menakutkan yang dapat Anda lakukan dengan modul-modul itu. Tolong jangan.
    • Setiap bot di-sandbox dengan cukup baik melalui salinan defensif dan namedtupleketidakmampuan alami dari s, tetapi ada beberapa celah / eksploitasi yang tidak dapat ditambal.
    • Fungsionalitas lain dari inspectdan ctypesdapat digunakan selama keduanya tidak digunakan untuk menghindari fungsi pengontrol.
    • Metode apa pun untuk mengambil instance bot lain dalam gim Anda saat ini tidak diperbolehkan.
  • Bot harus beroperasi sendiri dan tidak boleh berkoordinasi dengan bot lain dengan cara apa pun untuk tujuan apa pun. Ini termasuk menciptakan dua bot dengan tujuan yang berbeda sehingga satu mengorbankan dirinya untuk keberhasilan yang lain. Setelah ada lebih dari 10 pesaing, Anda tidak akan benar-benar dijamin memiliki dua bot dalam permainan yang sama dan nama petualang tidak memberikan indikasi kelas bot, jadi jenis strategi ini tetap terbatas.
  • Saat ini tidak ada batasan keras pada waktu pelaksanaan, namun saya berhak untuk membatasi dengan keras di masa depan jika turnamen mulai memakan waktu terlalu lama. Bersikaplah masuk akal dan cobalah untuk tetap memproses di bawah 100 ms , karena saya tidak mengantisipasi perlu membatasi di bawah ambang itu. (Turnamen akan berjalan sekitar 2 jam jika semua bot membutuhkan waktu sekitar 100 ms per putaran.)
  • Kelas bot Anda harus dinamai secara unik di antara semua kiriman.
  • Anda mungkin tidak ingat apa pun di antara game. (Namun, Anda dapat mengingat hal-hal di antara belokan )
    • Jangan edit sys.modules. Apa pun di luar variabel instance harus diperlakukan sebagai konstanta.
  • Anda tidak boleh memodifikasi kode bot apa pun secara terprogram, termasuk kode Anda.
    • Ini termasuk menghapus dan mengembalikan kode Anda. Ini untuk membuat debugging dan turnamen lebih efisien.
  • Kode apa pun yang menyebabkan pengontrol macet akan segera didiskualifikasi. Sementara sebagian besar pengecualian akan ditangkap, beberapa mungkin lolos dan segfault tidak dapat ditandingi. (Ya, Anda dapat melakukan segmentasi dengan Python terima kasih kepada ctypes)

Pengajuan

Untuk membantu pengikisan jawaban, sebutkan nama bot Anda di bagian atas jawaban dengan #Header1dan pastikan jawaban Anda menyertakan setidaknya satu blok kode (hanya yang pertama dalam jawaban Anda yang akan digunakan). Anda tidak perlu memasukkan impor atau dokumen, karena akan ditambahkan secara otomatis oleh scraper.

Saya akan lebih cenderung memilih jawaban dengan penjelasan terperinci dan dapat dimengerti. Orang lain cenderung berperilaku sama.

Secara kasar, jawaban Anda harus diformat seperti ini:

# Name of Bot
Optional blurb

    #imports go here

    class BotName(Adventurer):
        #implementation

Explanation of bot algorithm, credits, etc...

(diterjemahkan sebagai)

Nama Bot

Uraian opsional

#imports go here

class BotName(Adventurer):
    #implementation

Penjelasan tentang algoritma bot, kredit, dll ...

Menjalankan Test Driver Secara Lokal

Anda akan membutuhkan Python 3.7+ dan saya sarankan Anda juga menginstal tabulatevia pip. Memotong halaman ini untuk pengiriman juga membutuhkan lxmldan requests. Anda juga harus menggunakan terminal dengan dukungan untuk pelolosan warna ANSI untuk hasil terbaik. Info tentang cara mengatur ini di Windows 10 dapat ditemukan di sini .

Tambahkan bot Anda ke file dalam subdirektori dalam direktori yang sama dengan ruins.py( ruins_botssecara default) dan pastikan untuk menambahkan from __main__ import Adventurerke bagian atas modul. Ini ditambahkan ke modul ketika scraper mengunduh kiriman Anda, dan meskipun pasti hacky, ini adalah cara paling mudah untuk memastikan bot Anda memiliki akses Adventurer.

Semua bot dalam direktori itu akan dimuat secara dinamis saat runtime, jadi tidak ada perubahan lebih lanjut yang diperlukan.

Turnamen

Pemenang utama akan ditentukan dalam serangkaian game dengan hingga 10 bot di setiap game. Jika ada lebih dari 10 total pengiriman, 10 bot teratas akan ditentukan dengan secara sistematis memecahnya menjadi kelompok-kelompok 10 hingga setiap bot telah memainkan (tepatnya) 20 pertandingan. 10 bot teratas akan dipilih dari grup ini dengan skor reset dan akan memainkan permainan sampai bot tempat pertama telah mencapai 50 poin memimpin di atas bot tempat kedua atau sampai 500 pertandingan telah dimainkan.

Sampai setidaknya ada 10 pengajuan, slot kosong akan diisi dengan "Pemabuk" yang berkeliaran secara acak melalui reruntuhan dan mengambil (dan kadang-kadang menjatuhkan) harta acak sampai kehabisan stamina dan harus menuju ke pintu keluar.

Turnamen akan dijalankan kembali setiap minggu jika ada kiriman baru. Ini adalah tantangan KOTH terbuka tanpa tanggal akhir yang ditetapkan.

Papan peringkat

Dari lari pada 4 Mei 2019 pada 16: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

Pembaruan - 15 Apr: pembaruan / klarifikasi peraturan pasangan

Pembaruan - 17 Apr: melarang beberapa kasus tepi dari tindakan jahat seperti memodifikasi kode bot lain.

Pembaruan - 4 Mei: Hadiah yang diberikan kepada Sleafar karena benar-benar menghancurkan Mundur. Selamat!


1
Akhirnya di sini! Kira saya harus mulai membuat bot saya sekarang.
Belhenix

12
Mengapa batas satu bot? Saya memiliki beberapa ide yang saling eksklusif, dan saya lebih suka tidak harus membuang bot yang sangat bagus setiap kali saya membuat yang baru.

@Mnemonic, sebagian besar untuk mencegah perpindahan kiriman baru dengan menggunakan beberapa bot yang hampir identik. Alasan lainnya adalah untuk mencegah bot bekerja sama, tetapi itu tetap dilarang. Saya akan mempertimbangkan untuk mengizinkannya. Orang-orang yang mendukung memungkinkan pengiriman banyak, mendukung komentar Mnemonic di atas.
Beefster

1
@ Draco18s Jika Anda telah pipmenginstal dan menghidupkan PATH(yang merupakan standar untuk instalasi yang lebih baru AFAIK) maka dari windows Anda dapat menjalankannya pip install modulenamedi command prompt. Untuk keadaan lain (yang saya tidak tahu), buka pip , cari modul yang diperlukan dan pilih opsi.
Artemis mendukung Monica

1
Saya menduga ini akan menjadi 'tidak', tetapi apakah kita diizinkan untuk menyimpan informasi melalui turnamen? (mis. ketika tawaran bekerja)
Artemis mendukung Monica

Jawaban:


5

Akuntan

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'

Akuntan adalah orang yang sangat menghindari risiko. Dia suka memastikan bahwa apa yang dia lakukan benar-benar adalah pilihan terbaik dalam situasi tertentu. Jadi, dia menetapkan dirinya tujuan, dan hanya akan mengambil harta jika perhitungannya menunjukkan ini menempatkannya di jalur yang benar menuju tujuan itu. Namun, dia sangat birokratis dan tidak suka menjatuhkan barang yang sudah dia putuskan dia inginkan; setiap upaya untuk mengajarinya melakukannya sejauh ini mengakibatkan Akuntan menjatuhkan barang, dan kemudian mengambilnya kembali segera setelah itu.

Mungkin akan dilanjutkan.


1
Pekerjaan yang bagus menentukan nilai harta karun. Saya benar-benar ingin menulis kode yang lebih baik "apakah itu layak", tetapi belum sampai di sana. Bajingan itu datang untuk garis bawah akuntan, meskipun ...
Draco18s

"Setiap upaya untuk mengajarinya sejauh ini mengakibatkan akuntan menjatuhkan item, dan kemudian mengambilnya kembali segera setelah itu." Anda bisa menyiasatinya dengan menyimpan satu set nama harta yang dijatuhkan. Saya punya perasaan bahwa nama harta akan berguna (meskipun mereka baru saja diberi nomor sekarang)
Beefster

Terima kasih, tapi saya sudah tahu mengapa itu terjadi. Tidak tahu apakah saya akan segera memperbaikinya, karena dia jarang menggunakannya ketika saya diuji memasukkannya.
ArBo

2

Akomodasi

Berbasis longgar pada bot LightWeight saya yang lain. Di mana bot LightWeight sederhana, bot ini jauh lebih kompleks untuk mengakomodasi interaksi dengan bot lain: baik jinak dan sengaja mengganggu.

Bot ini pertama-tama akan berlari ke ruangan yang ditugaskan secara acak dan kemudian akan mencoba menawar untuk nilai rasio rasio berat / berat terbaik, jika ada pemain lain di ruangan itu kemudian berasumsi bahwa mereka juga akan menawar jadi ganti tawaran untuk harta terbaik kedua. Jika tawaran itu gagal maka pada gilirannya giliran tawaran untuk harta terbaik berikutnya.

Setelah penawaran berhasil, ulangi penawaran untuk best / secondbest hingga tidak ada lagi harta di ruangan kemudian pindah lebih dalam ke kehancuran

Untuk setiap kamar masuk ke dalam ruin, ulangi penawaran untuk best / secondbest sampai tidak ada lagi harta di ruangan itu kemudian pindah lebih dalam ke reruntuhan atau jika kami mendeteksi bahwa kami tidak bisa masuk lebih dalam kemudian bertukar ke keadaan 'Keluar' dan mulai jatuhkan yang terburuk harta sampai kita dapat menjamin bahwa kita dapat keluar dari kehancuran hidup-hidup.

Ketika dalam keadaan keluar kita akan memeriksa untuk melihat apakah kita dapat menambahkan harta 1kg dan masih membuatnya hidup-hidup, jika demikian maka kita akan berusaha untuk menawar harta 1kg, jika tidak maka kita akan menuju ke kamar sebelumnya.

Kinerjanya sangat bervariasi ... namun biasanya akan menjadi salah satu dari tiga bot teratas.

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'

Impresif! Yang ini mendominasi turnamen dalam sekitar 50 putaran.
Beefster

2

Pelari cepat

Mirip dengan Diver, Sprinter masuk ke dalam dan mengambil barang-barang terbaik dalam perjalanan kembali.

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 masuk lebih dalam lalu menghitung skor untuk setiap harta dan mengambil apa pun di atas ambang batas tertentu. Ambang ini tergantung pada ruangan tempat dia berada saat ini karena harganya lebih mahal untuk mengambil barang dari kamar yang lebih dalam di reruntuhan.

Saya masih memiliki 2 optimisasi mengenai "memperjuangkan harta" yang direncanakan untuk hari-hari berikutnya.

17.04 .: Scoundrel menjadi terlalu pintar, Sprinter memutuskan untuk mendorongnya ke dalam perangkap awalnya saya ingin membunuh bot yang mencoba memohon Sprinter tetapi penguji sayangnya tidak menangani pengecualian yang terjadi pada init. Jadi perbaikan selanjutnya untuk Scoundrel cukup mudah ...


Pembunuhan bajingan sedang berlangsung ...
AKroell

2

Rencanakan ke Depan

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

Saya menggunakan bakhil perhitungan terbaik / terburuk dari jawaban Artemis Fowl , tetapi logika pilihan sepenuhnya dari desain saya sendiri dan sejak itu telah dimodifikasi untuk memasukkan beberapa faktor tambahan, seperti harta karun yang terlihat di kamar sebelumnya (untuk meminimalkan pengambilan harta, hanya untuk mundur, jatuhkan, dan mengambil sesuatu yang lain).

Bot usaha sedalam yang dianggapnya cukup aman untuk dilakukan (perhitungan ini secara efektif berhasil menyelam ke kedalaman tertentu, tetapi memiliki fleksibilitas penanganan nilai stamina awal lainnya), mengumpulkan artefak (memprioritaskan biaya tinggi dan berat rendah), kemudian mulai mundur begitu ia memutuskan bahwa ia tidak dapat membawa lagi.

Di jalan keluar ia akan mengambil harta tambahan yang dilihatnya menentukan masih bisa dibawa dengan aman ke pintu keluar. Bahkan akan menjatuhkan artefak yang sudah dipegang jika yang baru adalah kesepakatan yang lebih baik dan tidak akan menyebabkan kelelahan. Jika memiliki ruang di tas punggungnya, ia akan mengambil harta yang baru sebelum menjatuhkan yang berkualitas lebih rendah, meminimalkan pertempuran dengan bot lain.


Huh, ketika Anda menggambarkannya, itu sama dengan milik saya. Saya akan mengutak-atik nomor saya ... Catatan: __init__fungsi sudah diterapkan, Anda tidak perlu menimpanya.
Artemis mendukung Monica


2

Artyventurer

Beats the Drunkards sekitar $ 1000! Tidak dapat memikirkan nama kreatif, tetapi di sini Anda semua adalah:

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'

Kadang-kadang sebagian besar kode di sini tidak pernah melakukan apa-apa (setidaknya ketika saya mengujinya terhadap pemabuk), program hanya (mencoba) menemukan langkah terbaik, termasuk memproses untuk situasi yang mencoba untuk tidak menempatkan dirinya ke dalam. Beberapa di antaranya tidak pernah bisa berjalan, hanya ada di sana sehingga saya bisa bermain-main dengan angka-angka, yang mungkin masih bisa ditingkatkan.

Penjelasan

  • if state.inventory ... worsti = None
    Temukan item 'terburuk' dalam inventori, yaitu item yang memiliki rasio nilai terendah terhadap berat. Ini menyimpan worsti, yang berisi indeks itu, nilainya, dan beratnya, sebagai tuple, atauNone jika tidak ada item dalam persediaan.

  • if self.drop ... return 'drop', worsti[0]
    Jika saya mengatakan untuk membatalkan giliran terakhir ini (lihat di bawah), dan itu bisa, jatuhkan item 'terburuk' seperti yang dihitung di atas.

  • extra = ... * state.room
    Hitung berapa banyak stamina yang tersisa jika saya mengatakannya untuk langsung kembali sekarang.

  • if extra > CONTINUE_IN:\ return 'next'
    Jika lebih dari CONTINUE_IN, kembali 'next'.

  • if extra < 0 and worsti:\ return 'drop', worsti[0]
    Jika kurang dari itu 0, jatuhkan item terburuk.

  • if extra < state.room:\ return 'previous'
    Jika kurang dari nomor kamar (tidak dapat membawa harta lagi) kembali.

  • if state.treasures: ... bestt = None
    Cari harta karun terbaik untuk diambil, mirip dengan item terburuk dalam inventaris di atas. Menyimpannya dalam bestt.

  • if extra > 0 and bestt: ... return 'take', bestt[0], bid
    Dengan angka saat ini, ini dieksekusi setiap kali kita sudah sejauh ini dan ada harta yang tersedia. Jika aman untuk mengambil harta yang 'terbaik', maka ia melakukannya. Tawarannya adalah minimum, atau satu lebih dari itu jika ada yang hadir.

  • if bestt and worsti: ... return 'take', bestt[0], bid
    Dengan angka saat ini, blok kode ini tidak akan pernah dijalankan, karena blok kode sebelumnya memiliki kondisi yang lebih luas. Ini dijalankan jika kita sudah sejauh ini dan ada harta karun dalam inventaris dan ruang. Jika harta 'terbaik' di ruangan itu lebih berharga daripada harta 'terburuk' dalam inventori saya, dan akan lebih aman untuk menukar mereka dalam dua putaran berikutnya, itu akan melakukannya.

  • return 'previous'
    Jika tidak ada yang terjadi, kembali saja.

Pembaruan 16/04/19:

Tindakan anti-bajingan. Ini akan menjadi perang penawaran :(

Pembaruan Lebih Lanjut 16/04/19:

Dikembalikan sebelumnya, alih-alih secara acak beralih setiap elemen lainnya saat menemukan yang terbaik, misalnya. [1, 2, 3, 4, 5, 6] → [2, 1, 3, 4, 6, 5]. Seharusnya lebih sulit untuk menyalin :).

Pembaruan 17/04/19:

Dikembalikan sebelumnya, alih-alih menghapus kode sumbernya sendiri . Ia melakukan ini di __init__mana akan selalu ada sebelumnya Scoundrel.enter_ruins, dan dengan demikian akan menghentikan Scoundrel dari memuatnya. Ia mengganti kode ketikaget_action pertama kali dipanggil, sehingga akan siap untuk waktu berikutnya. TETAP, Scoundrel sekarang mati pada saat kedatangan.

Pembaruan Lebih Lanjut 17/04/19:

Dikembalikan sebelumnya, sebagai gantinya menggantikan sys.modulesentri dengan modul matematika, sehingga ketika Scoundrel mencoba memuatnya, itu memuat modul matematika sebagai gantinya. :)
Juga, saya baru menyadari bahwa stamina bergerak adalah 10 + berat / 5 , jadi coba perbaiki itu.

Pembaruan Lebih Lanjut 17/04/19:

Sekarang termasuk bawang putih dari kedua pembaruan sebelumnya.

Pembaruan 18/04/19:

Mengotak-atik angka dan perhitungan, sekarang mendapat $ 2000 - $ 3000.

Pembaruan Lebih Lanjut 18/04/19:

Bawang putih dihapus file-dihapus karena telah dilarang, menambahkan bawang putih baru yang memastikan 'draco'tidak bertanggung jawab untuk itu berjalan, jika itu hanya kembali previouspada belokan pertama. Hasil telah mengambil menyelam misterius ke $ 1200- $ 1800, yang saya cari.


tampaknya sangat efektif melawan Pemabuk, saya ingin melihat bagaimana tarifnya ketika bot lain bergabung dalam penggerebekan :)
Moogie

@ Moogie Beats the Diver sekitar $ 100 ketika 8 Pemabuk juga hadir.
Artemis mendukung Monica

2

Bajingan

import math, importlib

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

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

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

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

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

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

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

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

Scoundrel utamanya berfungsi untuk mengganggu kontestan lain. Saat ini mengganggu Sprinter, Artyventurer, dan Akuntan (daftar ini akan bertambah seiring waktu asalkan sesuai dengan kepentingan terbaik Scoundrel). Hal ini dilakukan dengan meniru bot lain dan kemudian keluar penawaran, pemotongan, atau memperebutkan peninggalan. Dengan demikian, kemungkinan entri ini tidak akan pernah mendominasi papan peringkat dan sebaliknya beroperasi sebagai kekuatan yang memanjakan. Revisi saat ini pada saat posting ini menempatkannya di posisi ke-2 dengan skor rata-rata sekitar 7.

Scoundrel menggagalkan upaya bot lain untuk memodifikasi diri mereka untuk bertahan melawan Scoundrel dengan secara langsung mengeksekusi kode pendatang lain sebagai salinan klon yang tidak dapat dibedakan. Masalah dengan impor yang menghasilkan duplikat pendaftar diselesaikan melalui pembuatan klon melalui Refleksi (perang edit yang melibatkan perincian penentuan matematis yang baik tidak diinginkan dari sudut pandang Stack Exchange, tetapi akan menghasilkan hasil yang sama). Tantangan KOTH memiliki sejarah yang memungkinkan hal ini juga.

Scoundrel menggantikan Teamsters untuk menjaga Teamsters demi menarik. Setelah pengeditan ini, Teamsters seharusnya tidak lagi dikikis oleh pengontrol.

Pembaruan 4/17/2019: langkah-langkah balasan lebih lanjut.

The Teamsters (dianggap ilegal)

Tetapi merasa bebas untuk berlari secara lokal di mana tidak ada lebih dari 8 kontestan lain!

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

Entri ini (walaupun sekarang secara eksplisit tidak valid), pada kenyataannya, dua bot, dan pengontrol akan melakukannya dengan senang hati mengikis keduanya dan menambahkannya ke daftar kontestan (karena hore Python?)

Fase 1:

  • TeamsterA menuju ke level 25 (ish) 1 dan berulang kali mengambil dan menjatuhkan harta paling ringan yang bisa dia temukan. Ini membutuhkan 1 stamina putaran hingga fase kedua.
  • TeamsterB menuju ke level 55 dan mengambil semua barang berharga yang tergeletak di sekitar dan kemudian kembali ke level 25 (ish).2 Kemudian mulailah fase 2.

1. Jika tidak ada harta yang beratnya kurang dari 3 di lantai, ia bergerak ke bawah
2. Karena ia cukup dijamin menjadi petualang terakhir yang kembali ke permukaan, yang harus ia lakukan hanyalah menemukan seseorang.

Fase 2:

  • TeamsterB mengosongkan sakunya sebelum merangkak mati karena kelelahan. Kami tahu Anda bisa melakukannya.
  • TeamsterA berpikir "itu adalah pernak-pernik yang berkilau, sobat yang baik!" dan memuat harta yang jauh lebih berharga daripada sampah lainnya di ruangan sebelum melanjutkan ke pintu keluar, kantong penuh emas.

Nama harta sebenarnya berguna untuk membantu logika tidak memuat sampah di lantai 25 dan pergi lebih awal karena tidak ada cara untuk berkomunikasi antara dua bot (dan TeamsterA akan selalu menemukan dirinya di sebuah ruangan dengan orang lain sebelumnya) TeamsterB telah kembali).

Kesimpulan logis berikutnya: Menciptakan pasukan

Secara teori ini dapat digunakan untuk menyelami kedalaman dan mendapatkan harta dari sedalam Kamar 98, namun, karena itu akan membutuhkan lebih dari 2 bot, logika terdiri dari bot itu akan menjadi semakin kompleks, dan seperti yang saya yakin bahwa ini adalah pengajuan ilegal karena melanggar aturan tidak tertulis, jadi saya tidak akan repot.

Secara efektif Amenunggu di 30, Bmenunggu di 50 ... nmenyelam ke 98, mengambil harta, bergerak ke 97, menjatuhkannya (dan kemudian mati), n-1mengambilnya dan bergerak ke 96 ... Cmenjatuhkannya (mati), Bmengambilnya naik dan bergerak ke 30, menjatuhkannya (mati),A mengambilnya dan kembali ke pintu keluar.

Saya memperkirakan bahwa ini akan membutuhkan 11 bot.

Namun, itu tidak layak dilakukan kecuali Anda dapat memulihkan sekitar 4 harta dari kedalaman itu untuk bersaing dengan entri seperti PlanAhead atau Artyventure, karena penskalaan antara biaya stamina untuk bergerak dan nilai rata-rata harta.

Contoh hasil

Skor jarang di bawah $ 4000, kadang-kadang puncak $ 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
Saya berpikir bahwa ketika hanya akan ada satu bot per orang tidak perlu aturan eksplisit seperti itu. Tetapi aturan tentang penargetan bot tertentu untuk alasan yang tidak benar-benar tidak sama dengan menolak beberapa bot bekerja sama. Jadi diperlukan aturan eksplisit dari OP.
Moogie

Ya, ini akan menjadi tidak dari saya, dawg. Ini adalah hal yang saya pikirkan dengan bot bekerja bersama.
Beefster

1
@ Beefster Itulah yang saya kira. Saya senang membuatnya. Saya akan menangani edit-untuk-mencegah-inklusi malam ini.
Draco18s

Saya akan mempertimbangkan untuk mengizinkan ini begitu ada lebih dari 11 pesaing karena efektivitasnya akan tetap. Sebagian besar karena saya tidak ingin membuat kode untuk pengiriman autoban.
Beefster

Jika Anda sudah hanya menggores blok kode pertama, yang harus saya lakukan adalah mengedit bot yang berbeda di bagian atas.
Draco18s

2

Ke belakang

Karena beroperasi secara terbalik

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'

Mengapa disebut Mundur?

Karena saya mengambil The Accountant dan mencoba membuatnya menjalankan logikanya sehingga akan menyelam lebih dalam, kemudian mengambil rampasan yang disukai di jalan keluar (mundur dari Akuntan).

Pada akhirnya ia masih mengumpulkan sebagian besar hadiahnya di jalan (mengambilnya sebelum para pencari in-collect-out tradisional melakukannya, beroperasi mundur ke semua orang), tetapi jauh lebih banyak lagi. selektif. tentang mana yang dibutuhkan, meskipun masih mengambil barang-barang dalam perjalanan kembali.

Hasil akhirnya adalah bahwa stamina dilestarikan dalam perjalanan sementara masih memprioritaskan harta bernilai tinggi, kemudian mengambil keuntungan dari perputaran yang dalam dan pengambilan mudah dalam perjalanan kembali. Mundur dikenal untuk mengumpulkan harta dari Ruang 41 (dan selama pengembangan akan masuk, maka segera pergi, Kamar 42).


2

Bounty Hunter

Metode sederhana adalah yang terbaik. Rebutlah harta berharga dan ringan sembari melangkah sedalam mungkin. Rebut harta yang kurang berharga dalam perjalanan kembali.

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'

Sepertinya Anda mendapatkan hadiahnya. Tidak hanya melakukan ini lebih baik daripada Mundur, tetapi bahkan menyebabkan Mundur ke tangki. Sudah selesai dilakukan dengan baik.
Beefster

1

Berat ringan

Bot sederhana yang masih berkinerja cukup baik.

Setelah merambah ke reruntuhan (saat ini 21 kamar) itu akan mengambil harta karun terbaik di ruangan itu yang hanya 1kg (maka nama bot) dan lebih berharga daripada harta paling tidak berharga dalam persediaan. Jika inventaris penuh, jatuhkan harta paling tidak bernilai. Jika tidak ada tindakan lain yang dipilih, pindahlah ke reruntuhan. Jika kita berada pada batas stamina kita untuk dapat keluar hidup-hidup maka pergilah ke pintu keluar

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'

Saya akan merekomendasikan menempatkan dumpingdi enter_ruinsmetode. Ini benar-benar akan mengingatnya di antara gim dan tidak akan berfungsi pada gim 2. Secara teknis tidak diizinkan, tetapi saya menambahkan aturan tadi (saya lupa sebelumnya tetapi itu adalah keinginan saya), jadi saya akan mengendur. : P
Beefster

@ Beefster Saya telah menghapus bendera negara dumping, tidak diperlukan karena bot hanya membuang satu harta sekarang. Itu digunakan untuk membuang setengah dari harta karunnya. Jadi harus kompatibel dengan aturan baru.
Moogie

1

Memorizer

Saya dapat mengirimkan bot ke KotH saya sendiri, bukan?

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'

Bot ini menyelam ke kamar 30 dan mengingat semua harta yang telah dilihatnya. Pada saat itu, ia memulai perjalanannya kembali ke pintu masuk, mencoba mengambil harta karun yang diingatnya di kamar-kamar sebelumnya.

Saya berharap ini akan lebih baik. Kemungkinan peningkatan mungkin berasal dari perencanaan yang lebih baik dan menjadi lebih dinamis di ruangan mana ia berhenti menyelam dan lebih bersedia untuk mengeksplorasi opsi cadangan.

Perbarui: sekarang ambil harta karun 1kg senilai $ 60 atau lebih dalam perjalanan masuk


Saya membayangkan semua harta yang baik itu hilang begitu saja ketika bot kembali ke sana ... Mungkin Anda bisa mencoba kombo di mana ia akan mengambil barang yang benar-benar baik di jalannya, mengingat harta yang biasa-biasa saja yang bisa diambilnya dalam perjalanan kembali?
ArBo

Mungkin terlalu jauh
Beefster

FYI, sepertinya kadang-kadang salah perhitungan jika memiliki stamina yang cukup untuk kembali: [Turn 072] Ryu Ridley (Memorizer) collapsed in the doorway to room #1 and died of exhaustion
Larkeith

1

Ponderer

Saya pikir itu sangat mirip dengan Memorizer karena menggunakan pengetahuan tentang kamar yang dikunjungi untuk memilih kamar dan harta yang dikumpulkan dari dalam perjalanan kembali ke pintu keluar, namun telah diperoleh secara independen.

Bot ini berlari hingga sebuah ruang dalam acak mengambil catatan harta yang ditemukan di sepanjang jalan. Begitu sampai di ruang target maka akan direnungkan pilihan harta yang ideal untuk dibawa kembali ke pintu keluar. Setiap belokan itu akan direnungkan lagi untuk menentukan pilihan harta yang paling mungkin diambil.

Saat ini ada algoritma sederhana (kekuatan terbalik dari nomor kamar) yang menghasilkan asumsi jumlah harta yang diambil (atau akan diambil ketika dikunjungi oleh bot ini) untuk setiap kamar dan karenanya harta ini diabaikan ketika merenungkan harta / kamar yang mana untuk mengambil dari. Saya punya ide untuk algoritma lebih maju lainnya untuk memodelkan harta yang tersisa. Tetapi saya harus melihat apakah manfaatnya sepadan.

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

Penimbun

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

Sang Penimbun tinggal di sebuah ruangan sampai ia mengambil semua harta di dalam ruangan (atau menghitung bahwa ia tidak memiliki stamina yang cukup untuk terus mengambil / melanjutkan). Ketika semua harta hilang, jika bot bisa bergerak dengan aman itu akan, dan melanjutkan proses mengambil semua harta.


Ini mati setiap pertandingan dengan mengisi ranselnya terlalu banyak.
Beefster

seperti saya di Minecraft (͡ ° ͜ʖ ͡ °) Bot ini akan menjarah, masuk lebih dalam, dan kemudian menemukan harta berharga. Sehingga akan menjatuhkan apa yang menurutnya adalah hasil curian yang baik tadi. Itu sebabnya Backwards's, Sprinter' s dan Memorizer's kerja strategi; karena mereka tahu apa nilai relatif dari setiap harta yang mereka lihat.
V. Courtois

0

Penyelam

(Tidak dapat menguji saat ini, jadi beri tahu saya jika ini rusak.)

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'

Harta terbaik ada di reruntuhan yang lebih dalam, jadi selami kedalamannya, lalu raih apa yang kita bisa saat keluar.


Saya tidak terlalu berpengalaman dengan python, tetapi di mana divingdidefinisikan?
Perwujudan Ketidaktahuan

1
@EmbodimentofIgnorance Di enter_ruins (), yang dipanggil sebelum permainan dijalankan dan tindakan dilakukan.

Jacob the Orphan (Diver) was sliced in half by a swinging blade trap.Tidak yakin apa yang Anda lakukan salah, tetapi itu berarti 'pengembalian tidak valid' AFAIK.
Artemis mendukung Monica

@ ArtemisFowl dia menawar terlalu rendah untuk harta karun itu. Harganya berat harta untuk mengambilnya.
Beefster

@Beefster Oh, ya.
Artemis mendukung Monica
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.