Bagaimana memilih peralatan untuk game Persia dengan Python

Kami belajar menemukan yang terbaik untuk perampok kami dengan bantuan pemrograman. Kami juga mencari tahu apakah program ini mendorong kami "oleh hidung".

gambar

Tujuan: untuk mempelajari tahap-demi-tahap mensimulasikan bagian yang diperlukan dari mekanisme permainan in vitro, untuk mendapatkan data yang diperlukan dan menarik kesimpulan dari mereka.

Yang Anda butuhkan: Python 3, lingkungan untuk bekerja dengan kode (saya punya PyCharm).

Dalam permainan, banyak orang ingin memeras karakter mereka secara maksimal, dan untuk ini Anda harus memilih kombinasi peralatan yang paling optimal, yang sering kali banyak. Mari kita coba menulis algoritma kita sendiri untuk menguji berbagai kombinasi peralatan dan mengumpulkan data.

Awalnya, saya terinspirasi oleh game "World of Warcraft: Classic" (saya mengambil ikon dari sana ), tetapi dalam prosesnya saya membuat beberapa penyederhanaan. Tautan ke seluruh proyek di akhir artikel.

LANGKAH 1 - mengevaluasi area pencarian


Misalkan kita memiliki karakter di kelas Rogue. Penting untuk mengambil peralatan untuknya di mana ia akan memberikan kerusakan maksimum pada musuh. Kami tertarik pada hal-hal untuk slot "senjata di tangan kanan" (4 pcs.), "Senjata di tangan kiri" (4 pcs.), "Sarung tangan" (2 pcs.), "Kepala" (3 pcs.), "Dada" (3 pcs.), "Kaki" (3 pcs.), "Kaki" (2 pcs.). Kami akan memakai berbagai kombinasi mereka pada karakter dan mensimulasikan pertempuran. Dan jika Anda menerapkan ide pencarian lengkap (yang akan kami mulai), untuk mengevaluasi semua kombinasi Anda harus menghabiskan setidaknya 4 * 4 * 2 * 3 * 3 * 3 * 3 * 2 = 1728 perkelahian.

Untuk penilaian kombinasi terbaik yang lebih akurat, Anda harus melakukan pertempuran tambahan.

Jadi, sudah pada tahap ini kita dapat mempresentasikan skema proyek sebagai berikut:

gambar

LANGKAH 2 - menganalisis mekanika game


Mari kita mulai dengan karakter. Dia memiliki karakteristik yang mempengaruhi kerusakan yang dilakukan dan pada satu sama lain:

  1. kekuatan serangan - itu dikonversi langsung ke kerusakan yang disebabkan oleh pukulan normal (1 ke 1). Itu dihitung dengan rumus: titik kekuatan serangan + titik kekuatan + titik kelincahan
  2. Kekuatan - +1 untuk menyerang kekuatan dan semua (apa yang harus dilakukan, ini adalah desain game)
  3. Keluwesan - +1 untuk menyerang kekuatan, dan juga setiap 20 unit kelincahan menambahkan 1% peluang kritis
  4. Kreta. kesempatan - peluang menyebabkan kerusakan ganda jika serangan tidak meluncur dan meleset
  5. akurasi - peningkatan peluang untuk mengenai lawan
  6. penguasaan - setiap unit penguasaan mengurangi kemungkinan serangan geser sebesar 4% (yang awalnya sama dengan 40%, yang berarti bahwa 10 unit penguasaan sepenuhnya mengecualikan kemungkinan serangan geser)

Diagram di bawah ini menunjukkan nilai-nilai dasar untuk perampok kami dan bagaimana cara menempatkan item peralatan mengubahnya:

gambar

Jadi, sekarang saatnya untuk mulai menulis kode. Kami menggambarkan apa yang sudah kami ketahui di kelas Rogue. Metode set_stats_without_equip akan mengembalikan keadaan karakter tanpa peralatan, yang berguna saat mengubah koleksi. Metode calcul_critical_percent dan calcul_glancing_percent di masa mendatang akan dipanggil hanya jika perlu, memperbarui nilai karakteristik tertentu.

