=================================================================
//取得整個雷區每個方塊的狀態,填入Cells中供分析。
procedure FetchCells;
var
i, j: Integer;
begin
//掃描每個方塊,根據指定像素的顏色判斷該方塊的性質。
//特定像素的顏色與方塊性質的對應關系歸納自“掃雷”程序本身的資源。
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; //空白,相當於數字0
clBlue: Cells[i, j] := cs1; //數字1
clGreen: Cells[i, j] := cs2; //數字2
clRed: Cells[i, j] := cs3; //數字3
clNavy: Cells[i, j] := cs4; //數字4
clMaroon: Cells[i, j] := cs5; //數字5
clTeal: Cells[i, j] := cs6; //數字6
clBlack: Cells[i, j] := cs7; //數字7
clGray: Cells[i, j] := cs8; //數字8
end;
end;
end;
=================================================================
以上程序中,三個包含 case...of 的行,都用到了前面實測到的數據。執行完之後,掃雷窗口所有方塊的狀態就原原本本地在輸入緩沖區裡了。
=================================================================
//將Operations中所記載的對每個方塊的操作真正作用於掃雷窗口。
procedure OperateCells;
var
i, j: Integer;
downMsg, upMsg: Cardinal; //按下和抬起鼠標按鈕時分別發送的消息
wparam, lparam: Integer; //消息參數
clickCount: Integer; //按鍵次數,只有取值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;
//根據操作種類,設定發送的消息及其參數
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);
//設定發送消息次數,即單擊還是雙擊
clickCount := IfThen(Operations[i, j] = opRightDoubleClick, 2, 1);
//發送消息
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四個消息。它們是在一個窗口客戶區內按下或抬起鼠標左或右按鈕時,發給這個窗口的消息。所以,手動地發送這些消息,其實就是模擬鼠標的點擊。發送消息用到了WinAPI函數PostMessage,它和大家所熟悉的SendMessage函數的參數是相同的,作用也幾乎相同,主要區別是不等待消息的返回,詳見MSDN。上述四個消息的WParam和LParam都具有同樣的意義:WParam用來指定按下該鍵的時候,還有哪些其它特定的鍵(其它鼠標鍵,或Ctrl,Shift等)被按下。0表示沒有其它鍵被按下,在這裡還用到了值MK_RBUTTON,即按下左鍵時指定右鍵同時也被按下,用來模擬同時按下左右鍵的情況。當然,發送RBUTTONDOWN和RBUTTONUP時指定MK_LBUTTON也是同樣的效果。而LParam則指定了按下或抬起鍵時鼠標指針的坐標,它的高16位為Y坐標,低16位為X坐標。給lparam賦值的那一行,就是把X,Y兩個值組裝成了一個lparam。
另外提一下IfThen函數,這個函數平時並不見有多人使用,但它真的很方便,至少它解決了對於“Delphi中沒有C的 ? : 運算符”的抱怨^_^。不錯,這就是Delphi的問號運算符,三個參數中第一個是Boolean型,若為真,返回值就是第二個參數,否則是第三個參數。第二、第三個參數類型相同,並且有多個重載版本。數值版本需要包含Math庫,而字符串版本需要StrUtils庫。顯然,編譯上述代碼是需要包含Math庫的。若不願,當然你也可以使用 if...then。
至此,接口層全部實現完畢,接下來就可以安心的實現“數學模型”了。