棋盘有了,怎么支持在上面落子呢?
只要解决下面3个问题就可以了:
1.响应鼠标点击事件,获得“下棋子”的动作源。
2.修改和记录棋局状态。
3.在棋盘上显示棋局的状态。
为此,直接增加一个“棋局类“,也就是对“一盘棋“对象的实现。
先把已经编好的棋盘类移到一个新的单元里,不再放到窗体单元中,我喜欢这样把成熟的程序逐渐移到新单元内存放。
棋盘单元如下:
unit UnitBoardView;
 interface
 uses
   Windows, Messages, SysUtils, Variants, Types, Classes, Graphics;
 Type
  TStringGoBoard = Class(TObject)     //线棋盘类
    Private
      FMaxP : Integer;                  //棋盘最大点树
      FRect : TRect;                    //棋盘区域位置
      Function GetDD : Integer;         //相邻交叉点间隔距离
      Function GetBX0 : Integer;        //棋盘起画点X
      Function GetBY0 : Integer;        //棋盘起画点Y
    Protected
      Procedure SetMaxP(AMaxP : Integer);
      Property DD : Integer Read GetDD;     //相邻交叉点间隔距离
      Property BX0 : Integer Read GetBX0;   //棋盘起画点X
      Property BY0 : Integer Read GetBY0;   //棋盘起画点Y
    Public
      Procedure Drawto(ACanvas : TCanvas);  //画到一个画布上
      Procedure DrawMove(ACanvas : TCanvas; APos : Integer; AStatus : Integer); //画一步棋
      Function Position(X,Y : Integer) : Integer;         //找一个下棋位置
      Property MaxP : Integer Read FMaxP Write SetMaxP;
      Property Rect : TRect Read FRect Write FRect;
    End;
 implementation
 {TStringGoBoard}
 Function TStringGoBoard.GetDD : Integer;     //相邻交叉点间隔距离
   begin
     Result := ((Rect.Right - Rect.Left) - 20) div MaxP; //宽度两边各留10个像素
   end;
 Function TStringGoBoard.GetBX0 : Integer;        //棋盘起画点X
   begin
     Result := Rect.Left + 10;
   end;
 Function TStringGoBoard.GetBY0 : Integer;        //棋盘起画点Y
   begin
     Result := Rect.Top + (Rect.Bottom - Rect.Top) div 2;
   end;
 Procedure TStringGoBoard.SetMaxP(AMaxP : Integer);
   begin
     FMaxP := (AMaxP Div 2) * 2; //N必须是偶数,也就是必须得到奇数个交叉点;
   end;
 Procedure TStringGoBoard.Drawto(ACanvas : TCanvas); //画到一个画布上
 var
   i,M: Integer;
   X0,Y0,BDD,CDD : Integer;
 begin
   M := MaxP div 2;
   with ACanvas do
     begin
       Pen.Width := 1;
       X0 := BX0;          //动态计算画棋盘位置
       Y0 := BY0;
       BDD := DD;
       CDD := BDD div 2;
       moveto(X0,Y0);
       LineTo(X0 + MaxP * BDD, Y0);
       For i := 0 to MaxP do
         begin
           if (i = 0) or (i = MaxP) then
              Pen.Width := 3           //画两端的粗线
             else
              Pen.Width := 1;          //画中间的细线
           moveto(X0 + i * BDD,Y0 - CDD);
           Lineto(X0 + i * BDD,Y0 + CDD);
           if i = M then               //在中点画一个星(天元,呵呵!)
             begin
               Brush.Color := ClBlack;
               Brush.Style := bsSolid;
               Ellipse(X0 - 2 + i * BDD, Y0 -2, X0 +2 + i * BDD, Y0 +2);
             end;
         end;
     end;
 end;
 Procedure TStringGoBoard.DrawMove(ACanvas : TCanvas; APos : Integer; AStatus : Integer); //画一步棋
 var
   X0,Y0,BDD,CDD : Integer;
 begin
   with ACanvas do
     begin
       X0 := BX0;
       Y0 := BY0;
       BDD := DD;
       CDD := BDD div 2;
       if AStatus = 1 then
          Brush.Color := ClBlack
         else
          Brush.Color := ClWhite;
       Pen.Width := 1;
       Pen.Color := Brush.Color;
       Brush.Style := bsSolid;
       Ellipse(X0 - CDD + APos * BDD, Y0 - CDD, X0 + CDD + APos * BDD, Y0 + CDD);
     end;
 end;
 Function TStringGoBoard.Position(X,Y : Integer) : Integer;         //找一个下棋位置
   var
     i : Integer;
     X0,Y0,BDD,CDD,X1,Y1,X2,Y2 : Integer;
   begin
     Result := -1;
     X0 := BX0;
     Y0 := BY0;
     BDD := DD;
     CDD := BDD div 2;
     For i := 0 to MaxP do
       begin
         X1 := X0 - CDD + i * BDD;
         Y1 := Y0 - CDD;
         X2 := X0 + CDD + i * BDD;
         Y2 := Y0 + CDD;
         if (X >= X1) and (X <= X2) and
            (Y >= Y1) and (Y <= Y2) then
           begin
             Result := i;
             Exit;
           end;
       end;
   end;
end.
里面已经添加了显示一步棋和根据鼠标位置找下棋点位置的方法了,这是支持下棋所必需要有的方法。
然后,继续在窗体类的单元内试验新建的棋局类。为简便起见,棋盘就当作棋局本身的一部分了。
新的窗体单元变成了这个样子的:
unit Unit1;
 interface
 uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, UnitBoardView;
 type
 TGame = Class(TObject)
   Private
     FBoard : TStringGoBoard;            //包含一个棋盘对象
     FPosStatus : Array of Integer;      //记录棋子状态的数组
     FCurPlayer : Integer;                   //当前行棋方,1:黑,2:白
   Protected
     Function GetMaxP : Integer;
     Procedure SetMaxP (AMaxP : Integer);
   Public
     Constructor Create;
     Destructor Destroy; Override;
     Procedure SetPos(APos : Integer);     //在位置上走一步棋
     Procedure Drawto(ACanvas : TCanvas);  //把棋局画在画布上
     Property Board : TStringGoBoard Read FBoard;
     Property MaxP : Integer Read GetMaxP Write SetMaxP;  //最大棋盘位置
   end;
 TForm1 = class(TForm)
     procedure FormPaint(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormResize(Sender: TObject);
     procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
   private
     FGame : TGame;               //棋局对象
   public
     { Public declarations }
   end;
 var
   Form1: TForm1;
 implementation
 {$R *.dfm}
 procedure TForm1.FormCreate(Sender: TObject);
 begin
   FGame := TGame.Create;            //窗口创建时是创建对局
   FGame.Board.Rect := ClientRect;   //棋盘占整个窗口位置
   FGame.MaxP := 8;                //设为9点棋局
   //FGame.MaxP := 15;                //设为15点棋局
 end;
 procedure TForm1.FormPaint(Sender: TObject);
 begin
   FGame.Drawto(Canvas);              //画棋局
 end;
 procedure TForm1.FormDestroy(Sender: TObject);
 begin
   FGame.Free;                       //窗口销毁时销毁棋局
 end;
 procedure TForm1.FormResize(Sender: TObject);
 begin
   FGame.Board.Rect := ClientRect;    //窗口变化大小是变化棋盘大小
   Repaint;                           //启动重画窗口
 end;
 procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
   Shift: TShiftState; X, Y: Integer);
 var
   Pos : Integer;
 begin
   if Button = mbLeft then
     begin
       Pos := FGame.Board.Position(X,Y);  //根据屏幕坐标得到下棋点坐标。
       if Pos >= 0 then
         begin
           FGame.SetPos(Pos);          //在棋局上走一步棋
           Repaint;                    //显示棋局
         end;
     end;
 end;
 {TGame}
 Constructor TGame.Create;
   begin
     Inherited Create;
     FCurPlayer := 1;                 //默认黑为当前下子方
     FBoard := TStringGoBoard.Create;
   end;
 Destructor TGame.Destroy;
   begin
     FBoard.Free;
     Inherited Destroy;
   end;
 Procedure TGame.SetPos(APos : Integer);
   begin
     FPosStatus[APos] := FCurPlayer;
     FCurPlayer := 3 - FCurPlayer;
   end;
 Procedure TGame.Drawto(ACanvas : TCanvas);
   var
     i : Integer;
   begin
     FBoard.Drawto(ACanvas);           //画出棋盘
     For i := 0 to MaxP do
       begin
         if FPosStatus[i] <> 0  then
           begin
             FBoard.DrawMove(ACanvas,i,FPosStatus[i]);   //画棋子
           end;
       end;
   end;
 Function TGame.GetMaxP : Integer;
   begin
     Result := FBoard.MaxP;
   end;
 Procedure TGame.SetMaxP (AMaxP : Integer);
   var
     i : Integer;
   begin
     FBoard.MaxP := AMaxP;          //设置棋盘大小
     SetLength(FPosStatus, MaxP + 1 );    //初始化记录数组
     For i := 0 to MaxP do
       begin
         FPosStatus[i] := 0;
       end;
   end;
 end. 
呵呵,我用了一个动态的整数数组来记录棋局的状态。
这个程序已经可以用鼠标交替落子了,但还不能真正支持对弈,因为还不知道怎么提子。下一步就是要实现下棋规则了,最好支持自动提子,就可以用来玩了。
程序运行的效果如下: