1. 程式人生 > >《連環奪寶》算法淺析

《連環奪寶》算法淺析

blog gem LG 是個 == ret get ids 應用場景

  孤陋寡聞如我,也只是去年才知道有“連環奪寶”這麽個遊戲,而且似乎各福彩大廳都有數十臺遊戲機供人“娛樂”。去年和幾個朋友路過福彩看了下,大廳裏在玩《連環奪寶》的人似乎挺多的,幾十臺的遊戲機都基本不夠用。玩眾也百色,無論是西裝革履的中年同誌,抑或打扮普通的普通民眾,都玩得不亦樂乎...老實說,我是無法理解為何他們對此沈迷如斯的,當然我對此類遊戲確實也不感冒。
  可能,通過遊戲賺(大)錢、打發時間是比較合理的解釋吧。譬如每臺遊戲機都會不定時的滾屏播報某地誰又中了多少獎勵之類信息,對不少玩眾還是有些或較有誘惑力的。
  當然,《連環奪寶》由福彩運營(?),在某些方面,可能玩眾也會放心一些吧。

  作為技術人員,我其實對《連環奪寶》裏的寶石連線算法以及寶石矩陣填充處理等技術點比較感興趣。較之遊戲開發裏的常規業務邏輯編寫,可能它們在實現上更有“技術含量”吧。
  言歸正傳。第一個問題,寶石連線算法,一開始我沒看清連線規則,以為是“一筆畫”之類,後發現不是,只要相鄰的寶石同類即可連線。細想了會算法實現,很明顯,廣度優先搜索即非常適合這種應用場景(連連看之類遊戲差不多應也是類似算法處理)。
  於是來到第二個問題,當每次符合連線規則的寶石消除後,應如何填充這個寶石矩陣呢?畢竟,這是個聯網遊戲,假定關鍵性的遊戲邏輯都在服務器端計算,那麽服務器應如何將寶石矩陣(可能較大)以及消除信息(可能較多)等告知客戶端呢?我想出的做法粗略如下:
    0) 寶石矩陣(GemMatrix)生成這個沒啥說的,一般都是通過各類寶石概率配置來生成每一個元素
    1) 消除經 BFS 標記出的寶石(可能有多組)
    2) 對 GemMatrix 按列逐行由低往高,移動寶石至低行空位,最後計算出需填補的行數(NeedRows)
    3) 對額外的寶石填充矩陣(GemMatrixEx)作類似上一步的處理
    4) 若 GemMatrixEx 的完整行數(AvailRows)少於 NeedRows,則將 GemMatrixEx 擴充 NeedRows - AvailRows 行並按步驟 0 的規則填充,再以步驟 2 的規則作寶石移動處理
    5) 按列逐行由低往高將 GemMatrixEx 中的非空元素置入 GemMatrix 中的空位
    6) 對填充後的 GemMatrix 作 BFS 處理,再轉到步驟 1,循環往復直至 GemMatrix 消無可消
  而綜合以上思路,由服務器告知客戶端的最終的寶石大矩陣及消除歷史等信息已不難生成。

  簡化的處理代碼如下。

-- 每關中寶石矩陣尺寸
local MatrixSizeArr = {4, 5, 6}
-- 寶石矩陣
local Gems = {}
-- 寶石填補矩陣
local GemsStandby = {}

local function GetValidStandbyRowNum(lv)    
    local ret = 0
    local sz = MatrixSizeArr[lv]
    local r = #GemsStandby
    for row = 1, r do
        for col = 1, sz do
            if GemsStandby[row][col] == 0
then return ret end end ret = ret + 1 end return ret end local function ExtendStandbyGems(row, lv) local sz = MatrixSizeArr[lv] for i = 1, row do table.insert(GemsStandby, {}) for j = 1, sz do table.insert(GemsStandby[#GemsStandby], GenGemIndex(j))
end end end local function ShiftStandbyGems(lv) local r = #GemsStandby if r < 2 then return true end local sz = MatrixSizeArr[lv] for col = 1, sz do for row = 1, r do if GemsStandby[row][col] == 0 then for k = row + 1, r do if GemsStandby[k][col] >= 1 and GemsStandby[k][col] <= 5 then GemsStandby[row][col] = GemsStandby[k][col] GemsStandby[k][col] = 0 break end end end end end return true end local function ShiftGems(lv) local sz = MatrixSizeArr[lv] for col = 1, sz do for row = 1, sz do if Gems[row][col].Selected or Gems[row][col].GemIndex == 0 then local bFlag = false for k = row + 1, sz do if (not Gems[k][col].Selected) and (Gems[k][col].GemIndex >= 1) and (Gems[k][col].GemIndex <= 5) then Gems[row][col].GemIndex = Gems[k][col].GemIndex Gems[row][col].Selected = false Gems[k][col].GemIndex = 0 Gems[k][col].Selected = false bFlag = true break else Gems[k][col].Selected = false Gems[k][col].GemIndex = 0 end end if not bFlag then Gems[row][col].GemIndex = 0 Gems[row][col].Selected = false end end end end return true end local function FillGems(lv) local needRows = 0 local sz = MatrixSizeArr[lv] for col = 1, sz do local tmp = 0 for row = sz, 1, -1 do if Gems[row][col].GemIndex == 0 then tmp = tmp + 1 else break end end if tmp > needRows then needRows = tmp end end ShiftStandbyGems(lv) local num = GetValidStandbyRowNum(lv) if num < needRows then ExtendStandbyGems(needRows - num, lv) ShiftStandbyGems(lv) end for col = 1, sz do for row = 1, sz do if Gems[row][col].GemIndex == 0 then for row2 = 1, needRows do if GemsStandby[row2][col] ~= 0 then Gems[row][col].GemIndex = GemsStandby[row2][col] GemsStandby[row2][col] = 0 break end end end end end return true end

《連環奪寶》算法淺析