baris pertama kelas
class Rogue:
    """    ."""

    def __init__(self):

        #    ( -     ):
        self.basic_stat_agility = 50
        self.basic_stat_power = 40
        self.basic_stat_hit = 80
        self.basic_stat_crit = 20
        self.basic_stat_mastery = 0

        #     :
        self.set_stats_without_equip()


    #       :
    def set_stats_without_equip(self):
        self.stat_agility = self.basic_stat_agility
        self.stat_power = self.basic_stat_power
        self.stat_attackpower = self.stat_agility + self.stat_power
        self.stat_hit = self.basic_stat_hit
        self.direct_crit_bonus = 0
        self.calculate_critical_percent()
        self.stat_mastery = self.basic_stat_mastery
        self.calculate_glancing_percent()


    #      :
    def calculate_critical_percent(self):
        self.stat_crit = self.basic_stat_crit + self.direct_crit_bonus + self.stat_agility // 20


    #      :
    def calculate_glancing_percent(self):
        self.stat_glancing_percent = 40 - self.stat_mastery * 4


Sekarang Anda harus berurusan dengan peralatan. Untuk menyortir semua hal dengan mudah, membuat kombinasinya, saya memutuskan untuk membuat kamus terpisah untuk setiap jenis peralatan: RIGHT_HANDS, LEFT_HANDS, GLOVES, HEADS, DADA, CELANA, CELANA, CELANA, BOOTS. Tupel berikut disimpan sebagai nilai dalam kamus:

gambar

Buat file terpisah untuk kamus dengan peralatan. Saya punya beberapa file dengan set berbeda.

alat uji abstrak
#    ,     :
# 0 - , 1 - , 2 - , 3 - , 4 - , 5 - , 6 - 

EQUIPMENT_COLLECTION = 'custom'

RIGHT_HANDS = dict()
RIGHT_HANDS[1] = ('  ', 50, 3, 0, 0, 0, 0)
RIGHT_HANDS[2] = (' ', 40, 22, 0, 0, 0, 0)
RIGHT_HANDS[3] = (' ', 40, 0, 0, 3, 0, 0)
RIGHT_HANDS[4] = (' ', 40, 0, 0, 0, 0, 5)

LEFT_HANDS = dict()
LEFT_HANDS[1] = ('  ', 35, 3, 0, 0, 0, 0)
LEFT_HANDS[2] = (' ', 40, 22, 0, 0, 0, 0)
LEFT_HANDS[3] = (' ', 40, 0, 0, 3, 0, 0)
LEFT_HANDS[4] = (' ', 40, 0, 0, 0, 0, 5)

GLOVES = dict()
GLOVES[1] = (' ', 0, 12, 0, 2, 0, 0)
GLOVES[2] = (' ', 2, 2, 2, 1, 1, 0)

HEADS = dict()
HEADS[1] = (' ', 0, 22, 0, 0, 0, 0)
HEADS[2] = (' ', 0, 0, 0, 0, 2, 0)
HEADS[3] = (' ', 0, 0, 0, 2, 0, 0)

CHESTS = dict()
CHESTS[1] = (' ', 0, 30, 0, 0, 0, 0)
CHESTS[2] = (' ', 0, 0, 0, 0, 3, 0)
CHESTS[3] = (' ', 0, 0, 0, 3, 0, 0)

PANTS = dict()
PANTS[1] = (' ', 0, 24, 0, 0, 0, 0)
PANTS[2] = (' ', 0, 0, 0, 0, 2, 0)
PANTS[3] = (' ', 0, 0, 0, 2, 0, 0)

BOOTS = dict()
BOOTS[1] = ('  ', 14, 0, 5, 0, 1, 0)
BOOTS[2] = (' ', 0, 18, 0, 1, 0, 0)


Pakaian World of Warcraft
#    ,     :
# 0 - , 1 - , 2 - , 3 - , 4 - , 5 - , 6 - 

EQUIPMENT_COLLECTION = "wow_classic_preraid"

RIGHT_HANDS = dict()
RIGHT_HANDS[1] = ('  \'', 81, 0, 4, 0, 1, 0)
RIGHT_HANDS[2] = (' ', 49, 0, 4, 0, 1, 0)
RIGHT_HANDS[3] = (' ', 57, 9, 9, 0, 0, 0)

LEFT_HANDS = dict()
LEFT_HANDS[1] = ('  \'', 52, 0, 0, 0, 0, 0)
LEFT_HANDS[2] = (' ', 49, 0, 4, 0, 1, 0)
LEFT_HANDS[3] = (' ', 57, 9, 9, 0, 0, 0)

GLOVES = dict()
GLOVES[1] = (' ', 28, 0, 0, 0, 1, 0)
GLOVES[2] = ('  ', 40, 0, 0, 0, 0, 0)

HEADS = dict()
HEADS[1] = (' ', 0, 0, 0, 2, 1, 0)
HEADS[2] = (' ', 0, 0, 13, 0, 2, 0)
HEADS[3] = (' ', 32, 0, 8, 0, 0, 0)
HEADS[4] = (' ', 0, 19, 12, 0, 0, 0)

CHESTS = dict()
CHESTS[1] = (' ', 60, 8, 8, 0, 0, 0)
CHESTS[2] = ('  ', 50, 5, 0, 0, 0, 0)
CHESTS[3] = (' ', 0, 11, 18, 0, 0, 0)

PANTS = dict()
PANTS[1] = (' ', 46, 0, 0, 0, 1, 0)
PANTS[2] = ('  ', 0, 5, 0, 1, 1, 0)

BOOTS = dict()
BOOTS[1] = (' ', 0, 21, 4, 0, 0, 0)
BOOTS[2] = ('  ', 40, 0, 0, 0, 0, 0)
BOOTS[3] = (' ', 0, 23, 0, 0, 0, 0)


tambahkan string equipype ke konstruktor dari kelas Rogue
    ...
    #    ,    id  :
    # 0 -  , 1 -  , 2 - , 3 - , 4 - , 5 - , 6 - 
    self.equipment_slots = [0] * 7

    #    ,      :
    self.equipment_names = [''] * 7


Kami juga menambahkan ke kelas kami wear_item (perhitungan karakteristik saat memakai sesuatu) dan metode unwear_all (hapus semua hal).

metode kelas yang bertanggung jawab untuk bekerja dengan peralatan
    ...
    #   "  ":
    def unwear_all(self):
        #  id      :
        for i in range(0, len(self.equipment_slots) ):
            self.equipment_slots[i] = 0
            self.equipment_names[i] = ''

        self.set_stats_without_equip()


    #    :
    def wear_item(self, slot, item_id, items_list):

        #      ,        ,   :
        if self.equipment_slots[slot] == 0:
            self.equipment_slots[slot] = item_id
            self.equipment_names[slot] = items_list[item_id][0]
            self.stat_agility += items_list[item_id][2]
            self.stat_power += items_list[item_id][3]
            #  ,            :
            self.stat_attackpower += items_list[item_id][1] + items_list[item_id][2] + items_list[item_id][3]
            self.stat_hit += items_list[item_id][4]
            self.direct_crit_bonus += items_list[item_id][5]
            self.stat_mastery += items_list[item_id][6]

            #         . ,   . :
            if items_list[item_id][2] != 0 or items_list[item_id][5] != 0:
                self.calculate_critical_percent()

            #    ,    :
            if items_list[item_id][6] != 0:
                self.calculate_glancing_percent()


Juga, fakta menggabungkan beberapa hal memberikan bonus tambahan (dalam "World of Warcraft" ini dikenal sebagai "bonus set"). Dalam set abstrak saya, bonus seperti itu diberikan dari mengenakan Penjaga Hutan yang kidal dan Penjaga Hutan yang kidal pada saat yang sama. Tambahkan ini ke kode metode wear_item :

setel bonus dalam metode wear_item
    ...
    #      "custom":
            if EQUIPMENT_COLLECTION == 'custom':
                #      "  " (id 1   " "),     "  " (id 1   " "),   2  . :
                if slot == 1:
                    if self.equipment_slots[1] == 1 and self.equipment_slots[0] == 1:
                        self.direct_crit_bonus += 2
                        self.calculate_critical_percent()
                        print('  ...')


Sekarang perampok kita perlu diajari cara bertarung. Kami akan mempertimbangkan pertarungan serangkaian 1000 serangan pada lawan yang berdiri dengan membelakangi kami dan sibuk dengan hal lain (situasi khas untuk World of Warcraft). Setiap serangan, terlepas dari yang sebelumnya, mungkin:

  • normal - kerusakan standar, dalam model kami setara dengan "kekuatan serangan" karakter
  • bergerak - kerusakan 70% dari normal
  • kritis - kerusakan ganda dari normal
  • miss - 0 damage

Ini akan ditentukan oleh serangkaian pemeriksaan sesuai dengan skema ini:

gambar

Dan untuk perampok dengan nilai-nilai dasar, skema ini berbentuk: Mari memprogram

gambar

mekanik ini dengan menambahkan metode do_attack ke kode kelas kita. Ini akan mengembalikan tupel dua angka: (hasil serangan, kerusakan dilakukan).

kode serangan
    ...
    #    :
    def do_attack(self):
        #   :
        event_hit = randint(1, 100)

        #  :
        if event_hit > self.stat_hit:
            return 0, 0

        #  :
        else:
            #   :
            event_glancing = randint(1, 100)

            #    ,    ,
            #      10  "",  stat_glancing_percent   0,
            #      
            if event_glancing <= self.stat_glancing_percent:
                damage = floor(self.stat_attackpower * 0.7)
                return 1, damage

            #    :
            else:
                #   :
                event_crit = randint(1, 100)

                #    :
                if event_crit > self.stat_crit:
                    damage = self.stat_attackpower
                    return 2, damage

                #   :
                else:
                    damage = self.stat_attackpower * 2
                    return 3, damage


Kami akan mencapai tampilan nyaman dari keadaan perampok saat ini, sehingga kapan saja Anda dapat memeriksa apa yang terjadi padanya:

mendefinisikan kembali metode sihir __str__
    ...
    #  " "     :
    def __str__(self):

        #      :
        using_equipment_names = ''
        for i in range(0, len(self.equipment_names) - 1 ):
            using_equipment_names += self.equipment_names[i] + '", "'
        using_equipment_names = '"' + using_equipment_names + self.equipment_names[-1] + '"'

        #  :
        description = ' 60 \n'
        description += using_equipment_names + '\n'
        description += ' : ' + str(self.stat_attackpower) + ' .\n'
        description += ': ' + str(self.stat_agility) + ' .\n'
        description += ': ' + str(self.stat_power) + ' .\n'
        description += ': ' + str(self.stat_hit) + '%\n'
        description += '. : ' + str(self.stat_crit) + '%\n'
        description += ': ' + str(self.stat_mastery) + ' .\n'
        description += ' . .: ' + str(self.stat_glancing_percent) + '%\n'
        return description


LANGKAH 3 - Bersiap untuk Meluncurkan


Sekarang saatnya menulis kode yang akan memberikan pertarungan untuk semua set peralatan yang mungkin. Untuk melakukan ini, saya secara berurutan memanggil fungsi sesuai dengan skema ini:

gambar

  1. run_session - loop bersarang diimplementasikan di sini, memilah-milah semua kamus yang diperlukan dengan hal-hal dan memanggil fungsi berikut untuk setiap kombinasi; pada akhirnya teks laporan akan dihasilkan dan disimpan dalam log sesi
  2. test_combination - semua item yang sebelumnya dipakai direset dan metode wear_item dipanggil berulang-ulang, mendandani karakter dalam "pakaian" baru, setelah itu fungsi selanjutnya disebut
  3. simulate_fight - metode do_attack yang sama disebut 1000 kali, data yang diterima disimpan, jika perlu, log rinci disimpan untuk setiap pertempuran

fungsi run_session, test_combination, simulate_fight
#     :
def run_session(SESSION_LOG):

    #  :
    fight_number = 1

    #    :
    all_fight_data = ''

    #      :
    for new_righthand_id in RIGHT_HANDS:
        #      :
        for new_lefthand_id in LEFT_HANDS:
            #   :
            for new_gloves_id in GLOVES:
                #   :
                for new_head_id in HEADS:
                    #   :
                    for new_chest_id in CHESTS:
                        #   :
                        for new_pants_id in PANTS:
                            #   :
                            for new_boots_id in BOOTS:

                                new_fight_data = test_combination(fight_number,
                                                                  new_righthand_id,
                                                                  new_lefthand_id,
                                                                  new_gloves_id,
                                                                  new_head_id,
                                                                  new_chest_id,
                                                                  new_pants_id,
                                                                  new_boots_id
                                                                  )

                                all_fight_data += new_fight_data
                                fight_number += 1

    #       :
    save_data_to_file(SESSION_LOG, all_fight_data)

