程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 掃雷外掛的設計與實現

掃雷外掛的設計與實現

編輯:Delphi

  如前所述,算法層的實現,不外乎兩種操作:1。如果一個方塊的數值等於周圍未挖開的方塊數目,則把周圍所有方塊標記為雷;2。如果一個方塊的數值等於周圍已經標記為雷的方塊個數,則在該塊上同時單擊左右鍵。實際上,這只是最簡單的兩種判斷(簡單到甚至不該稱之為“判斷”,而只是例行公事而已),而比這更復雜的分析判斷還可以有很多,但現在我們追求的是程序的簡單易懂,而且,就這兩種最簡單的判斷,已經可以達到很好的效果了,在實際中它們絕對占到了掃雷所用時間的一大多半。更高級的判斷,在掃雷外掛的0.2版本裡也已經實現了,但在此處若要加以敘述,不免還要大幅增加篇幅。

  就來看這個最簡單的算法:

  =================================================================

  //根據Cells中的數據進行判斷,把適當的操作填入Operations中
  procedure AnalyzeCells;
  var
    i, j: Integer;
    neighborCount: Integer;      //保存一個方塊周圍未挖開的方塊的數目
  begin
    //首先清空輸出緩沖區
    for i:=0 to AreaWidth-1 do
    for j:=0 to AreaHeight-1 do
      Operations[i, j] := opNone;
    //掃描輸入緩沖區,執行兩種最簡單的判斷
    for i:=0 to AreaWidth-1 do
    for j:=0 to AreaHeight-1 do
    begin
      //取得一個方塊周圍未挖開的方塊的數目
      neighborCount := CountNeighbors(i, j, [csUnknown, csPossible]);
      //只有1~8的數字,並且周圍存在未挖開的方塊,這樣的方塊才有分析價值
      if (Cells[i, j]>cs0) and (Cells[i, j]<=cs8) and (neighborCount > 0) then
        //第一種情況
        if neighborCount = Ord(Cells[i, j])-CountNeighbors(i, j, [csMarked]) then
          MarkAllNeighbors(i, j)
        //第二種情況
        else if Ord(Cells[i, j]) = CountNeighbors(i, j, [csMarked]) then
          Operations[i, j] := opBothClick;
    end;
  end;

  
  //將指定方塊周圍8個方塊中,未挖開的,包括已標記問號的,都標記為雷。
  procedure MarkAllNeighbors(const x, y: Integer);
  var
    i,j: Integer;
  begin
    //掃描以某個坐標為中心的9個方塊
    for i:=x-1 to x+1 do
    for j:=y-1 to y+1 do
    begin
      //去除中心塊,並避免數組越界
      if ((i=x) and (j=y)) or (i<0) or (i>=AreaWidth) or (j<0) or (j>=AreaHeight) then
        Continue;
      //未挖開的空白則單擊右鍵,未挖開的標問號的,則雙擊右鍵
      if Cells[i, j] = csUnknown then
          Operations[i, j] := opRightClick
      else if Cells[i, j] = csPossible then
          Operations[i, j] := opRightDoubleClick;
    end;
  end;

  
  //取得指定方塊周圍8個方塊中等於任一個指定狀態的方塊的個數。
  function CountNeighbors(const x, y: Integer; const targetStates: TCellStates): Integer;
  var
    i,j: Integer;
  begin
    result := 0;
    //掃描以某個坐標為中心的9個方塊
    for i:=x-1 to x+1 do
    for j:=y-1 to y+1 do
    begin
      //去除中心塊,並避免數組越界
      if ((i=x) and (j=y)) or (i<0) or (i>=AreaWidth) or (j<0) or (j>=AreaHeight) then
        Continue;
      //計數指定狀態的方塊
      if Cells[i, j] in targetStates then
        Inc(result);
    end;
  end;
  =================================================================

  其中,由於枚舉TCellState的常量位置的安排,Ord函數對cs0~cs8所取得的值正是0~8,即等於該方塊的數值。這個算法可以算是中規中矩,沒什麼取巧的地方,因此應該不那麼難懂。不錯,至此掃雷外掛已經完全實現完畢。把上述所有函數和全局內容放在一個單元(可以是一個窗體)裡,設好TTimer控件的間隔,就可以很理想的工作了。在外掛類程序的開發中,本例用到的也許是最“笨”的一種方法,但對於平面方格類游戲,其原理具有通用性。它不需對游戲底層數據、協議之類有什麼了解,只需要了解游戲的屏幕圖形就可以了。本例對於Windows窗口相關的某些API,也是一個較好的熟悉機會,對於初學者會有其意義。這個例子本身並不是最完善的,了解了思想,每個人自可以做出更加完善的程序。比如,應用鉤子,這會大幅度減少該程序占用的系統資源。

  幾天時間終於寫完了這堆文章,希望能給適當的人帶來適當的收益。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved