home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CICA 1992 November
/
CICA_MS_Windows_CD-ROM_Walnut_Creek_November_1992.iso
/
win3
/
games
/
minehlp1
/
minehelp.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1992-04-26
|
20KB
|
630 lines
PROGRAM MineHelp;
{Version 1.0
Peter Karrer
April 26, 1992}
{$M 40960,8192}
{$G+}
USES WObjects, WinTypes, WinProcs, Strings;
{$R MINEHELP.RES}
CONST
appName: PCHAR = 'MineHelp';
{Child control IDs}
inactive = 103;
active = 104;
automatic = 105;
basic = 106;
expert = 107;
rand = 108;
id_Animation = 110;
id_OK = 109;
white = $ffffff;
{colors masked with $ffc0c0c0}
blue = $c00000;
dblue = $800000;
red = $0000c0;
dred = $000080;
dgreen= $008000;
dcyan = $808000;
black = 0;
dgray = $808080;
gray = $c0c0c0;
xOff = -4; { width of left border in Minesweeper window client area - 16}
yOff = 39; { width of top border in Minesweeper window client area - 16}
TYPE
TThisApp = OBJECT(TApplication)
PROCEDURE InitMainWindow; VIRTUAL;
END;
PThisWindow = ^TThisWindow;
TThisWindow = OBJECT(TDlgWindow)
stat: INTEGER; {id of checked "Status" button}
lev: INTEGER; {id of checked "Level" button}
animation: BOOLEAN;
msWin: HWnd;
mswX, mswY: INTEGER;
dimX, dimY: INTEGER;
busy: BOOLEAN;
CONSTRUCTOR Init;
FUNCTION GetClassName: PCHAR; VIRTUAL;
PROCEDURE GetWindowClass(VAR c: TWndClass); VIRTUAL;
PROCEDURE SetupWindow; VIRTUAL;
PROCEDURE DefChildProc(VAR msg: TMessage); VIRTUAL;
PROCEDURE WMDestroy(VAR msg: TMessage); VIRTUAL wm_first + wm_Destroy;
PROCEDURE WMTimer(VAR msg: TMessage); VIRTUAL wm_first + wm_Timer;
PROCEDURE DoIt;
FUNCTION GetMsWin: HWnd;
PROCEDURE GetBoard(VAR bomb: BOOLEAN);
PROCEDURE Click(x, y: INTEGER; btnDown, btnUp, modifier: WORD);
PROCEDURE Mark(x, y: INTEGER);
PROCEDURE ClearFields(VAR somethingDone: BOOLEAN);
PROCEDURE MarkFields(VAR somethingDone: BOOLEAN);
FUNCTION TwoFieldSearch(x1, y1, x2, y2: INTEGER): BOOLEAN;
PROCEDURE TwoFields(VAR success: BOOLEAN);
PROCEDURE ClearRandom(VAR somethingHappened: BOOLEAN);
END;
VAR
thisApp: TThisApp;
bb: ARRAY [0..25, 0..31] OF INTEGER;
ee: ARRAY [0..25, 0..31] OF INTEGER;
CONSTRUCTOR TThisWindow.Init;
BEGIN
TDlgWindow.Init(NIL, appName);
END;
FUNCTION TThisWindow.GetClassName;
BEGIN
GetClassName := appName;
END;
PROCEDURE TThisWindow.GetWindowClass(VAR c: TWndClass);
BEGIN
TDlgWindow.GetWindowClass(c);
{c.hIcon := LoadIcon(hInstance, appName); doesn't work?!}
END;
PROCEDURE TThisWindow.SetupWindow;
VAR
i: INTEGER;
BEGIN
TDlgWindow.SetupWindow;
IF SetTimer(hWindow, 1, 1000, NIL) = 0 THEN BEGIN
MessageBox(HWindow, 'Sorry, no timers', NIL, mb_Ok);
Destroy;
END;
{Setting the icon didn't work in GetWindowClass, dunno why}
SetClassWord(hWindow, GCW_HICON, LoadIcon(hInstance, appName));
animation := POS('n', ParamStr(1)) <> 0;
IF POS('h', ParamStr(1)) <> 0 THEN BEGIN
cmdShow := sw_Hide;
END ELSE IF POS('c', ParamStr(1)) <> 0 THEN BEGIN
cmdShow := sw_Minimize;
END;
IF POS('a', ParamStr(1)) <> 0 THEN BEGIN
stat := active;
END ELSE IF POS('i', ParamStr(1)) <> 0 THEN BEGIN
stat := inactive;
END ELSE BEGIN
stat := automatic;
END;
IF POS('b', ParamStr(1)) <> 0 THEN BEGIN
lev := basic;
END ELSE IF POS('r', ParamStr(1)) <> 0 THEN BEGIN
lev := rand;
END ELSE BEGIN
lev := expert;
END;
SendDlgItemMsg(stat, bm_SetCheck, 1, 0);
SendDlgItemMsg(lev, bm_SetCheck, 1, 0);
SendDlgItemMsg(id_animation, bm_SetCheck, ORD(animation), 0);
RANDOMIZE;
busy := FALSE;
END;
PROCEDURE TThisWindow.Click(x, y: INTEGER; btnDown, btnUp, modifier: WORD);
BEGIN
IF animation THEN BEGIN
SetCursorPos(mswX + xOff + 16*x + 8, mswY + yOff + 16*y + 8);
END;
SendMessage(msWin, btnDown, modifier, MakeLong(xOff + 16*x, yOff + 16*y));
SendMessage(msWin, btnUp, modifier, MakeLong(xOff + 16*x, yOff + 16*y));
END; {Click}
PROCEDURE TThisWindow.GetBoard(VAR bomb: BOOLEAN);
{Examine the Minesweeper window client area. Get the contents of the
individual squares by reading pixels at strategic locations. Colors
are masked with $FFC0C0C0, because not all display drivers use the same
intensities for colors like dark cyan or dark red}
VAR
x, y, v: INTEGER;
rgb: LONGINT;
msDC: HDC;
BEGIN
bomb := FALSE;
msDC := GetDC(msWin);
FOR y := 1 TO dimY DO BEGIN
FOR x := 1 TO dimX DO BEGIN
rgb := GetPixel(msDC, xOff + 9 + 16*x, yOff + 12 + 16*y) AND $ffc0c0c0;
IF rgb = blue THEN BEGIN
bb[y, x] := 1;
END ELSE IF rgb = dgreen THEN BEGIN
bb[y, x] := 2;
END ELSE IF rgb = red THEN BEGIN
bb[y, x] := 3;
END ELSE IF rgb = dblue THEN BEGIN
bb[y, x] := 4;
END ELSE IF rgb = dred THEN BEGIN
bb[y, x] := 5;
END ELSE IF rgb = dcyan THEN BEGIN
bb[y, x] := 6;
END ELSE IF rgb = black THEN BEGIN
rgb := GetPixel(msDC, xOff + 7 + 16*x, yOff + 6 + 16*y);
IF rgb = white THEN BEGIN
bb[y, x] := -2; bomb := TRUE; {mine}
END ELSE BEGIN
rgb := rgb AND $ffc0c0c0;
IF rgb = gray THEN BEGIN
bb[y, x] := 7;
END ELSE IF rgb = red THEN BEGIN
bb[y, x] := 128; {flag}
END ELSE IF rgb = black THEN BEGIN
bb[y, x] := 2049; {question mark}
END ELSE BEGIN
bb[y, x] := -999; bomb := TRUE; {invisible}
END;
END;
END ELSE IF rgb = dgray THEN BEGIN
bb[y, x] := 8;
END ELSE IF rgb = gray THEN BEGIN
rgb := GetPixel(msDC, xOff + 15 + 16*x, yOff + 1 +16*y) AND $ffc0c0c0;
IF rgb = gray THEN BEGIN
bb[y, x] := 0;
END ELSE IF rgb = dgray THEN BEGIN
rgb := GetPixel(msDC, xOff + 5 + 16*x, yOff + 5 +16*y) AND $ffc0c0c0;
IF rgb = black THEN BEGIN
bb[y,x] := 2049; {question mark}
END ELSE IF rgb = gray THEN BEGIN
bb[y, x] := 2048; {covered}
END ELSE BEGIN
bb[y, x] := -999; bomb := TRUE;
END;
END ELSE BEGIN
bb[y, x] := -999; bomb := TRUE; {invisible}
END;
END ELSE BEGIN
bb[y, x] := -999; bomb := TRUE; {invisible}
END;
END; {FOR x}
END; {FOR y}
ReleaseDC(msWin, msDC);
IF NOT bomb THEN BEGIN
FOR y := 1 TO dimY DO BEGIN
FOR x := 1 TO dimX DO BEGIN
v := bb[y, x];
IF (v > 0) AND (v <= 8) THEN BEGIN
ee[y, x] := bb[y-1,x-1]+bb[y-1,x]+bb[y-1,x+1]+bb[y,x-1]+
bb[y,x+1]+bb[y+1,x-1]+bb[y+1,x]+bb[y+1,x+1];
END ELSE BEGIN
ee[y, x] := 0;
END;
END; {FOR x}
END; {FOR y}
END; {NOT bomb}
END; {GetBoard}
FUNCTION TThisWindow.GetMsWin: HWnd;
{Find the Minesweeper window and its location on the screen}
VAR
w, mW: HWnd;
st: ARRAY[0..32] OF CHAR;
rp: RECORD
CASE INTEGER OF 1: (r: TRect);
2: (p: TPoint);
END;
i: INTEGER;
BEGIN
w := 0;
mW := 0;
w := GetWindow(hWindow, gw_HWndFirst);
WHILE (w <> 0) AND (mW = 0) DO BEGIN
GetWindowText(w, st, 32);
IF StrComp(st, 'Minesweeper') = 0 THEN BEGIN
mW := w;
GetClientRect(mW, rp.r);
dimX := (rp.r.right - 24) DIV 16;
dimY := (rp.r.bottom - 67) DIV 16;
ClientToScreen(mW, rp.p);
mswX := rp.p.x;
mswY := rp.p.y;
END;
w := GetNextWindow(w, gw_HWndNext);
END;
IF mW <> 0 THEN BEGIN
FOR i := 0 TO dimX + 1 DO BEGIN
bb[0, i] := 0;
ee[0, i] := 0;
bb[dimY + 1, i] := 0;
ee[dimY + 1, i] := 0;
END;
FOR i:= 1 TO dimY DO BEGIN
bb[i, 0] := 0;
ee[i, 0] := 0;
bb[i, dimX + 1] := 0;
ee[i, dimX + 1] := 0;
END;
END;
GetMsWin := mW;
END; {GetMsWin}
PROCEDURE TThisWindow.ClearFields(VAR somethingDone: BOOLEAN);
VAR
x, y, v, c: INTEGER;
BEGIN
somethingDone := FALSE;
FOR y := 1 TO dimY DO BEGIN
FOR x := 1 TO dimX DO BEGIN
v := bb[y, x];
IF (v > 0) AND (v <= 8) THEN BEGIN
c := ee[y, x];
IF c >= 2048 THEN BEGIN {at least 1 covered field}
c := c AND 2047 SHR 7; {number of flagged fields}
IF v = c THEN BEGIN
Click(x, y, wm_LButtonDown, wm_LButtonUp, mk_RButton);
somethingDone := TRUE;
IF stat <> automatic THEN BEGIN
EXIT;
END;
END;
END;
END; {IF (v > 0) ..}
END; {FOR x}
END; {FOR y}
END; {ClearFields}
PROCEDURE TThisWindow.Mark(x, y: INTEGER);
BEGIN
Click(x, y, wm_RButtonDown, wm_RButtonUp, 0);
IF bb[y, x] = 2049 THEN BEGIN {question mark}
Click(x, y, wm_RButtonDown, wm_RButtonUp, 0);
END;
bb[y, x] := 128; {make it flagged}
END; {Mark}
PROCEDURE TThisWindow.MarkFields(VAR somethingDone: BOOLEAN);
VAR
x, y, v, c, f: INTEGER;
BEGIN
somethingDone := FALSE;
FOR y := 1 TO dimY DO BEGIN
FOR x := 1 TO dimX DO BEGIN
v := bb[y, x];
IF (v > 0) AND (v <= 8) THEN BEGIN
c := bb[y-1,x-1]+bb[y-1,x]+bb[y-1,x+1]+bb[y,x-1]+
bb[y,x+1]+bb[y+1,x-1]+bb[y+1,x]+bb[y+1,x+1];
f := c SHR 11; {number of covered fields}
IF f <> 0 THEN BEGIN
c := c AND 2047 SHR 7; {number of flagged fields}
IF (f + c) = v THEN BEGIN
IF bb[y-1,x-1] >= 2048 THEN BEGIN Mark(x-1,y-1); END;
IF bb[y-1,x ] >= 2048 THEN BEGIN Mark(x, y-1); END;
IF bb[y-1,x+1] >= 2048 THEN BEGIN Mark(x+1,y-1); END;
IF bb[y ,x-1] >= 2048 THEN BEGIN Mark(x-1,y ); END;
IF bb[y, x+1] >= 2048 THEN BEGIN Mark(x+1,y ); END;
IF bb[y+1,x-1] >= 2048 THEN BEGIN Mark(x-1,y+1); END;
IF bb[y+1,x ] >= 2048 THEN BEGIN Mark(x, y+1); END;
IF bb[y+1,x+1] >= 2048 THEN BEGIN Mark(x+1,y+1); END;
somethingDone := TRUE;
IF stat <> automatic THEN BEGIN
EXIT;
END;
END;
END;
END; {IF (v > 0) ..}
END; {FOR x}
END; {FOR y}
END; {MarkFields}
FUNCTION TThisWindow.TwoFieldSearch(x1, y1, x2, y2: INTEGER): BOOLEAN;
VAR
a, b, c, x, y, na, nb: INTEGER;
PROCEDURE ClickFields(xx1, yy1, xx2, yy2: INTEGER; marks: BOOLEAN);
{Click on covered fields in environment of (x1,y1) but not of (x2,y2)}
VAR
xx, yy, dbg: INTEGER;
BEGIN
FOR yy := yy1 - 1 TO yy1 + 1 DO BEGIN
FOR xx := xx1 - 1 TO xx1 + 1 DO BEGIN
IF ((ABS(yy-yy2) > 1) OR (ABS(xx-xx2) > 1)) AND (bb[yy,xx] >= 2048) THEN BEGIN
IF marks THEN BEGIN
Mark(xx, yy);
END ELSE BEGIN
Click(xx, yy, wm_LButtonDown, wm_LButtonUp, 0);
bb[yy, xx] := 0; {meaning uncovered with unknown value}
END;
END;
END; {FOR xx}
END; {FOR yy}
TwoFieldSearch := TRUE;
END; {ClickFields}
BEGIN {TwoFieldSearch}
TwoFieldSearch := FALSE;
c := ee[y1, x1];
x := bb[y1, x1] - c AND 2047 SHR 7; {Number of unknown mines around A=(x1,y1)}
a := c SHR 11; {Number of covered fields around A=(x1,y1)}
c := ee[y2, x2];
y := bb[y2, x2] - c AND 2047 SHR 7; {Number of unknown mines around B=(x2,y2)}
b := c SHR 11; {Number of covered fields around B=(x2,y2)}
c := 0;
IF (ABS(y1+1-y2) <=1) AND (ABS(x1-x2) <= 1) AND (bb[y1+1,x1] >= 2048) THEN c := c + 1;
IF (ABS(y1+1-y2) <=1) AND (ABS(x1+1-x2)<=1) AND (bb[y1+1,x1+1]>=2048) THEN c := c + 1;
IF (ABS(y1+1-y2) <=1) AND (ABS(x1-1-x2)<=1) AND (bb[y1+1,x1-1]>=2048) THEN c := c + 1;
IF (ABS(y1-y2) <= 1) AND (ABS(x1+1-x2)<= 1) AND (bb[y1,x1+1] >= 2048) THEN c := c + 1;
IF (ABS(y1-y2) <= 1) AND (ABS(x1-1-x2)<= 1) AND (bb[y1,x1-1] >= 2048) THEN c := c + 1;
IF (ABS(y1-1-y2) <=1) AND (ABS(x1-x2) <= 1) AND (bb[y1-1,x1] >= 2048) THEN c := c + 1;
IF (ABS(y1-1-y2) <=1) AND (ABS(x1+1-x2)<=1) AND (bb[y1-1,x1+1]>=2048) THEN c := c + 1;
IF (ABS(y1-1-y2) <=1) AND (ABS(x1-1-x2)<=1) AND (bb[y1-1,x1-1]>=2048) THEN c := c + 1;
{c = number of covered fields common to the environments of A and B}
a := a - c;
b := b - c;
na := -1;
nb := -1;
IF a = 0 THEN BEGIN
na := 0;
END ELSE IF x + b = y THEN BEGIN
na := 0;
END ELSE IF x - a = y THEN BEGIN
na := a;
END ELSE IF b = 0 THEN BEGIN
na := x - y;
END;
IF na >= 0 THEN BEGIN
nb := y - x + na;
END ELSE IF b = 0 THEN BEGIN
nb := 0;
END ELSE IF y - b = x THEN BEGIN
nb := b;
END ELSE IF a = 0 THEN BEGIN
nb := y - x;
END;
IF nb >= 0 THEN BEGIN
na := x - y + nb;
END;
IF a <> 0 THEN BEGIN
IF na = 0 THEN BEGIN
{Clear all fields in env A but not env B}
ClickFields(x1, y1, x2, y2, FALSE);
END ELSE IF na = a THEN BEGIN
{Mark all those fields}
ClickFields(x1, y1, x2, y2, TRUE);
END;
END;
IF b <> 0 THEN BEGIN
IF (nb = 0) AND (b <> 0) THEN BEGIN
{Clear all fields in env B but not env A}
ClickFields(x2, y2, x1, y1, FALSE);
END ELSE IF nb = b THEN BEGIN
{Mark all those fields}
ClickFields(x2, y2, x1, y1, TRUE);
END;
END;
END; {TwoFieldSearch}
PROCEDURE TThisWindow.TwoFields(VAR success: BOOLEAN);
PROCEDURE S(x1, y1: INTEGER);
VAR
x, y, miny, maxy: INTEGER;
BEGIN
IF success AND (stat <> automatic) THEN BEGIN
EXIT;
END;
IF y1 >= 0 THEN BEGIN
miny := 1;
maxy := dimY - y1;
END ELSE BEGIN
miny := 1 - y1;
maxy := dimY;
END;
FOR y := miny TO maxy DO BEGIN
FOR x := 1 TO dimX - x1 DO BEGIN
IF (ee[y, x] >= 2048) AND (ee[y + y1, x + x1] >= 2048) THEN BEGIN
success := success OR TwoFieldSearch(x, y, x + x1, y + y1);
IF success AND (stat <> automatic) THEN BEGIN
EXIT;
END;
END;
END;
END;
END; {S}
BEGIN {TwoFields}
success := FALSE;
S(1, 0); S(0, -1); S(1, 1); S(1, -1); S(2, -1); S(2, 1);
S(1, -2); S(1, 2); S(2, 0); S(0, -2); S(2, -2); S(2, 2);
END; {TwoFields}
PROCEDURE TThisWindow.ClearRandom(VAR somethingHappened: BOOLEAN);
VAR
x, y, c, i: INTEGER;
bomb: BOOLEAN;
BEGIN
GetBoard(bomb);
somethingHappened := FALSE;
IF NOT bomb THEN BEGIN
c := 0;
FOR y := 1 TO dimY DO BEGIN
FOR x:= 1 TO dimX DO BEGIN
IF bb[y, x] >= 2048 THEN BEGIN
c := c + 1;
END;
END;
END;
IF c <> 0 THEN BEGIN
i := RANDOM(c);
c := 0;
FOR y := 1 TO dimY DO BEGIN
FOR x := 1 TO dimX DO BEGIN
IF bb[y, x] >= 2048 THEN BEGIN
IF c = i THEN BEGIN
Click(x, y, wm_LButtonDown, wm_LButtonUp, 0);
somethingHappened := TRUE;
EXIT;
END;
c := c + 1;
END;
END; {FOR x}
END; {FOR y}
END; {c <> 0}
END; {NOT bomb}
END; {ClearRandom}
PROCEDURE WaitIdle;
{It's impolite to hog the CPU}
VAR
m: TMsg;
BEGIN
WHILE PeekMessage(m, 0, 0, 0, pm_Remove) DO BEGIN
IF m.message = wm_Quit THEN BEGIN
HALT(m.wParam);
END;
TranslateMessage(m);
DispatchMessage(m);
END;
END;
PROCEDURE TThisWindow.DefChildProc(VAR msg: TMessage);
VAR
i: INTEGER;
BEGIN
WITH msg DO BEGIN
IF (lParamLo <> 0) AND (lParamHi <> 1) THEN BEGIN
{ not menu, not accelerator id }
IF wParam = inactive THEN BEGIN
stat := inactive;
END ELSE IF wParam = active THEN BEGIN
stat := active;
END ELSE IF wParam = automatic THEN BEGIN
stat := automatic;
END ELSE IF wParam = basic THEN BEGIN
lev := basic;
END ELSE IF wParam = expert THEN BEGIN
lev := expert;
END ELSE IF wParam = rand THEN BEGIN
lev := rand;
END ELSE IF wParam = id_Animation THEN BEGIN
animation := NOT animation;
SendDlgItemMsg(id_Animation, bm_SetCheck, ORD(animation), 0);
END ELSE IF wParam = id_OK THEN BEGIN
IF stat = active THEN BEGIN
DoIt;
END;
END;
END; {IF (lParamLo ..}
END; {WITH msg}
TDlgWindow.DefChildProc(msg);
END;
PROCEDURE TThisWindow.DoIt;
VAR
bomb, somethingHappened, action: BOOLEAN;
x, y: INTEGER;
m: TMsg;
BEGIN
IF busy THEN BEGIN
{avoid reentrancy}
EXIT;
END;
busy := TRUE;
msWin := GetMsWin;
IF msWin <> 0 THEN BEGIN
REPEAT
REPEAT
GetBoard(bomb);
action := FALSE;
somethingHappened := TRUE;
WHILE NOT bomb AND somethingHappened DO BEGIN
MarkFields(somethingHappened);
IF somethingHappened AND (stat <> automatic) THEN BEGIN
busy := FALSE;
EXIT;
END;
WaitIdle;
action := action OR somethingHappened;
{GetBoard(msWin, bomb);}
END;
somethingHappened := TRUE;
WHILE NOT bomb AND somethingHappened DO BEGIN
ClearFields(somethingHappened);
IF somethingHappened AND (stat <> automatic) THEN BEGIN
busy := FALSE;
EXIT;
END;
WaitIdle;
action := action OR somethingHappened;
GetBoard(bomb);
END;
{action = there were changes in mark and clear phases}
UNTIL NOT action OR bomb;
somethingHappened := lev > basic;
WHILE NOT bomb AND somethingHappened DO BEGIN
TwoFields(somethingHappened);
IF somethingHappened AND (stat <> automatic) THEN BEGIN
busy := FALSE;
EXIT;
END;
WaitIdle;
action := action OR somethingHappened;
GetBoard(bomb);
END;
IF (lev = rand) AND NOT action THEN BEGIN
ClearRandom(action);
IF stat <> automatic THEN BEGIN
busy := FALSE;
EXIT;
END;
END;
UNTIL NOT action OR bomb;
END; {msWin <> 0}
busy := FALSE;
END; {DoIt}
PROCEDURE TThisWindow.WMTimer(VAR msg: TMessage);
BEGIN
IF stat = automatic THEN BEGIN
DoIt;
END;
END;
PROCEDURE TThisWindow.WMDestroy(VAR msg: TMessage);
BEGIN
KillTimer(hWindow, 1);
TDlgWindow.WMDestroy(msg);
END;
PROCEDURE TThisApp.InitMainWindow;
begin
mainWindow := NEW(PThisWindow, Init);
end;
BEGIN
{$G-}
IF (GetWinFlags AND (wf_CPU086 OR wf_CPU186)) <> 0 THEN BEGIN
MessageBox(0, 'WinHelp needs a 286 or better', NIL, mb_OK);
HALT(0);
END;
{$G+}
thisApp.Init(appName);
thisApp.Run;
thisApp.Done;
END.