不得不說,捕獲“掃雷”窗口以及取得它的數(shù)據(jù),是本程序的一個難點。現(xiàn)在這個難點已經(jīng)解決,接下來,完成接口層已經(jīng)不是問題了。那么,來看接口層的兩個核心過程:
=================================================================
//取得整個雷區(qū)每個方塊的狀態(tài),填入Cells中供分析。
PRocedure FetchCells;
var
  i, j: Integer;
begin
  //掃描每個方塊,根據(jù)指定像素的顏色判斷該方塊的性質(zhì)。
  //特定像素的顏色與方塊性質(zhì)的對應(yīng)關(guān)系歸納自“掃雷”程序本身的資源。
  for i:=0 to AreaWidth-1 do
  for j:=0 to AreaHeight-1 do
    //首先判斷(0, 0)點的像素
    case TColor(GetPixel(MineDC, LEFT_MARGIN + i*CELL_WIDTH, TOP_MARGIN + j*CELL_HEIGHT)) of
      clWhite:
        //是未挖開的方塊,再判斷(5, 4)點的像素
        case TColor(GetPixel(MineDC, LEFT_MARGIN + i*CELL_WIDTH + 5, TOP_MARGIN + j*CELL_HEIGHT + 4)) of
          clSilver: Cells[i, j] := csUnknown;     //未翻開的方塊
          clRed: Cells[i, j] := csMarked;           //已標記為雷的方塊
          clBlack: Cells[i, j] := csPossible;      //標問號的方塊
        end;
      clGray:
        //是已挖開的方塊,再判斷(7, 4)點的像素
        case TColor(GetPixel(MineDC, LEFT_MARGIN + i*CELL_WIDTH + 7, TOP_MARGIN + j*CELL_HEIGHT + 4)) of
          clSilver: Cells[i, j] := cs0;    //空白,相當(dāng)于數(shù)字0
          clBlue: Cells[i, j] := cs1;      //數(shù)字1
          clGreen: Cells[i, j] := cs2;     //數(shù)字2
          clRed: Cells[i, j] := cs3;       //數(shù)字3
          clNavy: Cells[i, j] := cs4;      //數(shù)字4
          clMaroon: Cells[i, j] := cs5;    //數(shù)字5
          clTeal: Cells[i, j] := cs6;      //數(shù)字6
          clBlack: Cells[i, j] := cs7;     //數(shù)字7
          clGray: Cells[i, j] := cs8;      //數(shù)字8
        end;
    end;
end;
=================================================================
以上程序中,三個包含 case...of 的行,都用到了前面實測到的數(shù)據(jù)。執(zhí)行完之后,掃雷窗口所有方塊的狀態(tài)就原原本本地在輸入緩沖區(qū)里了。
=================================================================
//將Operations中所記載的對每個方塊的操作真正作用于掃雷窗口。
procedure OperateCells;
var
  i, j: Integer;
  downMsg, upMsg: Cardinal;     //按下和抬起鼠標按鈕時分別發(fā)送的消息
  wparam, lparam: Integer;      //消息參數(shù)
  clickCount: Integer;          //按鍵次數(shù),只有取值1或2
begin
  //掃描每個方塊
  for i:=0 to AreaWidth-1 do
  for j:=0 to AreaHeight-1 do
  begin
    if Operations[i, j] = opNone then
      Continue;
    //根據(jù)操作種類,設(shè)定發(fā)送的消息及其參數(shù)
    lparam := ((TOP_MARGIN + j*CELL_HEIGHT) shl 16) + (LEFT_MARGIN + i*CELL_WIDTH);
    wparam := IfThen(Operations[i, j] = opBothClick, MK_RBUTTON, 0);
    downMsg := IfThen(Operations[i, j] in [opRightClick, opRightDoubleClick], WM_RBUTTONDOWN, WM_LBUTTONDOWN);
    upMsg := IfThen(Operations[i, j] in [opRightClick, opRightDoubleClick], WM_RBUTTONUP, WM_LBUTTONUP);
    //設(shè)定發(fā)送消息次數(shù),即單擊還是雙擊
    clickCount := IfThen(Operations[i, j] = opRightDoubleClick, 2, 1);
    //發(fā)送消息
    repeat
      PostMessage(MineWnd, downMsg, wparam, lparam);
      PostMessage(MineWnd, upMsg, wparam, lparam);
      Dec(clickCount);
    until clickCount = 0;
  end;
end;
=================================================================
這里需要說的是WM_LBUTTONDOWN、WM_LBUTTONUP、WM_RBUTTONDOWN和WM_RBUTTONUP四個消息。它們是在一個窗口客戶區(qū)內(nèi)按下或抬起鼠標左或右按鈕時,發(fā)給這個窗口的消息。所以,手動地發(fā)送這些消息,其實就是模擬鼠標的點擊。發(fā)送消息用到了WinAPI函數(shù)PostMessage,它和大家所熟悉的SendMessage函數(shù)的參數(shù)是相同的,作用也幾乎相同,主要區(qū)別是不等待消息的返回,詳見MSDN。上述四個消息的WParam和LParam都具有同樣的意義:WParam用來指定按下該鍵的時候,還有哪些其它特定的鍵(其它鼠標鍵,或Ctrl,Shift等)被按下。0表示沒有其它鍵被按下,在這里還用到了值MK_RBUTTON,即按下左鍵時指定右鍵同時也被按下,用來模擬同時按下左右鍵的情況。當(dāng)然,發(fā)送RBUTTONDOWN和RBUTTONUP時指定MK_LBUTTON也是同樣的效果。而LParam則指定了按下或抬起鍵時鼠標指針的坐標,它的高16位為Y坐標,低16位為X坐標。給lparam賦值的那一行,就是把X,Y兩個值組裝成了一個lparam。
另外提一下IfThen函數(shù),這個函數(shù)平時并不見有多人使用,但它真的很方便,至少它解決了對于“Delphi中沒有C的 ? : 運算符”的抱怨^_^。不錯,這就是Delphi的問號運算符,三個參數(shù)中第一個是Boolean型,若為真,返回值就是第二個參數(shù),否則是第三個參數(shù)。第二、第三個參數(shù)類型相同,并且有多個重載版本。數(shù)值版本需要包含Math庫,而字符串版本需要StrUtils庫。顯然,編譯上述代碼是需要包含Math庫的。若不愿,當(dāng)然你也可以使用 if...then。
至此,接口層全部實現(xiàn)完畢,接下來就可以安心的實現(xiàn)“數(shù)學(xué)模型”了。
新聞熱點
疑難解答