#       :
def test_combination(fight_number, righthand_id, lefthand_id, gloves_id, head_id, chest_id, pants_id, boots_id):

    #   :
    my_rogue.unwear_all()

    #     :
    my_rogue.wear_item(0, righthand_id, RIGHT_HANDS)

    #     :
    my_rogue.wear_item(1, lefthand_id, LEFT_HANDS)

    #  :
    my_rogue.wear_item(2, gloves_id, GLOVES)

    #  :
    my_rogue.wear_item(3, head_id, HEADS)

    #  :
    my_rogue.wear_item(4, chest_id, CHESTS)

    #  :
    my_rogue.wear_item(5, pants_id, PANTS)

    #  :
    my_rogue.wear_item(6, boots_id, BOOTS)


    #    "" :
    equipment_profile = str(righthand_id) + ',' + str(lefthand_id) + ',' + str(gloves_id) + \
                            ',' + str(head_id) + ',' + str(chest_id) + ',' + str(pants_id) + \
                            ',' + str(boots_id)

    print(my_rogue)
    print('equipment_profile =', equipment_profile)

    #        :
    return simulate_fight(equipment_profile, fight_number)


#  ,    attacks_total   :
def simulate_fight(equipment_profile, fight_number):
    global LOG_EVERY_FIGHT

    #   :
    sum_of_attack_types = [0, 0, 0, 0]
    sum_of_damage = 0

    #  ,     :
    if LOG_EVERY_FIGHT:
        fight_log = ''
        verdicts = {
            0: '.',
            1: '.',
            2: '.',
            3: '.'
        }

    attacks = 0
    global ATTACKS_IN_FIGHT

    #  ,      :
    while attacks < ATTACKS_IN_FIGHT:
        #  - :
        damage_info = my_rogue.do_attack()

        #   :
        sum_of_damage += damage_info[1]

        #   :
        sum_of_attack_types[ damage_info[0] ] += 1

        attacks += 1

        #  ,   :
        if LOG_EVERY_FIGHT:
            fight_log += verdicts[ damage_info[0] ] + ' ' + str(damage_info[1]) + ' ' + str(sum_of_damage) + '\n'

    #  ,  :
    if LOG_EVERY_FIGHT:
        #  :
        filename = 'fight_logs/log ' + str(fight_number) + '.txt'
        save_data_to_file(filename, fight_log)

    #       :
    attacks_statistic = ','.join(map(str, sum_of_attack_types))
    fight_data = '#' + str(fight_number) + '/' + equipment_profile + '/' + str(sum_of_damage) + ',' + attacks_statistic + '\n'

    return fight_data



Untuk menyimpan log, saya menggunakan dua fungsi sederhana:

fungsi save_data, add_data
#     :
def save_data_to_file(filename, data):
    with open(filename, 'w', encoding='utf8') as f:
        print(data, file=f)


#     :
def append_data_to_file(filename, data):
    with open(filename, 'a+', encoding='utf8') as f:
        print(data, file=f)


Jadi, sekarang tinggal menulis beberapa baris untuk memulai sesi dan menyimpan hasilnya. Kami juga mengimpor modul Python standar yang diperlukan. Di sinilah Anda dapat menentukan set peralatan yang akan diuji. Untuk penggemar World of Warcraft, saya mengambil peralatan dari sana, tetapi ingat bahwa proyek ini hanya merupakan perkiraan rekonstruksi mekanik dari sana.

kode program
#     :
from random import randint

#     :
from math import floor

#    :
from datetime import datetime
from time import time

#    :
from operations_with_files import *

#      :
from equipment_custom import *
#from equipment_wow_classic import *
#from equipment_obvious_strong import *
#from equipment_obvious_weak import *


# :
if __name__ == "__main__":

    #     :
    ATTACKS_IN_FIGHT = 1000

    #     :
    LOG_EVERY_FIGHT = False

    #     :
    SESSION_LOG = 'session_logs/for ' + EQUIPMENT_COLLECTION + ' results ' + datetime.strftime(datetime.now(), '%Y-%m-%d_%H-%M-%S') + '.txt'
    print('SESSION_LOG =', SESSION_LOG)

    #  :
    my_rogue = Rogue()

    #  :
    time_begin = time()

    #   :
    run_session(SESSION_LOG)

    #   :
    time_session = time() - time_begin
    duration_info = ' : ' + str( round(time_session, 2) ) + ' .'
    print('\n' + duration_info)
    append_data_to_file(SESSION_LOG, duration_info + '\n')

    #  ,   5    :
    top_sets_info = show_best_sets(SESSION_LOG, 5)

    #          :
    append_data_to_file(SESSION_LOG, top_sets_info)

