| 蚂蚁过桥 
 
 作者:SkyJacker 2007.06内容:OOP 习作
 目的:欢迎意见、建议、讨论。
 发布:http://bbs.cnpack.org
 QQ Group: 130970
 
 1、问题提出:
 
 小夏:
 有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。木杆很细,不能同时通过一只蚂蚁。开始时,蚂蚁的头朝左还是朝右是任意的,
 它们只会朝前走或调头,但不会后退。当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟可以走一厘米的距离。
 编写程序,求所有蚂蚁都离开木杆的最小时间和最大时间。
 
 2、解决思路:
 
 想用面向对象的方法来解决。
 看题目,首先要找出具体的独立的对象来,然后根据其特性封装成类。
 是否可以将细木干可以作为一个对象呢?如果将细木干作为一个对象,它需要管理每个节点。
 不过我感觉选择木干不是很清晰(也许有人会搞明白,望告知),因此选择了蚂蚁作为一个独立的对象,同时,因为有多个蚂蚁,蚂蚁之间的运动方向,有时取决于其他蚂蚁的方向,因此,建立一个用于管理蚂蚁的类,来实现具体的运动逻辑控制。
 
 3、具体实现
 
 有时候想的或说的可能简单,但是具体实现起来也有不少要考虑的东东。
 下面是具体实现,仅供参考。
 
 const
 MaxAntNum = 5;
 MaxTreeLen = 27;
 cnPositon = '%d %d %d %d %d';
 
 { TTreeAnt }
 
 TTreeAnt = class(TObject)
 {* 蚂蚁类}
 private
 FHeadDirection: Boolean; // 头部方向
 FPosition: Integer; // 位置
 FLifeValueUpperLimit: Integer; // 生命上限
 FLifeValueLowerLimit: Integer; // 生命下限
 FLiving: Boolean; // 活着
 FTurnAroundFlag: Boolean; // 是否要转向
 procedure SetTurnAroundFlag(ATurnAroundFlag: Boolean);
 procedure SetHeadDirection(AHeadDirection: Boolean);
 procedure SetPosition(APosition: Integer);
 procedure SetLifeValueUpperLimit(ALifeValue: Integer);
 procedure SetLifeValueLowerLimit(ALifeValue: Integer);
 protected
 
 public
 constructor Create;
 destructor Destroy; override;
 procedure StepIt; // 前进
 procedure TurnAround; // 转向
 published
 property HeadDirection: Boolean  read FHeadDirection write SetHeadDirection;
 property Position: Integer read FPosition write SetPosition;
 property Living: Boolean read FLiving;
 property LifeValueUpperLimit: Integer read FLifeValueUpperLimit write SetLifeValueUpperLimit;
 property LifeValueLowerLimit: Integer read FLifeValueLowerLimit write SetLifeValueLowerLimit;
 property TurnAroundFlag: Boolean read FTurnAroundFlag write SetTurnAroundFlag;
 end;
 
 { TTreeAntMgr }
 
 TTreeAntInitRec = record
 { 管理蚂蚁记录结构 }
 HeadDirection: Boolean;
 Position: Integer;
 UpperLimit: Integer;
 LowerLimit: Integer;
 end;
 
 // 树节点结构
 //TTreeNode = array[1..MaxTreeLen] of Integer;
 TTreeNode = array[-1..MaxTreeLen+1] of Integer; // 防溢出
 
 TTreeAntMgr = class(TObject)
 {* 蚂蚁管理类}
 private
 FTreeNode: TTreeNode;
 FAntCnt: Integer;
 FAntLivingCnt: Integer; // 存活蚂蚁计数
 FSpeed: Integer; // 速度
 FTimer: TTimer; // 蚂蚁步进定时器
 FTreeAnts: array[0..MaxAntNum-1] of TTreeAnt; // 蚂蚁数组
 FTimeCnt: Integer;
 FRunning: Boolean;
 procedure SetSpeed(ASpeed: Integer);
 procedure SetTreeNodeFlag(APosition: Integer);
 procedure ResetTreeNodeFlag(APosition: Integer);
 function GetPositionAntIdx(APosition: Integer): Integer;
 function IsTurnAround(AntIdx: Integer): Boolean; // 转向判断
 procedure TimerRun(Sender: TObject);
 procedure LogPosition; { 辅助函数 }
 protected
 
 public
 constructor Create;
 destructor Destroy; override;
 procedure EnterTree(TreeAntInitRec: TTreeAntInitRec);
 procedure Start;
 procedure Stop;
 procedure Init;
 published
 property Running: Boolean read FRunning;
 property Speed: Integer  read FSpeed write SetSpeed;
 end;
 
 { TTreeAnt }
 
 constructor TTreeAnt.Create;
 begin
 FPosition := 1;
 FHeadDirection := True;
 FLiving := True;
 FTurnAroundFlag := False;
 end;
 
 destructor TTreeAnt.Destroy;
 begin
 
 end;
 
 procedure TTreeAnt.SetTurnAroundFlag(ATurnAroundFlag: Boolean);
 begin
 if FTurnAroundFlag <> ATurnAroundFlag then
 FTurnAroundFlag := ATurnAroundFlag;
 end;
 
 procedure TTreeAnt.SetHeadDirection(AHeadDirection: Boolean);
 begin
 if FHeadDirection <> AHeadDirection then
 FHeadDirection := AHeadDirection;
 end;
 
 procedure TTreeAnt.SetPosition(APosition: Integer);
 begin
 if FPosition <> APosition then
 FPosition := APosition;
 end;
 
 procedure TTreeAnt.SetLifeValueUpperLimit(ALifeValue: Integer);
 begin
 if FLifeValueUpperLimit <> ALifeValue then
 FLifeValueUpperLimit := ALifeValue;
 end;
 
 procedure TTreeAnt.SetLifeValueLowerLimit(ALifeValue: Integer);
 begin
 if FLifeValueLowerLimit <> ALifeValue then
 FLifeValueLowerLimit := ALifeValue;
 end;
 
 procedure TTreeAnt.StepIt();
 begin
 if FHeadDirection then
 Inc(FPosition)
 else
 Dec(FPosition);
 
 if (FPosition > FLifeValueUpperLimit) or (FPosition < FLifeValueLowerLimit) then
 FLiving := False;
 end;
 
 procedure TTreeAnt.TurnAround;
 begin
 FHeadDirection := not FHeadDirection;
 end;
 
 { TTreeAntMgr }
 
 constructor TTreeAntMgr.Create;
 begin
 Init;
 FTimer := TTimer.Create(Application);
 FTimer.Enabled := False;
 SetSpeed(1000);
 FTimer.OnTimer := TimerRun;
 end;
 
 destructor TTreeAntMgr.Destroy;
 begin
 end;
 
 procedure TTreeAntMgr.Init;
 var
 I: Integer;
 begin
 FAntCnt := 0;
 FTimeCnt := 0;
 FAntLivingCnt := 0;
 FRunning := False;
 for I := Low(FTreeNode) to High(FTreeNode) do
 begin
 FTreeNode[I] := 0;
 end;
 end;
 
 procedure TTreeAntMgr.SetTreeNodeFlag(APosition: Integer);
 begin
 FTreeNode[APosition] := 1;
 end;
 
 procedure TTreeAntMgr.ResetTreeNodeFlag(APosition: Integer);
 begin
 FTreeNode[APosition] := 0;
 end;
 
 function TTreeAntMgr.GetPositionAntIdx(APosition: Integer): Integer;
 var
 I: Integer;
 begin
 Result := -1;
 for I := Low(FTreeAnts) to High(FTreeAnts) do
 begin
 if FTreeAnts[I].Position = APosition then
 begin
 Result := I;
 Break;
 end;
 end;
 end;
 
 function TTreeAntMgr.IsTurnAround(AntIdx: Integer): Boolean;
 var
 bMiddle: Boolean;
 bRight: Boolean;
 bLeft: Boolean;
 Position: Integer;
 NextAntIdx: Integer;
 begin
 Result := False;
 bMiddle := FTreeAnts[AntIdx].HeadDirection;
 Position := FTreeAnts[AntIdx].Position;
 
 if bMiddle then
 begin
 if FTreeNode[Position+2] = 0 then
 Exit;
 NextAntIdx := GetPositionAntIdx(Position+2);
 if NextAntIdx = -1 then Exit;
 bRight := FTreeAnts[NextAntIdx].HeadDirection;
 if not bRight then  // 1 23
 begin
 Result := True;
 Exit;
 end;
 end
 else
 begin
 if FTreeNode[Position-2] = 0 then
 Exit;
 NextAntIdx := GetPositionAntIdx(Position-2);
 if NextAntIdx = -1 then Exit;
 bLeft := FTreeAnts[NextAntIdx].HeadDirection;
 if bLeft then  // 12 3
 begin
 Result := True;
 Exit;
 end;
 end;
 end;
 
 procedure TTreeAntMgr.EnterTree(TreeAntInitRec: TTreeAntInitRec);
 begin
 if FAntCnt > High(FTreeAnts) then
 Exit;
 if TreeAntInitRec.Position > High(FTreeNode) then
 Exit;
 FTreeAnts[FAntCnt] := TTreeAnt.Create;
 FTreeAnts[FAntCnt].HeadDirection := TreeAntInitRec.HeadDirection;
 FTreeAnts[FAntCnt].Position := TreeAntInitRec.Position;
 FTreeAnts[FAntCnt].LifeValueUpperLimit := TreeAntInitRec.UpperLimit;
 FTreeAnts[FAntCnt].LifeValueLowerLimit := TreeAntInitRec.LowerLimit;
 //FTreeNode[FTreeAnts[FAntCnt].Position] := 1;
 SetTreeNodeFlag(TreeAntInitRec.Position);
 Inc(FAntCnt);
 end;
 
 procedure TTreeAntMgr.Start;
 begin
 FTimer.Enabled := True;
 FRunning := True;
 end;
 
 procedure TTreeAntMgr.Stop;
 begin
 FTimer.Enabled := False;
 FRunning := False;
 end;
 
 procedure TTreeAntMgr.SetSpeed(ASpeed: Integer);
 begin
 if FSpeed <> ASpeed then
 begin
 FSpeed := ASpeed;
 FTimer.Interval := FSpeed;
 end;
 end;
 
 procedure TTreeAntMgr.TimerRun(Sender: TObject);
 var
 I: Integer;
 begin
 Inc(FTimeCnt);
 Log('计时:' + IntToStr(FTimeCnt));
 LogPosition;
 FAntLivingCnt := 0;
 for I := Low(FTreeAnts) to High(FTreeAnts) do
 begin
 if FTreeAnts[I].FLiving then
 if FTreeAnts[I].TurnAroundFlag then
 begin
 FTreeAnts[I].TurnAround;
 Log('转向:'+IntToStr(I));
 end;
 FTreeAnts[I].TurnAroundFlag := IsTurnAround(I);
 end;
 for I := Low(FTreeAnts) to High(FTreeAnts) do
 begin
 if not FTreeAnts[I].Living then
 begin
 Inc(FAntLivingCnt);
 if FAntLivingCnt >= FAntCnt then
 begin
 Stop;
 AddTime(0);
 Exit;
 end;
 Continue;
 end;
 ResetTreeNodeFlag(FTreeAnts[I].Position);
 FTreeAnts[I].StepIt;
 if FTreeAnts[I].Living then
 SetTreeNodeFlag(FTreeAnts[I].Position)
 else
 begin
 Log('离桥:'+IntToStr(I) + ' 时间:' + IntToStr(FTimeCnt));
 AddTime(FTimeCnt);
 end;
 end;
 end;
 
 procedure TTreeAntMgr.LogPosition;
 var
 P1,P2,P3,P4,P5: Integer;
 H1, H2, H3, H4, H5: Boolean;
 begin
 P1 := FTreeAnts[0].Position;
 P2 := FTreeAnts[1].Position;
 P3 := FTreeAnts[2].Position;
 P4 := FTreeAnts[3].Position;
 P5 := FTreeAnts[4].Position;
 
 H1 := FTreeAnts[0].HeadDirection;
 H2 := FTreeAnts[1].HeadDirection;
 H3 := FTreeAnts[2].HeadDirection;
 H4 := FTreeAnts[3].HeadDirection;
 H5 := FTreeAnts[4].HeadDirection;
 
 FrmAnt.AntImgPostion([P1, P2, P3, P4, P5], [H1, H2, H3, H4, H5]);
 FrmAnt.LogPosition(Format(cnPositon, [P1, P2, P3, P4, P5]));
 end;
 
 4、发布可执行
 
 如果你有修改、改进的想法,我很愿意发给你工程源代码。
 
 [ 本帖最后由 skyjacker 于 2007-6-24 11:51 编辑 ]
 
 
  Image Attachment:
 1.jpg (2007-6-24 11:44, 40.57 K) 
 
   
 
  Image Attachment:
 2.jpg (2007-6-24 11:44, 48.56 K) 
 
   
 
  Attachment:
 蚂蚁过桥可执行.rar (2007-6-24 11:51, 210.03 K) Download count 616
 
 |