unit Unit1; { 3D tic tac toe events & control } interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons,shellapi; type TForm1 = class(TForm) PaintBox1: TPaintBox; msgtext: TStaticText; backBtn: TImage; Image2: TImage; analyseBtn: TImage; newBtn: TImage; procedure FormCreate(Sender: TObject); procedure PaintBox1Paint(Sender: TObject); procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure backBtnClick(Sender: TObject); procedure newBtnClick(Sender: TObject); procedure FormKeyPress(Sender: TObject; var Key: Char); procedure analyse1BTnClick(Sender: TObject); procedure Image2Click(Sender: TObject); procedure Image4Click(Sender: TObject); procedure Image1Click(Sender: TObject); procedure analyseBtnClick(Sender: TObject); private { Private declarations } public { Public declarations } end; procedure AnalyseReport(p,score : byte); var Form1: TForm1; implementation {$R *.dfm} uses unit2; type TPlane = record left : word; top : word; square : word; end; TPosTriple = record column : byte; row : byte; plane : byte; end; TGameStatus = (gsInitializing,gsAnalysing,gsMoving,gsEnd); TGameUpdate = (guMove,guWin,guAnalyseBtn,guNewBtn,guBackBtn); const planes : array[0..2] of TPlane = ((left : 20; top : 450; square : 80), (left : 280; top : 220; square : 70), (left : 510; top : 20; square : 60)); fw = 9; //frame width gameBGcolor = $feffff; markcolor = $00b0ff; chdef : array[1..2] of char = ('O','X'); playercolors : array[1..2] of dword = ($0000ff,$ff0000); procedure GameControl(gum : TGameUpdate);forward; procedure displaymover;forward; var selectfield : byte; gamestatus : TGamestatus; wintriple : Twintriple; Fwintriple : boolean; Fscores : boolean; scores : array[1..maxmove] of byte; //-- helpers procedure msgproc(s : string; colr : dword); begin with form1.msgtext do begin font.color := colr; caption := s; end; end; function playerColor(pl: byte) : dword; //return color of player; begin result := playercolors[pl]; end; procedure multiline(x1,y1,x2,y2: word; w : byte; colr : dword); //paint 5 fold line (x1,y1) --> (x2,y2) in paintbox1 var i : byte; begin with form1.PaintBox1.Canvas do begin pen.Width := 1; pen.Color := colr; for i := 0 to w-1 do begin moveto(x1+i,y1-i); lineto(x2+i,y2-i); end; end; end; procedure paintedges(im : Timage); var i : byte; begin with im.Picture.bitmap do with canvas do for i := 0 to 2 do begin pen.color := $606060; moveto(width-1-i,i); lineto(i,i); lineto(i,height-1-i); pen.color := $a0a0a0; lineto(width-1-i,height-1-i); lineto(width-1-i,i); end; end; function pos2triple(p : byte) : TPosTriple; //convert position p to column,row,plane begin with result do begin dec(p); column := p mod 3; p := p div 3; row := p mod 3; plane := p div 3; end; end; function triple2Pos(tr : TPosTriple) : byte; //convert column,row,plane to position begin with tr do result := column + 3*row + 9*plane + 1; end; function pos2Rect(p : byte) : Trect; //calculate rectangle of a field [0..26] var tr : Tpostriple; begin tr := pos2triple(p); with tr do with planes[tr.plane] do begin result.left := left + square*column; result.top := top + square*row; result.Right := result.Left + square; result.Bottom := result.Top + square; inc(result.Left,fw); dec(result.Bottom,fw); end; end; function xy2field(x,y : word) : byte; //convert mousex,y to field //no field = 0 var hit : boolean; r : Trect; i : byte; begin hit := false; i := 0; while (hit = false) and (i < maxmove) do begin inc(i); r := pos2rect(i); hit := (x > r.Left) and (x < r.Right) and (y > r.top ) and (y < r.Bottom); end;//while if hit then result := i else result := 0; end; procedure paintfield(nr : byte; bg : dword); var tr : TPosTriple; i,cc,w : byte; r : Trect; begin cc := game[nr]; r := pos2rect(nr); with form1.PaintBox1.Canvas do begin brush.style := bsSolid; brush.Color := bg; fillrect(r); if cc > 0 then begin tr := pos2Triple(nr); w := planes[tr.plane].square; r.Left := r.Left + (w div 4); brush.Style := bsClear; font.height := w-18; if cc = 1 then font.Color := $0000ff else font.color := $ff0000; for i := 0 to fw-1 do textout(r.left - i,r.top + i,chdef[cc]); end;//if cc end;//with end; procedure paintgame; var i : byte; begin for i := 1 to maxmove do paintfield(i,gameBGcolor); end; procedure paintframe; var i,j,k,sq,sq3 : byte; x,y,x1,y1 : word; begin for i := 0 to 2 do begin x1 := planes[i].left; y1 := planes[i].top; sq := planes[i].square; sq3 := sq*3; for j := 0 to 3 do multiline(x1,y1+sq*j,x1+sq3,y1+sq*j,fw,$c0c0c0); //hor for j := 0 to 3 do multiline(x1+sq*j,y1,x1+sq*j,y1+sq3,fw,$808080); //vert for j := 0 to 2 do begin y := y1 + sq*j; for k := 0 to 2 do begin x := x1+sq*k; multiline(x,y,x+fw,y,fw,$c0c0c0);//fix end; end; end; end; procedure markwinner; begin Fwintriple := true; with wintriple do begin paintfield(p1,markcolor); paintfield(p2,markcolor); paintfield(p3,markcolor); end; end; procedure unmarkwinner; begin if Fwintriple then with wintriple do begin Fwintriple := false; paintfield(p1,gameBGcolor); paintfield(p2,gameBGcolor); paintfield(p3,gameBGcolor); end; end; //-- call from analyser procedure analysereport(p,score : byte); //report analysis of move in position p var r : Trect; s : string; m : byte; colr : dword; begin Fscores := true; //scores painted scores[p] := score; m := 1 + (moveNr and 1); //next move colr := playercolor(m); with form1.PaintBox1.Canvas do begin font.Color := colr; font.Height := 18; brush.Style := bsClear; s := ''; if score > 50 then s := 'W' + inttostr(101-score); if (score > 0) and (score < 50) then s := 'L' + inttostr(score + 1); if score = 50 then s := '='; r := pos2rect(p); textout(r.Left,r.Top,s); end; application.ProcessMessages;//show data end; //-- events procedure TForm1.FormCreate(Sender: TObject); begin paintedges(newBtn); paintedges(backBtn); paintedges(analysebtn); gamestatus := gsInitializing; makewinlist; initgame; selectfield := 0; paintbox1.Cursor := crArrow; gamestatus := gsMoving; displaymover; end; procedure TForm1.PaintBox1Paint(Sender: TObject); var x1,y1,x2,y2 : word; i,w1,w2 : byte; begin with PaintBox1 do with canvas do begin brush.color := gameBGcolor; brush.Style := bsSolid; fillrect(rect(0,0,width,height)); x1 := 0; y1 := 0; x2 := width-1; y2 := height-1; pen.Width := 1; for i := 0 to 4 do begin pen.color := $e0e0e0 - abs(1-i)*$101010; moveto(x2-i,y1+i); lineto(x1+i,y1+i); lineto(x1+i,y2-i); pen.Color := $e0e0e0 - abs(3-i)*$101010; lineto(x2-i,y2-i); lineto(x2-i,y1+i); end; pen.color := $606060; x1 := planes[0].left; y1 := planes[0].top; x2 := planes[2].left; y2 := planes[2].top; moveto(x1,y1); lineto(x2,y2); w1 := planes[0].square*3; moveto(x1+w1,y1+w1); w2 := planes[2].square*3; lineto(x2+w2,y2+w2); end; paintframe; paintgame; end; procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var p : byte; begin if (gamestatus = gsMoving) then begin p := xy2field(x,y); //no field --> 27 if p <> selectfield then begin with paintbox1 do if (p > 0) and (game[p]=0) then cursor := crHandpoint else cursor := crArrow; selectfield := p; end; end; end; procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (gamestatus = gsMoving) and (selectfield > 0) then gameControl(guMove); end; procedure TForm1.backBtnClick(Sender: TObject); begin gamecontrol(guBackBtn); end; procedure TForm1.newBtnClick(Sender: TObject); begin gameControl(guNewBtn); end; procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin if key = #08 then gamecontrol(guBackBtn); end; procedure TForm1.analyse1BTnClick(Sender: TObject); begin gamecontrol(guAnalyseBtn); end; procedure TForm1.Image2Click(Sender: TObject); begin ShellExecute(0,'open','http://www.davdata.nl/math/', nil, nil, SW_SHOWNORMAL); end; procedure TForm1.Image4Click(Sender: TObject); begin gameControl(guNewBtn); end; procedure TForm1.Image1Click(Sender: TObject); begin gamecontrol(guBackBtn); end; procedure TForm1.analyseBtnClick(Sender: TObject); begin gamecontrol(guAnalyseBtn); end; //-- control helpers procedure displaymover; //write O,X message var p : byte; begin p := 1 + (moveNr and 1); msgproc(chdef[p] + ' move',playercolor(p)); end; procedure moveproc; var s : string; m : byte; begin if Fscores then begin Fscores := false; paintgame; end; s := ''; inc(moveNr); m := 2 - (moveNr and 1); game[selectfield] := m; paintfield(selectfield,gameBGcolor); node[moveNr].pos := selectfield; selectfield := 100; form1.PaintBox1.Cursor := crArrow; if checkwin(wintriple,m) then begin s := chdef[m] + ' wins. '; markwinner; gamestatus := gsEnd; end; if moveNr = maxmove then begin s := s + 'End of game.'; gamestatus := gsEnd; end; if gamestatus = gsMoving then displaymover else msgproc(s,$000000); end; procedure backproc; var p : byte; begin unmarkwinner; if Fscores then begin Fscores:= false; paintgame; end; if moveNr > 0 then begin p := node[movenr].pos; game[p] := 0; paintfield(p,gameBGcolor); dec(moveNr); displaymover; gamestatus := gsMoving; end; end; procedure newproc; begin gamestatus := gsInitializing; initgame; paintgame; gamestatus := gsMoving; displaymover; end; procedure analyseproc; var i : byte; begin for i := 1 to maxmove do scores[i] := 0; gamestatus := gsAnalysing; msgproc('analysing, please wait...',$000000); analyse; gamestatus := gsMoving; displaymover; end; procedure GameControl(gum : TGameUpdate); begin case gamestatus of gsMoving : case gum of guMove : moveproc; guBackBtn : backproc; guAnalyseBtn : analyseproc; guNewBtn : newproc; end;//case gsEnd : case gum of guNewBtn : newproc; guBackBtn : backproc; end;//case end;//case end; end.