善用表驱动法

简介:
  最近碰到个需求,计算游戏得分的规则,类似这样:

游戏人数

第一名获得赌注

第二名获得赌注

第三名获得赌注

第四名获得赌注

二人

100%

0%

二人(出现2个第1名时)

50%

50%

 

 

三人

70%

30%

0%

三人出现3个第1名时

33.3333%

33.3333%

33.3333%

 

三人(出现2个第1名时)

50%×2

0%

 

 

......
......
    这些奖励规则没有什么规律,随着人数增多,就越发复杂了,并且业务人员可能随时改变这些规则。
    显然,奖励规则可以采用策略模式,定义策略接口,根据游戏人数定义不同的规则,本质上就是利用动态的多态调用。可以想见,还是少不了复杂的case...when语句,以及繁多的代码。恰好最近读《unix编程艺术》和《代码大全2》,两者都提到一个结论:人类阅读复杂数据结构远比复杂的控制流程容易,或者说数据驱动开发是非常有价值的。《代码大全2》声称这个是表驱动法。因此,这个奖励系数的计算,能否转化成一个查表过程呢?注意到,在游戏中,名次是根据个人的积分在所有玩家中的排位来决定,大概会有这么个排序的玩家积分数组[100,50,3],这个数组表示3个玩家,第一名100分,第二名50分,第三名才3分。依据规则,第一名的奖励系数就是0.7,第二名就是0.3。我想到类似这样的数组其实都有个形式表示它们的内置结构,比如[100,50,3]数组的“结构”是"111",代表3个位置都有一个人。将"111"作为关键码去查表不就OK了?
    将每个排好序的积分数组解码为这样的关键码,然后去查预先写好的奖励系数表,这个奖励系数表大概类似:
  @@award_rate_hash = {
    :
" 2 " => {
      :
" 11 " => {: " 1 " => 1 ,: " 2 " => 0},
      :
" 20 " => {: " 1 " => 0.5 ,: " 2 " => 0.5 }
    },
    :
" 3 " => {
      :
" 111 " => {: " 1 " => 0.7 ,: " 2 " => 0.3 ,: " 3 " => 0},
      :
" 300 " => {: " 1 " => 0.33 },
      :
" 201 " => {: " 1 " => 0.5 ,: " 3 " => 0},
      :
" 120 " => {: " 1 " => 1 ,: " 2 " => 0}
    },
    :
" 4 " => {
      :
" 1111 " => {: " 1 " => 0.65 ,: " 2 " => 0.30 ,: " 3 " => 0.05 ,: " 4 " => 0},
      :
" 4000 " => {: " 1 " => 0.25 },
      :
" 3001 " => {: " 1 " => 0.33 ,: " 4 " => 0},
      :
" 1300 " => {: " 1 " => 1 ,: " 2 " => 0},
      :
" 2020 " => {: " 1 " => 0.5 ,: " 3 " => 0},
      :
" 1201 " => {: " 1 " => 0.7 ,: " 2 " => 0.15 ,: " 4 " => 0},
      :
" 1120 " => {: " 1 " => 0.7 ,: " 2 " => 0.3 ,: " 3 " => 0},
      :
" 2011 " => {: " 1 " => 0.35 ,: " 3 " => 0.3 ,: " 4 " => 0}
    }      
  }
    一个三级hash表,首先根据玩家人数查到特定的系数表,比如要查3个玩家、积分数组是[100,50,3]的奖励系数表就是   @@award_rate_hash [:"3"],然后积分数组[100,50,3] 解码为:"111",继续查,如此规则的奖励系数表就是 @@award_rate_hash [:"3"][ :"111"]——也就是 {: " 1 " => 0.7 ,: " 2 " => 0.3 ,: " 3 " => 0},那么查积分是100的玩家系数就是 @@award_rate_hash [:"3"][ :"111"][([100,50,3].index(100)+1 ).to_s.to_sym],也就是 : " 1 " => 0.7 [100,50,3].index(100)+1 就是积分100的玩家在数组中的名次(即1),也就是:"1"指向的结果0.7。其他玩家的查表过程与此类似,不细说了。
    这样,我们所有的奖励规则就是维护这么一张hash表,这个表看起来复杂,其实完全可以自动生成,让业务人员来提供样例数据,解码样例数据并生成这个表是很简单的事情。积分数组的“解码”,我给一个Ruby版本:
    # 解码数组为字符串关键码
   def decode_array(array)
    len
= array.size
    trace_list
= []
    result
= []
    len.times do 
| time |
      result[time]
= 0   
      trace_list[time]
= false
    end
    array.each_with_index do 
| item,index |
      result[index]
= count_times(array,trace_list,index,len)
    end
    
return  result.join( '' ).to_sym
  end
  
def count_times(array,trace_list,index,len)
    item
= array[index]
    result
= 0
     (index..len).each do 
| i |
      
if  array[i] == item  and  !trace_list[i]
        result
+= 1
        trace_list[i]
= true
      end
    end
    
return  result
  end
文章转自庄周梦蝶  ,原文发布时间2008-04-17
目录
相关文章
|
存储 Cloud Native Linux
C++ 表驱动方法代替if-else
C++ 表驱动方法代替if-else
|
Dart 对象存储 索引
表驱动法,逻辑控制优化利器
最近好多同学在开发过程中谈到设计表结构的一些idea,为了让大家少走一些弯路,今天就计划聊聊表驱动法吧~
表驱动法,逻辑控制优化利器
|
数据库 索引 大数据
这才是真正的表扩展方案
事情变得有意思了,上一篇花1小时撰写的“一分钟”文章,又引起了广泛的讨论,说明相关的技术大家感兴趣,挺好。第一次一篇技术文章的评论量过100,才知道原来“评论精选”还有100上限,甚为欣慰(虽然是以一种自己不愿看到的方式)。
660 0