计算公式 f = (base + genconst) * percent * genpercent + const
公式中的运算变量均问长度为 26 的一维向量。 在游戏卡牌中,假如有 20 个养成系统, 会对公式中的某个或多个部分提供一个加成,在 20 个系统加成完成后, 计算出最终的结果。

在一个函数中, 创建与公式对应的变量, 依次计算 20 个养成系统的加成并将养成累加到对应的变量, 最终得到结果。
这样的计算方式,非常简单直观。 但存在两个的问题:

  1. 当只有某个养成系统变化时,为了得到结果, 你需要完整的把 20 个都跑一遍
  2. 当其中某个养成计算结果比较费时时,你需要对特定的养成做缓存来提高计算的效率

将公式拆解成一颗树的形式,如下所示:
属性优化示意图

每个养成系统将自己的值放到对应的节点, 当某个养成系统有改动时,去设置对应节点的值,然后改动以增量的形式沿着路径往上走,直到根节点,这样根节点的值就是最新的,同时也不会引起到其他节点的计算。

这样就很好的处理了现有计算方式的两个问题, 但引入了额外的内存消耗, 但在可接受范围,在python 中不要忘记用 slots 优化内存。

demo 代码:

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. try:
  4. from game.object import AttrDefs
  5. # for test
  6. except ImportError:
  7. AttrMaxID = 26 + 1
  8. else:
  9. AttrMaxID = AttrDefs.attrTotal + 1
  10. import numpy as np
  11. def dict2attrs(d):
  12. value = np.zeros(AttrMaxID)
  13. for i, attr in enumerate(AttrDefs.attrsEnum):
  14. if attr:
  15. value[i] = d.get(attr, 0)
  16. return np.array(value)
  17. def attrs2dict(attr):
  18. d = {}
  19. for i, v in enumerate(attr):
  20. if v:
  21. d[AttrDefs.attrsEnum[i]] = v
  22. return d
  23. class AttrContext(object):
  24. '''simple context'''
  25. def __init__(self, game, scene=0, **kwargs):
  26. self.game = game
  27. self.scene = scene
  28. self.__dict__.update(kwargs)
  29. # TODO: use statement with to achieve a temp context
  30. class Node(object):
  31. __slots__ = ("name", "ret", "_adds", "_parent", "left", "right", "tag")
  32. def __init__(self, name, tag=None, parent=None, left=None, right=None):
  33. self.name = name
  34. self.ret = np.zeros(AttrMaxID)
  35. self._adds = {}
  36. self._parent = parent
  37. self.left = left
  38. self.right = right
  39. self.tag = tag
  40. def __str__(self):
  41. return '<Node object at 0x%x>\n%s: %s\n' % (id(self), self.name, tuple(self.ret))
  42. def addLeft(self, node):
  43. self.left = node
  44. node._parent = self
  45. node.tag = 'l'
  46. def addRight(self, node):
  47. self.right = node
  48. node._parent = self
  49. node.tag = 'r'
  50. def set(self, k, v):
  51. d = v - self._adds.get(k, 0)
  52. if any(d):
  53. self._adds[k] = v
  54. self.ret += d
  55. self.onChange(d)
  56. def onChange(self, d):
  57. if self._parent:
  58. self._parent.change(d, self.tag)
  59. def change(self, d, tag):
  60. if tag == 'l':
  61. v = self.right.ret
  62. else:
  63. v = self.left.ret
  64. if self.name == '*':
  65. d1 = v*d
  66. elif self.name == '+':
  67. d1 = d
  68. if any(d1):
  69. self.ret += d1
  70. self.onChange(d1)
  71. class Calculator(object):
  72. def __init__(self, ctx):
  73. self.ctx = ctx
  74. self._nodes = {}
  75. self._ft = self.buildFTree()
  76. self.init()
  77. # set default value
  78. def init(self):
  79. self.percent.set('default', np.ones(AttrMaxID))
  80. self.genpercent.set('default', np.ones(AttrMaxID))
  81. def __getattr__(self, name):
  82. try:
  83. return self._nodes[name]
  84. except KeyError:
  85. raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, name))
  86. def buildFTree(self, f):
  87. root = None
  88. p = Node('+')
  89. root = p
  90. base = Node('base')
  91. genconst = Node('genconst')
  92. percent = Node('percent')
  93. genpercent = Node('genpercent')
  94. const = Node('const')
  95. self._nodes['base'] = base
  96. self._nodes['genconst'] = genconst
  97. self._nodes['percent'] = percent
  98. self._nodes['genpercent'] = genpercent
  99. self._nodes['const'] = const
  100. p.addRight(const)
  101. tn = Node('*')
  102. p.addLeft(tn)
  103. p = tn
  104. p.addRight(genpercent)
  105. tn = Node('*')
  106. p.addLeft(tn)
  107. p = tn
  108. p.addRight(percent)
  109. tn = Node('+')
  110. p.addLeft(tn)
  111. p = tn
  112. p.addLeft(base)
  113. p.addRight(genconst)
  114. return root
  115. @property
  116. def result(self):
  117. return attrs2dict(self._ft.ret)
  118. # show formula
  119. def showf(self):
  120. f = []
  121. def show(p, f):
  122. if p.name in '+*':
  123. f.append('(')
  124. if p.left:
  125. show(p.left, f)
  126. f.append(p.name)
  127. if p.right:
  128. show(p.right, f)
  129. if p.name in '+*':
  130. f.append(')')
  131. show(self._ft, f)
  132. print(''.join(f))

posted on 2019-03-07 16:52 nowg 阅读() 评论() 编辑 收藏

版权声明:本文为nowg原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/nowg/p/10490707.html