Delphi 经典游戏程序设计40例 的学习 例37 在迷宫中追赶对手

unit R37;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TRei37 = class(TForm)
    Button1: TButton;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1Timer(Sender: TObject);
    procedure FormPaint(Sender: TObject);
  private
    { Private declarations }
    procedure WtMove;
    procedure RdMove;
    procedure Ncheck(X1,Y1,Dr: Byte);
    procedure Gnext(var X1: Byte;var Y1: Byte;Dr: Byte);
    procedure DiMaze;

  public
    { Public declarations }
  end;
const
  Xmax = 28;      //迷宫的宽度-1
  Ymax = 20;      //迷宫的高度-1
  Mwidth = (Xmax + 1) * 16 + 32;
  Mheight = (Ymax + 1) * 16 + 32;
  
var
  Rei37: TRei37;
  MakeBmap: TBitmap;
  St,x,y,Wtime: Byte;
  WX,WY,WD: Byte; //白点的坐标,方向
  RX,RY,RD: Byte; //红点的坐标,方向
  See : Byte; //0-3 看得到白点的方向,255看不到
  Mem : Byte;  // 0追赶中,1白点转弯时,255平常的时候
  PX,PY,PD: Byte; // 追赶时,白点转弯后的坐标,方向
  RectD: TRect;
  Dmap: array[0..3] of Byte;   //求出DINF[] 准备的数据   字节数组,一维,长度4
  Dinf: array[0..4] of Byte;  //能够移动的方向编号
  //迷宫数据数组
  Mdata: array[0..Xmax,0..Ymax] of Byte =
    (
    (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0),
    (0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0),
    (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
    (0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0),
    (0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0),
    (0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0),
    (0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0),
    (0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0),
    (0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0),
    (0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0),
    (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
    (0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0),
    (0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0),
    (0,1,0,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0),
    (0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0),
    (0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0),
    (0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0),
    (0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0),
    (0,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0),
    (0,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0),
    (0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0),
    (0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,0),
    (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
    );

implementation

{$R *.dfm}

procedure TRei37.FormCreate(Sender: TObject);
begin
  Rei37.Canvas.CopyMode := cmSrcCopy;
  MakeBmap := TBitmap.Create;
  MakeBmap.Height := Mheight;
  MakeBmap.Width := Mwidth;
  St := 1;
  Randomize;
end;

procedure TRei37.WtMove;
  //移动白点
begin
  with Rei37 do
    Canvas.Brush.Color := clBlack;  //擦除原坐标白点
    Canvas.Ellipse(WX * 16 + 19,WY * 16 + 19,WX * 16 + 29,WY * 16 + 29);
    Ncheck(WX,WY,WD);  //检查白点能够前往的位置
    if (Dinf[0] = 1) or (Dinf[0] = 2) then  //优先直行?
      WD := Dinf[1]
    else
      WD := Dinf[1 + Random(Dinf[0] - 1)];  //如果3,4个方向则随即选择方向?
    Gnext(WX,WY,WD);  //按指定方向改变白点坐标
    Canvas.Brush.Color := clWhite;   //绘制新坐标白点
    Canvas.Ellipse(WX * 16 + 19,WY * 16 + 19,WX * 16 + 29,WY * 16 + 29);

end;

procedure TRei37.RdMove;
   //移动红点
var
  n : Byte;

begin
  with Rei37 do
    Canvas.Brush.Color := clBlack;    //擦除原红点
    Canvas.Ellipse(RX * 16 + 19,RY * 16 + 19,RX * 16 + 29,RY * 16 + 29);
    Ncheck(RX,RY,RD);         //检查红点能够前往的位置
    if (WY = RY) and (WX > RX) then   //在同Y轴上,且白点在右
    begin
      See := 0;       //0-3 看得到白点的方向0,255看不到
      for n := RX + 1 to WX do
        if Mdata[n,RY] <> 0 then    //检查白红点间有墙
          See := 255;
    end
    else if (WX = RX) and (WY < RY) then    //在同X轴上,且白点在上
      begin
      See := 1;         //看得到白点的方向1
      for n := RY -1 downto wy do
        if Mdata[RX,n] <> 0 then
          See := 255;
    end
    else if (WY = RY) and (WX < RX) then  //在同Y轴上,且白点在左
    begin
      See := 2;             //看得到白点的方向2
      for n := RX -1 downto WX do
        if Mdata[n,RY] <> 0 then
          See := 255;
    end
    else if (WX = RX) and (WY > RY) then   //在同X轴上,且白点在下
    begin
      See := 3;              //看得到白点的方向3
      for n := RY + 1 to WY do
        if Mdata[RX,n] <> 0 then
        See := 255;
    end;
    
    if See < 4 then
    begin
      if (RD = See) or (Dinf[0] > 2) or (Dinf[1] <> RD) then
      begin          //红点同向或迷宫3出口,或第一选择出口不是红点选择
        RD := See;   //红点方向同
        Mem := 0;     // 0追赶中,1白点转弯时,255平常的时候
      end;            //进入追赶状态
      See := 255; //否则看不到?
    end
    else if Mem = 0 then   //追赶状态下进入白点转弯状态
    begin
      PX := WX;
      PY := WY;
      PD := WD;
      Mem := 1;
    end
    else if Mem = 1 then     //白点转弯状态下进入追丢目标状态
    begin
      if (RX = PX) or (RY = PY) then    //如果红点在原白点X或Y直线上
      begin
        RD := PD;                       //选择白点原转弯的方向
        Mem := 255;                     //进入追丢目标状态
      end;
    end
    else if Dinf[0] <= 2 then
      RD := Dinf[1]             //如2方向优先选择右转?
    else
      RD := Dinf[1 + Random(Dinf[0] - 1)];  //3方向及以上红点随即选择方向


    Gnext(RX,RY,RD); // //按指定方向改变红点坐标
    Canvas.Brush.Color := clRed;    //刷新红点
    Canvas.Ellipse(RX * 16 + 19,RY * 16 + 19,RX * 16 + 29,RY * 16 + 29);
end;

procedure TRei37.Ncheck(X1,Y1,Dr: Byte);
var          //检查能够前往的位置
  n: Byte;
begin
  if X1 < Xmax then              //取得右,X1+1方向数据
    Dmap[0] := Mdata[X1 + 1,Y1] //一维数组,二维数组,元素相同,而对下标值进行计算
  else                          //边界判断
    Dmap[0] := 1;

  if Y1 > 0 then                 //取得上,数据,及边界判断
    Dmap[1] := Mdata[X1,Y1 - 1]
  else
    Dmap[1] := 1;

  if X1 > 0 then                   //取得左数据,及边界判断
    Dmap[2] := Mdata[X1 - 1,Y1]
  else
    Dmap[2] := 1;

  if Y1 < Ymax then              //取得下数据,及边界判断
    Dmap[3] := Mdata[X1,Y1 + 1]
  else
    Dmap[3] := 1;

  n := 1;
  if Dmap[Dr] = 0 then        //指定方向能够通过
  begin
    Dinf[n] := Dr;
    n := n + 1;               //能通过的方向+1
  end;

  if Dmap[(Dr + 1) and 3] = 0 then     //指定方向逆时针位可通过,左侧
  begin
    Dinf[n] := (Dr + 1) and 3;
    n := n + 1;
  end;

  if Dmap[(Dr -1) and 3] = 0 then     //指定方向顺时针位可通过,右侧
  begin
    Dinf[n] := (Dr - 1) and 3;
    n := n + 1;
  end;

  if Dmap[(Dr + 2) and 3] = 0 then     //指定方向的反方向可通过,后侧
  begin
    Dinf[n] := (Dr + 2) and 3;
    n := n + 1;
  end;

  Dinf[0] := n - 1;   //得到可以通过的方向数,



end;

procedure TRei37.Gnext(var X1: Byte;var Y1: Byte;Dr: Byte);
  //按各个方向依次改变坐标
begin
  case Dr of
    0: X1 := X1 + 1;         //
    1: Y1 := Y1 - 1;         //
    2: X1 := X1 - 1;         //
    3: Y1 := Y1 + 1;         //
  end;

end;

procedure TRei37.DiMaze;
  //显示迷宫
var
  x,y : Byte;
begin
  RectD := Rect(0,0,Mwidth,Mheight);
  MakeBmap.Canvas.Brush.Color := clOlive;
  MakeBmap.Canvas.FillRect(RectD);
  MakeBmap.Canvas.Brush.Color := clBlack;
  for x := 0 to Xmax do
    for y := 0 to Ymax do
      if Mdata[x,y] <> 1 then
      begin
        RectD := Rect(x * 16 + 16,y * 16 + 16,x * 16 + 32,y * 16 + 32);
        MakeBmap.Canvas.FillRect(RectD);
      end;
  Rei37.Canvas.Draw(0,0,MakeBmap);    
end;



procedure TRei37.Button1Click(Sender: TObject);
begin
  St := 2;
  with Rei37 do
    Canvas.Brush.Color := clBlack;
    Canvas.Ellipse(WX * 16 + 19,WY * 16 + 19,WX * 16 + 29,WY * 16 + 29);
    Canvas.Ellipse(RX * 16 + 19,RY * 16 + 19,RX * 16 + 29,RY * 16 + 29);
end;

procedure TRei37.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MakeBmap.Free;
end;

procedure TRei37.Timer1Timer(Sender: TObject);
begin
  case St of
    0: begin
      Wtime := Wtime xor 1;               //异或翻转 设置开关
      if (WX = RX) and (WY = RY) then    //判断捕捉到
      begin
        with Rei37 do                    //红白闪烁
          if Wtime = 0 then
            Canvas.Brush.Color := clRed
          else
            Canvas.Brush.Color := clWhite;
        Canvas.Ellipse(WX * 16 + 19,WY * 16 + 19,WX * 16 + 29,WY * 16 + 29);
      end
      else begin
        WtMove;            //白点移动
        if (WX <> RX) or (WY <> RY) then        //这里为什么要判断?
          RdMove;          //红点移动
      end;
    end;
    1: begin
      DiMaze;          //绘出迷宫
      St := 2;
    end;
    2: begin           //重置
      St := 0;
      See := 255;
      Mem := 255;
      repeat
        WX := Random(Xmax + 1);
        WY := Random(Ymax + 1);
        WD := Random(4);
      until Mdata[WX,WY] = 0;     //白点必须随机设置在走道内
      repeat
        RX := Random(Xmax + 1);
        RY := Random(Ymax + 1);
        RD := Random(4);
      until (Mdata[RX,RY] = 0) and (Abs(WX - RX) > 8) and (Abs(WY - RY) > 6);
    end;                            //红点随机在走道内,并保持和白点的距离
  end;
end;

procedure TRei37.FormPaint(Sender: TObject);
begin
  Rei37.Canvas.Draw(0,0,MakeBmap);
end;

end.

1,迷宫的实现,一个二维的数组,下标 作为 一个可以运算的序数,可以当作 红白点 的X,Y 的值来计算

   其元素值 ,作为 墙和通道,1,0,当然作为一个BYTE 类型 数,可以有更多的表示,

  例如天空,草地,大树,石头,箱子,可以移动的物体等等,256种

  或者多个二维数组,实现 多层。

2,IF THEN ELSE IF THEN ELSE  ELSE 。。。的多层使用,

  条件的判断,实现什么逻辑?

3,迷宫数据转换成可以行进的方向的判断

posted @ 2022-11-17 10:10  D7mir  阅读(92)  评论(0)    收藏  举报