Python练习:炉石传说荣誉室返尘最优策略

一 背景

      炉石2019年荣誉室的消息公布后,很多人都在问荣誉室最优选择策略的问题。刚学了Python的基本语法,想试试手。因为对列表、字典这些基础类型的方法不太熟悉,写的时候遇到了好多问题,好在解决了大部分,目前所写的不是很健全,程序逻辑上有漏洞。如果有意向学习Python,可以自己动手写写这类项目,提高效果还是比较明显的。

二 最优选择策略分析

2.1 基础规则解释

返尘规则:

       ​

Python练习:炉石传说荣誉室返尘最优策略_第1张图片 官网截图

Python练习:炉石传说荣誉室返尘最优策略_第2张图片

Python练习:炉石传说荣誉室返尘最优策略_第3张图片

Python练习:炉石传说荣誉室返尘最优策略_第4张图片

规则解释:

      共9张非同名卡进了荣誉室,每套牌中可佩带1张同名传说品质卡,2张同名史诗\稀有\普通 品质卡。如果一张卡拥有金色与白色两种,则优先返还金色,其次白色。返还值为卡片的合成尘数,非分解尘数。本次9张卡中包括:2张非同名传说卡,2张非同名史诗卡,3张非同名稀有卡,2张非同名普通卡。

卡牌合成\分解尘数规则:

 

 

          白色

         金色

合成

分解

合成

分解

普通品质

40

5

400

50

稀有品质

100

20

800

100

史诗品质

400

100

1600

400

传说品质

1600

400

3200

1600

2.2 最优策略思路

    因为荣誉室返还的合成所需尘数,所以合成某卡就相当于白嫖该卡,原则上荣誉室只让玩家赚了所合成卡的分解尘数。但有特殊情况,当用户有某类卡的金色卡时,会发生赚不到分解尘数的情况(因为荣誉室返还的优先级:金色卡>白色卡)。

    在上述逻辑下,如果玩家已经拥有了某卡,想通过荣誉室得到更高的利益,就相当于期望: 一顿操作后的返尘数  > 不操作返尘数。要达成这个条件,等同于  一顿操作后所合成卡的分解尘数 > 已有卡的合成尘数 。 按照这个条件,我们看下上面的 “卡牌合成\分解尘数规则” , 发现 只有当卡牌品质为普通品质时,满足这个要求(50-40=10,单卡可以赚10尘),其他品质下,二者是相等的关系。

由此,我们可以得到结论:(返还优先级:金色卡 > 白色卡 > 没有卡)

合成任何 返还优先级 比 你所拥有 高的卡都不会亏。

如果你拥有某张卡的白色卡,只有该卡的品质为 普通 时才需要合成金色卡,稀有\史诗\传说品质的都不会赚。

三 程序设计

3.1说明

    下面的代码没有过多加入上述分析过程,是逻辑上的遍历,把存在的所有方案都算了一遍,求出每个方案的最终收益尘数

3.2构成分析

共分解成了5个方法。(这个项目中的方法、变量的命名不规范,仅供参考学习,需注意此问题)

operation_card(oper, color, card)  # 输入合成或分解 某种颜色的卡片,返回此项操作影响的尘数
fand(num) # 输入受返还影响的卡片总数,返回 所有方案的集合
fand_num(list_str, card) # 输入所有方案的集合、卡片的品质,返回该方案的尘数
color_sim(color) # 输入英文颜色,返回中文颜色(这个方法无关紧要,为了练习加的)
user_have(num, color, card, numx) # 输入用户拥有的某品质卡的总数量,以及卡片颜色、受返还影响的卡片总数  打印方案及尘数信息  (暂不支持用户同时拥有某个品质两种颜色的卡,这里需要再修改一下)

3.3源码

from functools import reduce

def operation_card(oper, color, card):
    if color == 'ordinary':
        if card == 'ordinary':
            if oper == 'add':
                return -40
            elif oper == 'reduce':
                return 5
        elif card == 'rare':
            if oper == 'add':
                return -100
            elif oper == 'reduce':
                return 20
        elif card == 'epic':
            if oper == 'add':
                return -400
            elif oper == 'reduce':
                return 100
        elif card == 'legend':
            if oper == 'add':
                return -1600
            elif oper == 'reduce':
                return 400
        else:
            return print('请输入正确的品质!')
    elif color == 'golden':
        if card == 'ordinary':
            if oper == 'add':
                return -400
            elif oper == 'reduce':
                return 50
        elif card == 'rare':
            if oper == 'add':
                return -400
            elif oper == 'reduce':
                return 100
        elif card == 'epic':
            if oper == 'add':
                return -1600
            elif oper == 'reduce':
                return 400
        elif card == 'legend':
            if oper == 'add':
                return -3200
            elif oper == 'reduce':
                return 1600
        else:
            return print('请输入正确的品质!')
    else:
        return print('请输入正确的颜色!')


