最近碰到個需求,計算遊戲得分的規則,類似這樣:
遊戲人數
第一名獲得賭注
第二名獲得賭注
第三名獲得賭注
第四名獲得賭注
二人
100%
0%
—
二人(出現2個第1名時)
50%
三人
70%
30%
三人(出現3個第1名時)
33.3333%
三人(出現2個第1名時)
50%×2
......
這些獎勵規則沒有什麼規律,随着人數增多,就越發複雜了,并且業務人員可能随時改變這些規則。
顯然,獎勵規則可以采用政策模式,定義政策接口,根據遊戲人數定義不同的規則,本質上就是利用動态的多态調用。可以想見,還是少不了複雜的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)
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
return result
文章轉自莊周夢蝶 ,原文釋出時間2008-04-17