{
    This file is part of Langton's Ant
    Copyright (c) 2002 by Ian Hickson

    An implementation of Langton's Ant

    See the file COPYING, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}
{$MODE OBJFPC}
unit LangtonAnt;

interface

type
  TDirection = (dUp, dRight, dDown, dLeft);
  TAntClass = class of TAnt;
  TAntFarm = class;
  TAnt = class
  public
    constructor Create(aX, aY: Integer; aAntFarm: TAntFarm); virtual;
    destructor Destroy; override;
    procedure Step;
    function Move(aDeltaX, aDeltaY: Integer): Boolean;
    procedure Rotate(aClockwise: Boolean);
  protected
    procedure SetPixel(sState: Boolean); virtual; abstract;
    function GetPixel: Boolean; virtual; abstract;
    procedure SetPosition(aX, aY: Integer); virtual;
    function InRange: Boolean; virtual;
    function Toggle: Boolean; virtual;
    FX, FY: Integer;
    FDirection: TDirection;
    FAntFarm: TAntFarm;
  end;

  TAntFarm = class
  public
    constructor Create(aAntType: TAntClass);
    function Stop: Boolean; virtual; abstract;
    function Width: Integer; virtual; abstract;
    function Height: Integer; virtual; abstract;
    function NewAnt(aX, aY: Integer): TAnt;
  protected
    FAntType: TAntClass;
  end;

implementation

    constructor TAnt.Create(aX, aY: Integer; aAntFarm: TAntFarm);
    begin
       inherited Create;
       FAntFarm := aAntFarm;
       FDirection := dUp;
       SetPosition(aX, aY);
    end;

    destructor TAnt.Destroy;
    begin
    end;

    procedure TAnt.Step;
    var
       newState: Boolean;
    begin
       case FDirection of
        dUp: newState := Move(0, -1);
        dRight: newState := Move(1, 0);
        dDown: newState := Move(0, 1);
        dLeft: newState := Move(-1, 0);
       end;
       Rotate(newState);
    end;

    function TAnt.Move(aDeltaX, aDeltaY: Integer): Boolean;
    begin
       SetPosition(FX + aDeltaX, FY + aDeltaY);
       Result := Toggle;
    end;

    procedure TAnt.SetPosition(aX, aY: Integer);
    begin
       { this makes it more boring (the highway just runs into the chaos again)
       while (aX > FAntFarm.Width) do
          aX := aX - FAntFarm.Width;
       while (aX < 1) do
          aX := aX + FAntFarm.Width;
       while (aY > FAntFarm.Height) do
          aY := aY - FAntFarm.Height;
       while (aY < 1) do
          aY := aY + FAntFarm.Height;
       }
       FX := aX;
       FY := aY;
    end;

    procedure TAnt.Rotate(aClockwise: Boolean);
    begin
       if aClockwise then
       begin
          if FDirection < High(TDirection) then
             Inc(FDirection)
          else
             FDirection := Low(TDirection);
       end
       else
       begin
          if FDirection > Low(TDirection) then
             Dec(FDirection)
          else
             FDirection := High(TDirection);
       end;
    end;

    function TAnt.Toggle: Boolean;
    begin
       if InRange then
       begin
          Result := GetPixel;
          SetPixel(not Result);
       end
       else
          Result := False;
    end;

    function TAnt.InRange: Boolean;
    begin
       Result := (FX > 0) and (FY > 0) and
                 (FX <= FAntFarm.Width) and (FY <= FAntFarm.Height);
    end;


    constructor TAntFarm.Create(aAntType: TAntClass);
    begin
       inherited Create;
       FAntType := aAntType;
    end;

    function TAntFarm.NewAnt(aX, aY: Integer): TAnt;
    begin
       Result := FAntType.Create(aX, aY, Self);
    end;

end.