else:
    print('__name__ is not "__main__".')


Sesi 1728 pertempuran memakan waktu 5 detik di laptop saya. Jika Anda mengatur LOG_EVERY_FIGHT = Benar, maka file dengan data untuk setiap pertarungan akan muncul di folder fight_logs, tetapi sesi sudah akan memakan waktu 9 detik. Bagaimanapun, log sesi akan muncul di folder session_logs:

10 baris pertama dari log
#1/1,1,1,1,1,1,1/256932,170,324,346,160
#2/1,1,1,1,1,1,2/241339,186,350,331,133
#3/1,1,1,1,1,2,1/221632,191,325,355,129
#4/1,1,1,1,1,2,2/225359,183,320,361,136
#5/1,1,1,1,1,3,1/243872,122,344,384,150
#6/1,1,1,1,1,3,2/243398,114,348,394,144
#7/1,1,1,1,2,1,1/225342,170,336,349,145
#8/1,1,1,1,2,1,2/226414,173,346,322,159
#9/1,1,1,1,2,2,1/207862,172,322,348,158
#10/1,1,1,1,2,2,2/203492,186,335,319,160


Seperti yang Anda lihat, hanya mengadakan sesi saja tidak cukup, Anda perlu mengekstrak informasi dari ratusan baris tentang kombinasi hal-hal yang mengarah pada hasil terbaik. Untuk melakukan ini, kita akan menulis dua fungsi lagi. Gagasan umum adalah membuka log yang diterima, membuat daftar jumlah kerusakan untuk setiap pertempuran, mengurutkannya, dan, misalnya, tuliskan nama-nama hal yang digunakan untuk 5 situasi terbaik.

fungsi untuk menentukan gigi atas
#       :
def show_best_sets(SESSION_LOG, number_of_sets):

    #      :
    list_log = list()

    #   ,      list_log ,
    #          :
    with open(SESSION_LOG, 'r', encoding='utf8') as f:
        lines = f.readlines()
        for line in lines:
            try:
                list_line = line.split('/')
                list_fight = list_line[2].split(',')
                list_log.append( ( int(list_fight[0]), list_line[1].split(',') ) )
            except IndexError:
                break

    #  ,      :
    list_log.sort(reverse=True)

    #   ,  number_of_sets     :
    top_sets_info = ''
    for i in range(0, number_of_sets):
        current_case = list_log[i]

        #           :
        clear_report = ''
        equipment_names = ''
        equip_group = 1

        for equip_id in current_case[1]:
            equipment_names += '\n' + get_equip_name(equip_id, equip_group)
            equip_group += 1

        line_for_clear_report = '\n#' + str(i+1) + ' - ' + str(current_case[0]) + '   :' + equipment_names
        clear_report += line_for_clear_report

        print('\n', clear_report)
        top_sets_info += clear_report + '\r'

    return top_sets_info


#     id:
def get_equip_name(equip_id, equip_group):
    equip_id = int(equip_id)

    if equip_group == 1:
        return RIGHT_HANDS[equip_id][0]
    if equip_group == 2:
        return LEFT_HANDS[equip_id][0]
    if equip_group == 3:
        return GLOVES[equip_id][0]
    if equip_group == 4:
        return HEADS[equip_id][0]
    if equip_group == 5:
        return CHESTS[equip_id][0]
    if equip_group == 6:
        return PANTS[equip_id][0]
    if equip_group == 7:
        return BOOTS[equip_id][0]


Sekarang di akhir baris log dengan 5 pilihan muncul, menunjukkan hasil terbaik:

baris log akhirnya dapat dibaca
 : 4.89 .

#1 - 293959   :
 
 
 
 
 
 
  

#2 - 293102   :
 
 
 
 
 
 
 

#3 - 290573   :
 
 
 
 
 
 
  

#4 - 287592   :
 
 
 
 
 
 
 

#5 - 284929   :
 
 
 
 
 
 
  


LANGKAH 4 - mengevaluasi keberlanjutan hasil


