棋盘有了,怎么支持在上面落子呢?
只要解决下面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.
呵呵,我用了一个动态的整数数组来记录棋局的状态。
这个程序已经可以用鼠标交替落子了,但还不能真正支持对弈,因为还不知道怎么提子。下一步就是要实现下棋规则了,最好支持自动提子,就可以用来玩了。
程序运行的效果如下: