善用表驅動法
最近碰到個需求,計算遊戲得分的規則,類似這樣:
...... 這些獎勵規則沒有什麼規律,隨著人數增多,就越發複雜了,並且業務人員可能隨時改變這些規則。 顯然,獎勵規則可以采用策略模式,定義策略接口,根據遊戲人數定義不同的規則,本質上就是利用動態的多態調用。可以想見,還是少不了複雜的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={
一個三級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。其他玩家的查表過程與此類似,不細說了。:"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表,這個表看起來複雜,其實完全可以自動生成,讓業務人員來提供樣例數據,解碼樣例數據並生成這個表是很簡單的事情。積分數組的“解碼”,我給一個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