Penting untuk diingat bahwa ada elemen keacakan dalam proyek ini: ketika menentukan jenis stroke menggunakan fungsi randint . Berulang kali melakukan tes, saya perhatikan bahwa ketika mengulangi sesi dengan data input yang sama, 5 pilihan teratas dapat bervariasi. Ini sangat tidak senang, dan butuh untuk menyelesaikan masalah.

Pertama-tama saya membuat test kit dari peralatan β€œclear_strong”, di mana bahkan tanpa tes jelaslah koleksi mana yang terbaik di sini:

perhatikan set jelas_strong
EQUIPMENT_COLLECTION = 'obvious_strong'

RIGHT_HANDS = dict()
RIGHT_HANDS[1] = (' ', 5000, 0, 0, 0, 0, 0)
RIGHT_HANDS[2] = (' ', 800, 0, 0, 0, 0, 0)
RIGHT_HANDS[3] = (' ', 20, 0, 0, 0, 0, 0)

LEFT_HANDS = dict()
LEFT_HANDS[1] = (' ', 4000, 0, 0, 0, 0, 0)
LEFT_HANDS[2] = (' ', 10, 0, 0, 0, 0, 0)

GLOVES = dict()
GLOVES[1] = (' ', 1, 0, 0, 0, 0, 0)

HEADS = dict()
HEADS[1] = (' ', 1, 0, 0, 0, 0, 0)

CHESTS = dict()
CHESTS[1] = (' ', 1, 0, 0, 0, 0, 0)

PANTS = dict()
PANTS[1] = (' ', 1, 0, 0, 0, 0, 0)

BOOTS = dict()
BOOTS[1] = (' ', 1, 0, 0, 0, 0, 0)


Dengan set ini akan ada 6 pertarungan (3 pedang * 2 belati * 1 * 1 * 1 * 1 * 1). Di 5 besar, pertempuran di mana pedang terburuk dan belati terburuk diambil tidak termasuk. Yah, tentu saja, tempat pertama harus menjadi pilihan dengan dua bilah terkuat. Jika Anda memikirkannya, maka untuk setiap koleksi jelas tempat itu akan jatuh ke mana. Melakukan tes, harapan terpenuhi.

Berikut adalah visualisasi hasil dari salah satu tes dari set ini:

gambar

Selanjutnya, saya mengurangi ke minimum kesenjangan dalam ukuran bonus yang diberikan oleh pisau ini dari 5000, 800, 20 dan 4000, 10 ke5, 4, 3 dan 2, 1, masing-masing (dalam proyek set ini terletak di file "equipment_obvious_weak.py"). Dan di sini, tiba-tiba, kombinasi pedang terkuat dan belati terburuk keluar di atas. Selain itu, dalam salah satu tes, dua senjata terbaik tiba-tiba datang di tempat terakhir:

gambar

Bagaimana memahami ini? Harapan dalam pengaturan yang jelas dari koleksi tetap tidak berubah, tetapi tingkat perbedaan di antara mereka berkurang secara signifikan . Dan sekarang, kecelakaan selama pertempuran (rasio kesalahan dan hit, hit kritis dan non-kritis, dll.) Telah menjadi sangat penting.

Mari kita periksa seberapa sering "duet pisau atas" tidak jatuh di tempat pertama. Saya membuat 100 peluncuran (untuk ini, saya membungkus "jalur peluncuran program" dalam siklus 100 iterasi dan mulai menyimpan log khusus untuk seluruh "sesi super" ini). Berikut adalah visualisasi dari hasilnya:

gambar

Jadi, hasil dalam program kami tidak selalu stabil (34% dari hasil "benar" versus 66% dari "salah").

Stabilitas hasil berbanding lurus dengan perbedaan dalam bonus dari barang yang diuji.

Mengingat bahwa perbedaan dalam jumlah bonus dari hal-hal baik yang masuk akal untuk diuji memiliki persepsi yang lemah (seperti dalam "World of Warcraft"), hasil tes tersebut akan relatif tidak stabil (tidak stabil, tidak stabil, dll.).

LANGKAH 5 - Meningkatkan Ketahanan


Kami mencoba berpikir logis.

Kami menguraikan kriteria keberhasilan: "duet pisau atas" harus berada di tempat pertama dalam 99% kasus.

Situasi saat ini: 34% dari kasus tersebut.