def fand(num):  
    list_all = []
    list_f = ['无', '白卡', '金卡']
    for i in range(num):
        list_all.append(list_f)  # list_all 表示 本次荣誉室 总卡数 存在的情况,list格式

    def funk(list1, list2):
        List = []
        for i in list1:
            if i != '无':
                for j in list2:
                    if j != '无':
                        List.append(i + ',' + j)
        return List

    List_m = reduce(funk, list_all)  # 内含字符串格式
    List_l = [L.split(sep=',') for L in reduce(funk, list_all)]  # 内含列表格式

    Kyy = []
    for list in List_m:
        Ky = {}
        for i in range(num):
            if Ky.get(list[i * 3:i * 3 + 2]):
                Ky[list[i * 3:i * 3 + 2]] = Ky[list[i * 3:i * 3 + 2]] + 1
            else:
                Ky[list[i * 3:i * 3 + 2]] = 1
        Kyy.append(Ky)

    return Kyy


def fand_num(list_str, card):
    number = []
    for vk in list_str:
        a = 0
        b = 0
        if vk.get('白卡'):
            if vk.get('白卡') > 0:
                a = vk.get('白卡') * operation_card('add', 'ordinary', card)
            else:
                a = vk.get('白卡') * operation_card('reduce', 'ordinary', card)
        if vk.get('金卡'):
            if vk.get('金卡') > 0:
                b = vk.get('金卡') * operation_card('add', 'golden', card)
            else:
                b = vk.get('金卡') * operation_card('reduce', 'golden', card)
        number.append(a + b)
    return number


def color_sim(color):
    if color == 'ordinary':
        color_cn = '白卡'
    elif color == 'golden':
        color_cn = '金卡'
    return color_cn


def user_have(num, color, card, numx):
    # 荣誉室返尘数
    if num >= numx:
        honor_u = numx * -operation_card('add', color, card)  # 用户原有返尘数
    else:
        honor_u = num * -operation_card('add', color, card)
    sum_u = num * operation_card('reduce', color, card) # 用户原有尘数
    print('现有卡分解后尘数:%s ,荣誉室可返还尘数:%s' % (sum_u, honor_u))

    color_cn = color_sim(color)
    card_ol = fand(num)  # 方案具体情况
    card_uh = {color_cn: num}

    honor_f = []
    
    # 方案纠偏
    for card_uo in card_ol:
        # print('循环%s次 \n %s' % (card_ol.index(card_uo), card_uo))
        if card_uo.get(color_cn):
            #  card_on[card_on.index(card_uo)][color_cn] = card_uo.get(color_cn) - card_uh.get(color_cn)
            honor_f.append(card_uo.get(color_cn) * -operation_card('add', color, card))
            # print('第%s次 honor_f append success \n %s' % (card_ol.index(card_uo), honor_f))
            card_uo[color_cn] = card_uo.get(color_cn) - card_uh.get(color_cn)
        else:
            honor_f.append(0)

    pay = fand_num(card_ol, card)
    # 拥有+返尘数
    sum_user_ade = sum_u + honor_u
    # 付出+方案返尘数
    sum_f_ade = [pay[i] + honor_f[i] for i in range(len(honor_f))]

    print('你所拥有情况:%s张%s%s' % (num, color_cn, card))
    print('原有方案价值尘数:', sum_user_ade)
    print('各种方案可得尘数:', sum_f_ade)
    print('方案细节:', fand(num))

    aaa = []
    bb = sum_f_ade
    cc = fand(num)
    for i in range(len(bb)):
        aaa.append([cc[i], bb[i]])
    aaa.sort(key=lambda d: d[1], reverse=True)

    for i in range(len(aaa)):
        print(aaa[i])

user_have(5, 'ordinary', 'ordinary', 4) # 用户拥有5张白色普通品质的卡,此次荣誉室加了4张此品质的卡(包含同名卡)
'''
各字段输入内容
        oper:   add/reduce
        color:  ordinary/golden
        card:   ordinary/rare/epic/legend
'''

输出结果:

