如何在Python中为波斯游戏选择设备

通过编程,我们学会为强盗找到最佳选择。我们还要弄清楚该程序是否正在“逼人”。

图片

目的:学习在体外逐步模拟游戏机制的必要部分,以获取必要的数据并从中得出结论。

您需要什么: Python 3,使用代码的环境(我有PyCharm)。

在游戏中,许多人想从角色中获得最大收益,为此,您需要选择最优化的设备组合,通常这很多。让我们尝试编写自己的算法,以测试设备的各种组合并收集数据。

最初,我受到“魔兽世界:经典”游戏的启发(我从那里获得了图标),但在此过程中我做了一些简化。在文章末尾链接到整个项目。

第1步-评估搜索区域


假设我们在Rogue类中有一个角色。有必要为他拾起装备,以使他对敌人造成最大的伤害。我们对“右手武器”(4个),“左手武器”(4个),“手套”(2个),“头部”(3个),“胸部”这些插槽感兴趣(3个),“腿”(3个),“脚”(2个)。我们将在角色上使用它们的各种组合并模拟战斗。并且如果您应用穷举搜索(我们将开始使用穷举搜索)的想法,则要评估所有组合,您将不得不花费至少4 * 4 * 2 * 3 * 3 * 3 * 2 = 1728次战斗。

为了更准确地评估最佳组合,您将需要进行其他战斗。

因此,已经在现阶段可以提出如下的项目方案:

图片

第2步-分析游戏机制


让我们从角色开始。他具有这样的特征,这些特征会相互影响并造成损害:

  1. 攻击力 -直接转换为普通打击(1比1)所造成的伤害。计算公式:攻击力+力量+敏捷
  2. 力量 -+1攻击力量和所有力量(该怎么做,这是游戏设计)
  3. 敏捷 -+1攻击强度,每20单位敏捷也会增加1%暴击几率
  4. 克里特岛 机会 -如果打击不滑而未击中,则造成双重伤害的机会
  5. 准确性 -增加击中对手的机会
  6. 精通 -每个精通单元会将滑行打击的概率降低4%(最初等于40%,这意味着10个精通单元完全排除了滑行打击的可能性)

下图显示了强盗的基本值以及装备物品如何改变它们:

图片

因此,该开始编写代码了。我们描述在流氓类中我们已经知道的内容。set_stats_without_equip方法将在不带设备的情况下恢复字符的状态,这在更改集合时很有用。将来仅在必要时才会调用方法calculate_critical_percentcalculate_glancing_percent,以更新特定特征的值。

上课的第一行
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


现在您需要处理设备。为了方便地分类所有事物并创建它们的组合,我决定为每种类型的设备创建一个单独的词典:RIGHT_HANDS,LEFT_HANDS,GLOVES,HEADS,CHESTS,PANTS,BOOTS。以下元组作为值存储在词典中:

图片

为带有设备的词典创建一个单独的文件。我有几个带有不同集合的文件。

抽象测试设备
#    ,     :
# 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)


魔兽世界装备
#    ,     :
# 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)


将equipype字符串添加到Rogue类的构造函数中
    ...
    #    ,    id  :
    # 0 -  , 1 -  , 2 - , 3 - , 4 - , 5 - , 6 - 
    self.equipment_slots = [0] * 7

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


我们还向我们的类中添加了wear_item(穿上东西时的特性计算)和unwear_all方法(删除了所有东西)。

负责使用设备的分类方法
    ...
    #   "  ":
    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()


而且,结合一些东西的事实会提供额外的奖金(在《魔兽世界》中,这被称为“固定奖金”)。在我的抽象集中,通过同时部署森林的右手卫士和森林的左手卫兵来获得这样的奖励。将其添加到wear_item方法的代码中

在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('  ...')