Jika Anda tidak mengubah pendekatan yang diterima pada prinsipnya (transisi dari simulasi pertarungan untuk semua pilihan ke perhitungan karakteristik yang sederhana, misalnya), masih harus mengubah beberapa parameter kuantitatif dari model kami.

Contohnya:

  • untuk melakukan bukan satu pertempuran untuk setiap koleksi, tetapi beberapa, kemudian catat rata-rata aritmatika dalam log, buang yang terbaik dan terburuk, dll.
  • melakukan beberapa sesi tes secara umum, dan kemudian juga mengambil "rata-rata"
  • untuk memperpanjang pertempuran itu sendiri dari 1.000 hit ke nilai tertentu, yang akan cukup untuk hasil yang adil untuk koleksi yang sangat dekat dalam hal total bonus

Pertama-tama, menurut saya ide yang berhasil dengan perpanjangan pertempuran, karena di tempat inilah segala sesuatu terjadi yang menyebabkan perpanjangan artikel ini.

Saya akan menguji hipotesis bahwa memperpanjang pertempuran dari 1.000 menjadi 10.000 pukulan akan meningkatkan stabilitas hasil (untuk ini Anda perlu mengatur ATTACKS_IN_FIGHT konstan menjadi 10.000). Dan begitulah:

gambar

Kemudian dia memutuskan untuk meningkatkan dari 10.000 menjadi 100.000 tembakan, dan ini menghasilkan kesuksesan seratus persen. Setelah itu, menggunakan metode pencarian biner, ia mulai memilih jumlah hit yang akan memberikan 99% keberhasilan untuk menyingkirkan perhitungan berlebihan. Berhenti di 46.875.

gambar

Jika perkiraan saya keandalan 99% dari sistem dengan panjang pertempuran seperti itu benar, maka dua tes berturut-turut mengurangi kemungkinan kesalahan menjadi 0,01 * 0,01 = 0,0001 .

Dan sekarang, jika Anda menjalankan tes dengan pertempuran 46.875 pukulan untuk satu set peralatan untuk 1.728 pertempuran, itu akan memakan waktu 233 detik dan menginspirasi keyakinan bahwa "Pedang sang Master" memerintah:

hasil 1728 pertempuran untuk 46.875 serangan
 : 233.89 .

#1 - 13643508   :
 
 
 
 
 
 
 

#2 - 13581310   :
 
 
 
 
 
 
  

#3 - 13494544   :
 
 
 
 
 
 
 

#4 - 13473820   :
 
 
 
 
 
 
  

#5 - 13450956   :
 
 
 
 
 
 
 


PS Dan ini mudah untuk dijelaskan: dua "Pedang Tuan" memungkinkan Anda untuk mendapatkan 10 unit keterampilan, yang, menurut mekanisme yang melekat, menghilangkan kemungkinan goresan geser, dan ini menambahkan sekitar 40% goresan ketika kerusakan X atau 2X diterapkan alih-alih 0,7X.

Hasil tes serupa untuk penggemar WoW:

hasil 1296 pertempuran dari 46.875 hit (wow classic preraid)
 : 174.58 .


#1 - 19950930   :
  '
  '
  
 
 
 
  

#2 - 19830324   :
  '
  '
 
 
 
 
  

#3 - 19681971   :
  '
  '
  
 
 
 
  

#4 - 19614600   :
  '
  '
 
 
 
 
  

#5 - 19474463   :
  '
  '
  
 
 
 
 


Ringkasan


  1. β€” . , , 4 * 4 * 3 * 3 * 3 * 3 * 2 = 2592, .. 33% . .
  2. : , , , .
  3. , : , , , .

Saya memposting semua kode proyek di github .

Komunitas yang terhormat, saya akan dengan senang hati menerima umpan balik tentang topik ini.

UPD dari 04/08/2020:
Terima kasih atas komentarnyaDeerenaros, knotri dan GriboksSaya menyadari bahwa alih-alih mensimulasikan ribuan pertempuran, Anda dapat menghitung ekspektasi matematis untuk satu pukulan dan atas dasar ini untuk menentukan peringkat peralatan. Saya membuang semua yang terkait dengan perkelahian dari kode, alih-alih fungsi simulate_fight yang saya lakukan menghitung_pilihan . Pada output, saya mendapatkan hasil yang sama. Menambahkan kode yang dihasilkan ke repositori .

All Articles