现有卡分解后尘数:25 ,荣誉室可返还尘数:160
你所拥有情况:5张白卡ordinary
原有方案价值尘数: 185
各种方案可得尘数: [200, -245, -245, -690, -245, -690, -690, -1135, -245, -690, -690, -1135, -690, -1135, -1135, -1580, -245, -690, -690, -1135, -690, -1135, -1135, -1580, -690, -1135, -1135, -1580, -1135, -1580, -1580, -2000]
方案细节: [{'白卡': 5}, {'白卡': 4, '金卡': 1}, {'白卡': 4, '金卡': 1}, {'白卡': 3, '金卡': 2}, {'白卡': 4, '金卡': 1}, {'白卡': 3, '金卡': 2}, {'白卡': 3, '金卡': 2}, {'白卡': 2, '金卡': 3}, {'白卡': 4, '金卡': 1}, {'白卡': 3, '金卡': 2}, {'白卡': 3, '金卡': 2}, {'白卡': 2, '金卡': 3}, {'白卡': 3, '金卡': 2}, {'白卡': 2, '金卡': 3}, {'白卡': 2, '金卡': 3}, {'白卡': 1, '金卡': 4}, {'金卡': 1, '白卡': 4}, {'金卡': 2, '白卡': 3}, {'金卡': 2, '白卡': 3}, {'金卡': 3, '白卡': 2}, {'金卡': 2, '白卡': 3}, {'金卡': 3, '白卡': 2}, {'金卡': 3, '白卡': 2}, {'金卡': 4, '白卡': 1}, {'金卡': 2, '白卡': 3}, {'金卡': 3, '白卡': 2}, {'金卡': 3, '白卡': 2}, {'金卡': 4, '白卡': 1}, {'金卡': 3, '白卡': 2}, {'金卡': 4, '白卡': 1}, {'金卡': 4, '白卡': 1}, {'金卡': 5}]
[{'白卡': 5}, 200]
[{'白卡': 4, '金卡': 1}, -245]
[{'白卡': 4, '金卡': 1}, -245]
[{'白卡': 4, '金卡': 1}, -245]
[{'白卡': 4, '金卡': 1}, -245]
[{'金卡': 1, '白卡': 4}, -245]
[{'白卡': 3, '金卡': 2}, -690]
[{'白卡': 3, '金卡': 2}, -690]
[{'白卡': 3, '金卡': 2}, -690]
[{'白卡': 3, '金卡': 2}, -690]
[{'白卡': 3, '金卡': 2}, -690]
[{'白卡': 3, '金卡': 2}, -690]
[{'金卡': 2, '白卡': 3}, -690]
[{'金卡': 2, '白卡': 3}, -690]
[{'金卡': 2, '白卡': 3}, -690]
[{'金卡': 2, '白卡': 3}, -690]
[{'白卡': 2, '金卡': 3}, -1135]
[{'白卡': 2, '金卡': 3}, -1135]
[{'白卡': 2, '金卡': 3}, -1135]
[{'白卡': 2, '金卡': 3}, -1135]
[{'金卡': 3, '白卡': 2}, -1135]
[{'金卡': 3, '白卡': 2}, -1135]
[{'金卡': 3, '白卡': 2}, -1135]
[{'金卡': 3, '白卡': 2}, -1135]
[{'金卡': 3, '白卡': 2}, -1135]
[{'金卡': 3, '白卡': 2}, -1135]
[{'白卡': 1, '金卡': 4}, -1580]
[{'金卡': 4, '白卡': 1}, -1580]
[{'金卡': 4, '白卡': 1}, -1580]
[{'金卡': 4, '白卡': 1}, -1580]
[{'金卡': 4, '白卡': 1}, -1580]
[{'金卡': 5}, -2000]

四 后记

        写的比较复杂,有很大的优化空间,记录下来自己的成长。喜欢玩炉石的朋友可以拿这个小项目当做学习Python的兴趣点开始入手。还有很多可以完善的地方,比如用户的策略倾向可能是不分解金卡,这样就不能简单的直接按照尘数计算收益率了;比如用户只有3000尘,在已有资源下的最优策略方案有哪些,这样的话最后一步输出方案时需要做个条件筛选,等等。这次原本是打算一天写完,赶快往下学习爬虫、数据所需的三方模块,结果中间因为排列组合、列表字典字符串的内置方法、三重循环时把数据类型搞蒙 这些种种原因查了3天才写了这个简单的项目,有很多不足,好在主流程能跑通,接下来要加紧进度了!

你可能感兴趣的:(Python)