home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast.iso
/
pcmag
/
vol11n01.zip
/
LN1101.ZIP
/
TICTACTO.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1991-12-10
|
13KB
|
397 lines
PROGRAM TicTacTo;
Uses WinTypes, WinProcs, WObjects;
{$R TicTacTo}
{$D Copyright (c) 1991 by Neil J. Rubenking}
CONST
AppName : PChar = 'TicTacTo';
cm_CCs = 102;
cm_CPs = 103;
cm_PXs = 104;
cm_POs = 105;
cm_Help = 106;
Xv = 1; X2 = 2*Xv;
Ov = 4; O2 = 2*Ov;
Draw = 255;
TYPE
TMyApplication = object(TApplication)
PROCEDURE InitMainWindow; virtual;
END;
PTicWindow = ^TTicWindow;
TTicWindow = OBJECT(TWindow)
Rects : ARRAY[0..8] OF TRect;
Plays : ARRAY[0..9] OF Byte;
IsX, UseComp : Boolean;
Moves, PenWid : Word;
CONSTRUCTOR Init(AParent : PWindowsObject; AName : PChar);
FUNCTION GetClassName : PChar; Virtual;
PROCEDURE GetWindowClass(var AWndClass: TWndClass); Virtual;
PROCEDURE Paint(pDC : hDC; VAR PS : TPaintStruct); Virtual;
PROCEDURE NewGame(XStart, vsComp : Boolean);
PROCEDURE PlayAndCheck(NewSpot : Word);
PROCEDURE ProgPlay;
PROCEDURE wmLButtonDown(VAR Msg : TMessage);
Virtual wm_First + wm_LButtonDown;
PROCEDURE WMKeyDown(VAR Msg : TMessage);
Virtual wm_First + wm_KeyDown;
PROCEDURE wmNCHitTest(VAR Msg : TMessage);
Virtual wm_First + wm_NCHitTest;
PROCEDURE DefCommandProc(VAR Msg : TMessage); Virtual;
END;
{--------------------------------------------------}
{ TTicWindow's methods }
{--------------------------------------------------}
CONSTRUCTOR TTicWindow.Init(AParent : PWindowsObject; AName : PChar);
BEGIN
TWindow.Init(AParent, AName);
Attr.Menu := LoadMenu(hInstance, AppName);
NewGame(TRUE, TRUE);
Randomize;
END;
FUNCTION TTicWindow.GetClassName;
BEGIN GetClassName := AppName; END;
PROCEDURE TTicWindow.GetWindowClass(VAR AWndClass : TWndClass);
BEGIN
TWindow.GetWindowClass(AWndClass);
AWndClass.hIcon := LoadIcon(HInstance, AppName);
AWndClass.hCursor := 0;
END;
{x$DEFINE ShowSlow}
PROCEDURE TTicWindow.Paint(pDC : hDC; VAR PS : TPaintStruct);
VAR X, Y, X3, Y3, X16, Y16, N : Integer;
hp, oldp : hPen;
TR : TRect;
CONST Blue = $00FF0000;
Red = $000000FF;
PROCEDURE OneSquare(N : Integer);
{$IFDEF ShowSlow}
VAR Slow : LongInt;
{100000 is for 486/33 - reduce for slower machines}
CONST Factor = 100000;
{$ENDIF}
BEGIN
{$IFDEF ShowSlow}
FOR Slow := 1 to Factor DO N := N;
{$ENDIF}
CASE Plays[N] OF
Xv: BEGIN
hp := CreatePen(ps_Solid, PenWid, Red);
oldp := SelectObject(pDC, hp);
WITH Rects[N] DO
BEGIN
MoveTo(pDC, Left, Top); LineTo(pDC, Right, Bottom);
MoveTo(pDC, Right, Top); LineTo(pDC, Left, Bottom);
END;
SelectObject(pDC, OldP);
DeleteObject(hP);
END;
Ov: BEGIN
hp := CreatePen(ps_Solid, PenWid, blue);
oldp := SelectObject(pDC, hp);
WITH Rects[N] DO Ellipse(pDC, Left, Top, RIght, Bottom);
SelectObject(pDC, OldP);
DeleteObject(hP);
END;
END;
END;
BEGIN
GetClientRect(hWindow, TR);
X := TR.Right; Y := TR.Bottom;
X3 := X DIV 3; Y3 := Y DIV 3;
X16 := X DIV 16; Y16 := Y DIV 16;
IF X16 < Y16 THEN PenWid := 2*X16 DIV 3
ELSE PenWid := 2*Y16 DIV 3;
IF EqualRect(TR, PS.rcPaint) THEN {paint whole window}
BEGIN
{draw the # diagram}
hp := CreatePen(ps_Solid, PenWid, 0);
oldp := SelectObject(pDC, hp);
MoveTo(pDC, X3, Y16); LineTo(pDC, X3, Y-Y16);
MoveTo(pDC, 2*X3, Y16); LineTo(pDC, 2*X3, Y-Y16);
MoveTo(pDC, X16, Y3); LineTo(pDC, X-X16, Y3);
MoveTo(pDC, X16, 2*Y3); LineTo(pDC, X-X16, 2*Y3);
SelectObject(pDC, OldP);
DeleteObject(hP);
{establish the "control" rectangles}
FOR N := 0 to 8 DO
BEGIN
SetRect(Rects[N], (N MOD 3)*X3, (N DIV 3)*Y3,
Succ(N MOD 3)*X3, Succ(N DIV 3)*Y3);
InflateRect(Rects[N], -X16, -Y16);
END;
{draw the X's and O's}
FOR N := 0 to 8 DO OneSquare(N);
END
ELSE {just paint the necessary areas}
BEGIN
{paint squares that need it}
FOR N := 0 to 8 DO
IF IntersectRect(TR,Rects[N],PS.rcPaint)<>0 THEN
OneSquare(N);
{paint lines of the # diagram that need it}
hp := CreatePen(ps_Solid, PenWid, 0);
oldp := SelectObject(pDC, hp);
SetRect(TR, X3-X16, Y16, X3+X16, Y-Y16);
IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
BEGIN
MoveTo(pDC, X3, Y16);
LineTo(pDC, X3, Y-Y16);
END;
SetRect(TR, 2*X3-X16, Y16, 2*X3+X16, Y-Y16);
IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
BEGIN
MoveTo(pDC, 2*X3, Y16);
LineTo(pDC, 2*X3, Y-Y16);
END;
SetRect(TR, X16, Y3-Y16, X-X16, Y3+Y16);
IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
BEGIN
MoveTo(pDC, X16, Y3);
LineTo(pDC, X-X16, Y3);
END;
SetRect(TR, X16, 2*Y3-Y16, X-X16, 2*Y3+Y16);
IF IntersectRect(TR, TR, PS.rcPaint) <> 0 THEN
BEGIN
MoveTo(pDC, X16, 2*Y3);
LineTo(pDC, X-X16, 2*Y3);
END;
SelectObject(pDC, OldP);
DeleteObject(hP);
END;
END;
PROCEDURE TTicWindow.NewGame(XStart, vsComp : Boolean);
BEGIN
IsX := XStart; UseComp := vsComp;
FillChar(Plays, SizeOf(Plays), 0);
Plays[9] := 127; Moves := 0;
InvalidateRect(hWindow, NIL, TRUE);
IF UseCOMP AND (NOT IsX) THEN ProgPlay;
END;
PROCEDURE TTicWindow.PlayAndCheck(NewSpot : Word);
VAR TR : TRect;
FUNCTION Won : Byte;
VAR N : Word;
BEGIN
FOR N := 0 to 2 DO
CASE Plays[N*3+0] + Plays[N*3+1] + Plays[N*3+2] OF
3*Xv: BEGIN Won := Xv; Exit; END;
3*Ov: BEGIN Won := Ov; Exit; END;
END;
FOR N := 0 to 2 DO
CASE Plays[N+0] + Plays[N+3] + Plays[N+6] OF
3*Xv: BEGIN Won := Xv; Exit; END;
3*Ov: BEGIN Won := Ov; Exit; END;
END;
CASE Plays[0] + Plays[4] + Plays[8] OF
3*Xv: BEGIN Won := Xv; Exit; END;
3*Ov: BEGIN Won := Ov; Exit; END;
END;
CASE Plays[2] + Plays[4] + Plays[6] OF
3*Xv: BEGIN Won := Xv; Exit; END;
3*Ov: BEGIN Won := Ov; Exit; END;
END;
IF Moves = 9 THEN
BEGIN Won := Draw; Exit; END;
Won := 0;
END;
BEGIN
IF Plays[NewSpot] <> 0 THEN
BEGIN MessageBeep(0); Exit; END;
IF IsX THEN Plays[NewSpot] := Xv ELSE Plays[NewSpot] := Ov;
Inc(Moves);
IsX := NOT IsX;
TR := Rects[NewSpot];
InflateRect(TR, PenWid, PenWid);
InvalidateRect(hWindow, @Tr, FALSE);
CASE Won OF
Xv : BEGIN
MessageBox(hWindow,'X wins!','A WINNER!', mb_Ok);
NewGame(IsX XOR Odd(Moves), UseComp);
END;
Ov : BEGIN
MessageBox(hWindow,'O wins!','A WINNER!', mb_Ok);
NewGame(IsX XOR Odd(Moves), UseComp);
END;
Draw : BEGIN
MessageBox(hWindow,'A Draw!','NO WINNER!', mb_Ok);
NewGame(NOT IsX, UseComp);
END;
ELSE IF UseCOMP AND (NOT IsX) THEN ProgPlay;
END;
END;
PROCEDURE TTicWIndow.ProgPlay;
VAR spot : Word;
TR : TRect;
CONST Corners : ARRAY[0..3] OF Byte = (0, 2, 6, 8);
FUNCTION RateThem : Word;
{NEVER called 'til after middle square (#4) is used}
VAR N, Best, BestRate, a1, a2, d1, d2,
g1, g2, ac, dn, dg : Word;
Ratings : ARRAY[0..8] OF Byte;
PROCEDURE UpdateBest(Num, Value : Word);
BEGIN
Ratings[Num] := Value;
IF Value > BestRate THEN
BEGIN BestRate := Value; Best := Num; END;
END;
BEGIN
Best := 0; BestRate := 0;
FOR N := 0 to 8 DO
BEGIN
IF Plays[N] <> 0 THEN Ratings[N] := 0
ELSE
BEGIN
a1 := (N DIV 3) * 3; a2 := succ(a1);
IF a1 = N THEN Inc(a1, 2);
IF a2 = N THEN Inc(a2);
d1 := N MOD 3; d2 := d1 + 3;
IF d1 = N THEN Inc(D1, 6);
IF d2 = N THEN Inc(D2, 3);
g1 := 4;
IF Odd(N) THEN
BEGIN g1 := 9; g2 := 9; END
ELSE
CASE N OF
0 : g2 := 8;
2 : g2 := 6;
6 : g2 := 2;
8 : g2 := 0;
END;
ac := Plays[a1] + Plays[a2];
dn := Plays[d1] + Plays[d2];
dg := Plays[g1] + Plays[g2];
IF (ac=O2) OR (dn=O2) OR (dg=O2) THEN
UpdateBest(N, 5)
ELSE IF (ac=X2) OR (dn=X2) OR (dg=X2) THEN
UpdateBest(N, 4)
ELSE IF (ac+dn=O2) OR (ac+dg=O2) OR (dn+dg=O2) THEN
UpdateBest(N, 3)
ELSE IF (ac=Ov) OR (dn=Ov) OR (dg=Ov) THEN
UpdateBest(N, 2)
ELSE UpdateBest(N, 1);
END;
END;
RateThem := Best;
END;
BEGIN
CASE Moves OF
0 : Spot := 4;
1 : BEGIN
IF Plays[4] = 0 THEN Spot := 4
ELSE Spot := Corners[Random(4)];
END;
ELSE Spot := RateThem;
END;
PlayAndCheck(Spot);
END;
PROCEDURE TTicWindow.WmLButtonDown(VAR Msg : TMessage);
VAR N : Word;
BEGIN
N := 0;
{determine if the mouse is in any of our rectangles}
WHILE (N < 9) AND (NOT PtInRect(Rects[N], TPoint(Msg.LParam))) DO
Inc(N);
IF N < 9 THEN PlayAndCheck(N);
END;
PROCEDURE TTicWindow.WMKeyDown(VAR Msg : TMessage);
VAR T : TPoint;
N : Integer;
BEGIN
GetCursorPos(T);
ScreenToClient(hWindow, T);
N := 0;
{determine if the mouse is in any of our rectangles}
WHILE (N < 9) AND (NOT PtInRect(Rects[N], T)) DO Inc(N);
IF N = 9 THEN N := 0
ELSE
CASE Msg.wParam OF
vk_Tab : N := Succ(N) MOD 9;
vk_Right : IF (N MOD 3) = 2 THEN Dec(N,2) ELSE Inc(N);
vk_Left : IF (N MOD 3) = 0 THEN Inc(N,2) ELSE Dec(N);
vk_Down : IF (N DIV 3) = 2 THEN Dec(N, 6) ELSE Inc(N, 3);
vk_Up : IF (N DIV 3) = 0 THEN Inc(N, 6) ELSE Dec(N, 3);
vk_Space,
vk_Return: PlayAndCheck(N);
END;
WITH Rects[N] DO
BEGIN
T.X := (Right + Left) DIV 2;
T.Y := (Bottom + Top) DIV 2;
END;
ClientToScreen(hWindow, T);
SetCursorPos(T.X, T.Y);
DefWndProc(Msg);
END;
PROCEDURE TTicWindow.WmNCHitTest(VAR Msg : TMessage);
VAR N : Word;
Pt : TPoint;
CurA : hCursor;
BEGIN
Move(Msg.Lparam, Pt, 4);
ScreenToClient(hWindow, Pt);
N := 0;
WHILE (N < 9) AND (NOT PtInRect(Rects[N], Pt)) DO
Inc(N);
IF N < 9 THEN
BEGIN
IF Plays[N] <> 0 THEN CurA := LoadCursor(hInstance, 'CurNO')
ELSE IF IsX THEN CurA := LoadCursor(hInstance, 'CurX')
ELSE CurA := LoadCursor(hInstance, 'CurO');
END
ELSE CurA := LoadCursor(0, idc_Arrow);
SetCursor(CurA);
DefWndProc(Msg);
END;
PROCEDURE TTicWindow.DefCommandProc(VAR Msg : TMessage);
VAR PD : PDialog;
BEGIN
IF Msg.WParamHi = 0 THEN
CASE Msg.WParamLo OF
cm_CCs : NewGame(FALSE, TRUE);
cm_CPs : NewGame(TRUE, TRUE);
cm_PXs : NewGame(TRUE, FALSE);
cm_POs : NewGame(FALSE, FALSE);
cm_Help : BEGIN
New(PD, Init(@Self, 'TicHelp'));
Application^.ExecDialog(PD);
END;
ELSE TWindow.DefCommandProc(Msg);
END;
END;
{--------------------------------------------------}
{ TMyApplication's method implementations: }
{--------------------------------------------------}
PROCEDURE TMyApplication.InitMainWindow;
BEGIN MainWindow := New(PTicWindow, Init(Nil, AppName)); END;
{--------------------------------------------------}
{ Main program: }
{--------------------------------------------------}
VAR MyApp: TMyApplication;
BEGIN
MyApp.Init(AppName);
MyApp.Run;
MyApp.Done;
END.