现在需要教导我们的强盗如何战斗。我们将考虑对敌人站立并忙于其他事情的战斗进行一系列的1000次攻击(这是《魔兽世界》的典型情况)。不论以前的罢工如何,每次罢工可能是:

  • 正常 -标准伤害,在我们的模型中相当于角色的“攻击力”
  • 移动 -普通伤害70%
  • 暴击 -普通伤害的两倍
  • 错过 -0伤害

这将根据此方案通过一系列检查确定:

图片

对于具有基本值的强盗,此方案采用以下形式:让我们

图片

通过在类代码中添加do_attack方法编程此机制它将返回两个数字的元组:(攻击的结果,造成的损害)。

攻击代码
    ...
    #    :
    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


我们将方便地显示强盗的当前状态,以便您可以随时查看他的情况:

重新定义__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


第3步-准备启动


现在是时候编写一条代码,为所有可能的设备打架了。为此,我根据此方案依次调用函数:

图片

  1. run_session-嵌套循环在这里实现,对所有必需的字典进行排序,并为每种组合调用以下函数;最后,将生成报告文本并将其保存在会话日志中
  2. test_combination-重置所有先前磨损的物品,并反复调用wear_item方法,将角色换成新的“衣服”,然后调用下一个函数
  3. simulate_fight -同一do_attack方法被调用1000次,所接收的数据被保持,如果必要的话,一个详细的日志被保持为每个战斗

函数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



为了保存日志,我使用了两个简单的功能:

函数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)


因此,现在只需写几行即可开始会话并保存其结果。我们还将导入必要的标准Python模块。在这里,您可以确定要测试的设备集。对于《魔兽世界》的粉丝来说,我是从那儿拿起装备的,但是请记住,这个项目只是从那儿对机械师的近似改造。

程式码
#     :
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__".')


在笔记本电脑上,进行1728次战斗需要5秒钟。如果您将LOG_EVERY_FIGHT设置为True,则包含每个战斗数据的文件将显示在fight_logs文件夹中,但是该会话将花费9秒钟。无论如何,会话日志将出现在session_logs文件夹中:

日志的前10行
#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


如您所见,仅举行一个会话是不够的;您需要从数百行中提取有关导致最佳结果的事物组合的信息。为此,我们将再编写两个函数。通常的想法是打开接收到的日志,为每场战斗创建一个伤害金额列表,对其进行排序,例如写下用于5种最佳情况的物​​品名称。

确定最高档位的功能
#       :
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]


现在,在日志行的末尾出现5个选择,显示了最佳结果:

最终可读的日志行
 : 4.89 .

#1 - 293959   :
 
 
 
 
 
 
  

#2 - 293102   :
 
 
 
 
 
 
 

#3 - 290573   :
 
 
 
 
 
 
  

#4 - 287592   :
 
 
 
 
 
 
 

#5 - 284929   :
 
 
 
 
 
 
  


步骤4-评估结果的可持续性


重要的是要记住,该项目中存在随机性的元素:使用randint函数确定笔划的类型时反复进行测试,我注意到在重复使用相同输入数据的会话时,前5个选择可能会有所不同。这不是很高兴,并采取了解决问题的措施。

首先,我为设备“ obvious_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)


有了这个设置,将有6场战斗(3剑* 2匕首* 1 * 1 * 1 * 1 * 1)。在前5名中,不包括使用最差剑和最短匕首的战斗。好吧,当然,第一位应该是选择两个最坚固的刀片。如果您考虑一下,那么对于每个集合而言,很明显它将落入哪个位置。进行测试,达到了预期。

下面是该组的测试之一的结果的可视化:

图片

接下来,我减少到最低限度在由这些叶片给出的奖金的量的间隙 5000,800,20和4000,10 分别为5、4、3和2、1(在项目中,此集合位于文件“ equipment_obvious_weak.py”中)。在这里,突然间,最强壮的剑和最糟糕的匕首的组合出现在最上面。此外,在其中一项测试中,两种最好的武器突然出现在最后一位:

图片

