閱讀824 返回首頁    go 微軟 go windows


善用表驅動法

  最近碰到個需求,計算遊戲得分的規則,類似這樣:

遊戲人數

第一名獲得賭注

第二名獲得賭注

第三名獲得賭注

第四名獲得賭注

二人

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

最後更新:2017-05-17 18:01:44

  上一篇:go  使用Rope來高效處理長字符串
  下一篇:go  scheme解決約瑟夫環問題(續)