如何理解这一点?集合的明显正确安排中的期望保持不变,但是它们之间的差异程度显着降低。而现在,战斗中的事故(未命中率,严重和非严重命中率等)已具有决定性的重要性。

让我们检查一下“顶部刀片二重奏”掉落到第一位的频率。我进行了100次这样的启动(为此,我以100次迭代的周期包装了“程序启动行”,并开始为整个“超级会话”保留特殊的日志)。这是结果的可视化:

图片

因此,我们程序中的结果并不总是稳定的(“正确”结果的34%,“错误”结果的66%)。

结果的稳定性与被测项目奖金差额成正比

鉴于测试有意义的好东西的奖金数量上的差异是微不足道的(例如在《魔兽世界》中),因此这些测试的结果将相对不稳定(不稳定,不稳定等)。

步骤5-增强弹性


我们尝试逻辑思考。

我们概述了成功标准:在99%的情况下 “顶部刀片的二重奏”应放在首位。

现状:这类案件的34%。

如果您原则上不更改接受的方法(例如,从所有选择的战斗模拟到特征的简单计算的过渡),那么仍然有必要更改我们模型的一些定量参数。

例如:

  • 不为每个集合进行一次战斗,而是进行多次,然后将算术平均值记录在日志中,丢弃最佳和最差等。
  • 一般进行几次测试,然后再取“平均”
  • 将战斗本身从1000个命中扩展到一定值,这足以使结果在总奖金方面非常接近

首先,在我看来,延长战役是一个成功的主意,因为正是在这里发生的所有事情都导致了本文的延长。

将测试一个假设,即将战斗从1000延长10,000会增加结果的稳定性(为此,您需要将常数ACKACKS_IN_FIGHT设置为10000)。就是这样:

图片

然后他决定将10,000 照片增加到100,000张照片,这成功地实现了100%。在那之后,他开始使用二分查找法来选择能够获得99%成功的命中数,以消除过多的计算。停在46 875。

图片

如果我对具有这种战斗力的系统的99%可靠性的估计是正确的,那么连续进行两次测试会将出错的可能性降低至0.01 * 0.01 = 0.0001

现在,如果您以46,875击的战斗力进行测试,以进行一套1,728场战斗的装备,这将花费233秒,并激发人们对“大师之剑”的统治力的信心:

1728次战斗的结果为46875次打击
 : 233.89 .

#1 - 13643508   :
 
 
 
 
 
 
 

#2 - 13581310   :
 
 
 
 
 
 
  

#3 - 13494544   :
 
 
 
 
 
 
 

#4 - 13473820   :
 
 
 
 
 
 
  

#5 - 13450956   :
 
 
 
 
 
 
 


PS:这很容易解释:两个“大师之剑”使您获得10个技能单位,根据固有的力学原理,它消除了滑动的可能性,当施加X或2X伤害而不是0.7X时,这增加了大约40%的笔划。

对《魔兽世界》粉丝进行类似测试的结果:

1296场战斗的结果(46 875次点击)
 : 174.58 .


#1 - 19950930   :
  '
  '
  
 
 
 
  

#2 - 19830324   :
  '
  '
 
 
 
 
  

#3 - 19681971   :
  '
  '
  
 
 
 
  

#4 - 19614600   :
  '
  '
 
 
 
 
  

#5 - 19474463   :
  '
  '
  
 
 
 
 


摘要


  1. — . , , 4 * 4 * 3 * 3 * 3 * 3 * 2 = 2592, .. 33% . .
  2. : , , , .
  3. , : , , , .

我将所有项目代码发布在github上

尊敬的社区,我将很高兴收到有关此主题的反馈。

UPD从04/08/2020:
感谢您的评论Deerenaros诺特里格里博克斯我意识到,不必模拟数千场战斗,您可以计算一次命中的数学期望值,并在此基础上对设备进行排名。我从代码中删除了与战斗相关的所有内容,而不是我执行calculate_expectationsimulate_fight函数在输出中,我得到相同的结果。将结果代码添加到存储库

All Articles