library RFuddle;

{ Komplexni organismus na bázi binárního stromu. Po přidání do prostředí se
  nový jedinec stane vrcholem tohoto stromu. Po čase vypustí dva collectory,
  kteří se starají o sběr potravy. Sám přitom sedí na místě. Po ještě delším
  čase vypustí dva syny a ti dojedou na pozice v okolí, po čase vypustí dva
  collectory... Každý vrchol stromu má souřadnice rodiče a svých synů.
  V případě nebezpečí (spatření czího organismu) se vypustí messenger, který
  donese informace k sousedů, to ji rozšíří ke svým sousedům a tak dále po celém
  stromě.

  Co zbývá dodělat (když na to bude čas):
  - speciální poddruh "warrior", který pojede na souřadnice, které mu řekl
    messenger, a zničí tam napadající organismsu
  - rychlejší pohyb messengerů
  - periodickou kontrolu stavu stromu (započato, ale nedoděláno) }

{$R *.RES}

uses
  Windows, SysUtils,
  OrgInterface;

const
  // BEGIN UR
  ParentX = 0;
  ParentY = 1;
  ChildX1 = 2;
  ChildX2 = 3;
  ChildY1 = 4;
  ChildY2 = 5;
  Kind = 6;
    kBinTree = 0;
    kMessenger = 1;
    kCollector = 2;
  TreeLevel = 7;
  TargetX = 8;
  TargetY = 9;
  State = 10;
    sNothing = 0;
    sRunning = 1;
    sInforming = 2;
    sCollecting = 3;
    sEnemyHere = 4;
    sWar = 5;
  ChildCount = 11;
  Information = 12;
    iNone = 0;
    // iDanger = 1;
    // iTreeCheckQuestion = 2;
    // iTreeCheckAnswer = 3;
    // iTargetNotThere = 4;
    iSendWarrior = 5;
  childindex = 13;
    ci1 = 1;
    ci2 = 2;
  InformationSource = 14;
    isParent = 0;
    isChild1 = 1;
    isChild2 = 2;
  ParentMessengerSent = 15;
  Child1MessengerSent = 16;
  Child2MessengerSent = 17;
  InformationX = 18;
  InformationY = 19;
  // END UR

  BinTreeRadius = 20;
  AdultTime = BinTreeRadius + 20;
  MaxTreeLevel = MaxInt; // 10
  TreeCheckInterval = 2 * BinTreeRadius + 10;
  CollectorCount = 3;
  MinCollectorTime = BinTreeRadius + 5;
  MaxCollectorTime = BinTreeRadius + 5 + CollectorCount - 1;
  CollectorRadius = 10;

procedure OrgRun(Organism: IVLFOrganism); stdcall;
var
  AX, AY: Integer;
  Org: string;
  I: Integer;
begin
  with Organism do
  begin
    if Age = 0 then AllocUR(20);

    { inicializace }
    if (GenCount = 0) and (Age = 0) then
    begin
      SetUR(TargetX, X);
      SetUR(TargetY, Y);
      SetUR(State, sRunning);
    end;

    case UR(Kind) of

      //==================================================
      // BINTREE
      //==================================================
      kBinTree:
      begin
        if Age = 0 then SetColor(0, 255, 255);
        if UR(TreeLevel) = 0 then SetColor(192, 255, 255);

        if Mass < 20 then
        begin
          Eat(20);
          Build(20)
        end;
        if Energy < 20 then
        begin
          Eat(20);
          Digest(20);
        end;

        case UR(State) of
          //--------------------------------------------------
          // sNothing
          //--------------------------------------------------
          sNothing:
          begin

            // Collector
            if (Age >= MinCollectorTime) and (Age <= MaxCollectorTime) then
            begin
              DivCell;
              SetForeignUR(Kind, kCollector);
              SetForeignUR(ParentX, X);
              SetForeignUR(ParentY, Y);
              SetForeignUR(State, sCollecting);
              AX := X + Random(2 * CollectorRadius) - CollectorRadius;
              if AX < 0 then AX := 0;
              if AX > MaxX then AX := MaxX;
              AY := Y + Random(2 * CollectorRadius) - CollectorRadius;
              if AY < 0 then AY := 0;
              if AY > MaxY then AY := MaxY;
              SetForeignUR(TargetX, AX);
              SetForeignUR(TargetY, AY);
            end;

            // BinTree
            if (Age > AdultTime) and (UR(ChildCount) < 2) and (UR(TreeLevel) < MaxTreeLevel) then
            begin
              DivCell;
              SetForeignUR(Kind, kBinTree);
              SetForeignUR(TreeLevel, UR(TreeLevel) + 1);
              SetForeignUR(ParentX, X);
              SetForeignUR(ParentY, Y);
              SetForeignUR(State, sRunning);
              AX := X + Random(2 * BinTreeRadius) - BinTreeRadius;
              if AX < 0 then AX := 0;
              if AX > MaxX then AX := MaxX;
              AY := Y + Random(2 * BinTreeRadius) - BinTreeRadius;
              if AY < 0 then AY := 0;
              if AY > MaxY then AY := MaxY;
              SetForeignUR(TargetX, AX);
              SetForeignUR(TargetY, AY);
              case UR(ChildCount) of
                0:
                begin
                  SetForeignUR(childindex, ci1);
                  SetUR(ChildX1, AX);
                  SetUR(ChildY1, AY);
                end;
                1:
                begin
                  SetForeignUR(childindex, ci2);
                  SetUR(ChildX2, AX);
                  SetUR(ChildY2, AY);
                end;
              end;
              SetUR(ChildCount, UR(ChildCount) + 1);
            end;

            {
            // TreeCheck
            if (Age mod TreeCheckInterval = 0) and (UR(TreeLevel) > 0) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ParentX));
              SetForeignUR(TargetY, UR(ParentY));
              SetForeignUR(ParentX, X);
              SetForeignUR(ParentY, Y);
              SetForeignUR(Information, iTreeCheckQuestion);
              case childindex of
                ci1: SetForeignUR(InformationSource, isChild1);
                ci2: SetForeignUR(InformationSource, isChild2);
              end;
            end;
            }

            if UR(Information) = iSendWarrior then
            begin
              SetColor(255, 0, 0);
              case UR(InformationSource) of
                isParent:
                begin
                  SetUR(ParentMessengerSent, 1);
                  SetUR(Child1MessengerSent, 0);
                  SetUR(Child2MessengerSent, 0);
                end;
                isChild1:
                begin
                  SetUR(ParentMessengerSent, 0);
                  SetUR(Child1MessengerSent, 1);
                  SetUR(Child2MessengerSent, 0);
                end;
                isChild2:
                begin
                  SetUR(ParentMessengerSent, 0);
                  SetUR(Child1MessengerSent, 0);
                  SetUR(Child2MessengerSent, 1);
                end;
              end;
              SetUR(State, sWar);
            end;

            Org := OrgAt(0, 0);
            if (Org <> Species) and (Org <> '') then
            begin
              for I := 0 to 255 do
                SetForeignUR(I, Random(256)); // rozházíme registry
              SetUR(State, sEnemyHere);
            end;

          end;

          //--------------------------------------------------
          // sRunning
          //--------------------------------------------------
          sRunning:
          begin
            if UR(TargetX) < X then Move(W, 1);
            if UR(TargetX) > X then Move(E, 1);
            if UR(TargetY) < Y then Move(N, 1);
            if UR(TargetY) > Y then Move(S, 1);
            if (UR(TargetX) = X) and (UR(TargetY) = Y) then SetUR(State, sNothing);
          end;

          //--------------------------------------------------
          // sEnemyHere
          //--------------------------------------------------
          sEnemyHere:
          begin
            if (UR(ParentMessengerSent) = 0) and (UR(TreeLevel) > 0) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ParentX));
              SetForeignUR(TargetY, UR(ParentY));
              SetForeignUR(Information, iSendWarrior);
              SetForeignUR(InformationX, X);
              SetForeignUR(InformationY, Y);
              case UR(childindex) of
                ci1: SetForeignUR(InformationSource, isChild1);
                ci2: SetForeignUR(InformationSource, isChild2);
              end;
              SetUR(ParentMessengerSent, 1);
            end
            else if (UR(Child1MessengerSent) = 0) and (UR(ChildCount) >= 1) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ChildX1));
              SetForeignUR(TargetY, UR(ChildY1));
              SetForeignUR(Information, iSendWarrior);
              SetForeignUR(InformationX, X);
              SetForeignUR(InformationY, Y);
              SetForeignUR(InformationSource, isParent);
              SetUR(Child1MessengerSent, 1);
            end
            else if (UR(Child2MessengerSent) = 0) and (UR(ChildCount) >= 2) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ChildX2));
              SetForeignUR(TargetY, UR(ChildY2));
              SetForeignUR(Information, iSendWarrior);
              SetForeignUR(InformationX, X);
              SetForeignUR(InformationY, Y);
              SetForeignUR(InformationSource, isParent);
              SetUR(Child2MessengerSent, 1);
            end
            else
              SetUR(State, sNothing);
          end;

          //--------------------------------------------------
          // sWar
          //--------------------------------------------------
          sWar:
          begin
            if (UR(ParentMessengerSent) = 0) and (UR(TreeLevel) > 0) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ParentX));
              SetForeignUR(TargetY, UR(ParentY));
              SetForeignUR(Information, iSendWarrior);
              SetForeignUR(InformationX, UR(InformationX));
              SetForeignUR(InformationY, UR(InformationY));
              case UR(childindex) of
                ci1: SetForeignUR(InformationSource, isChild1);
                ci2: SetForeignUR(InformationSource, isChild2);
              end;
              SetUR(ParentMessengerSent, 1);
            end
            else if (UR(Child1MessengerSent) = 0) and (UR(ChildCount) >= 1) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ChildX1));
              SetForeignUR(TargetY, UR(ChildY1));
              SetForeignUR(Information, iSendWarrior);
              SetForeignUR(InformationX, InformationX);
              SetForeignUR(InformationY, InformationY);
              SetForeignUR(InformationSource, isParent);
              SetUR(Child1MessengerSent, 1);
            end
            else if (UR(Child2MessengerSent) = 0) and (UR(ChildCount) >= 2) then
            begin
              DivCell;
              SetForeignUR(Kind, kMessenger);
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ChildX2));
              SetForeignUR(TargetY, UR(ChildY2));
              SetForeignUR(Information, iSendWarrior);
              SetForeignUR(InformationX, InformationX);
              SetForeignUR(InformationY, InformationY);
              SetForeignUR(InformationSource, isParent);
              SetUR(Child2MessengerSent, 1);
            end
            // else
            //   setur(state,sNothing);
          end;

        end;

      end; // BinTree

      //==================================================
      // MESSENGER
      //==================================================
      kMessenger:
      begin
        if Age = 0 then SetColor(0, 128, 128);

        if Mass < 5 then
        begin
          Eat(5);
          Build(5)
        end;
        if Energy < 5 then
        begin
          Eat(5);
          Digest(5);
        end;

        case UR(State) of

          //--------------------------------------------------
          // sRunning
          //--------------------------------------------------
          sRunning:
          begin
            if UR(TargetX) < X then Move(W, 1);
            if UR(TargetX) > X then Move(E, 1);
            if UR(TargetY) < Y then Move(N, 1);
            if UR(TargetY) > Y then Move(S, 1);
            if (UR(TargetX) = X) and (UR(TargetY) = Y) then SetUR(State, sInforming);
          end;

          //--------------------------------------------------
          // sInforming
          //--------------------------------------------------
          sInforming:
          begin
            if OrgAt(0, 0) = Species then
            begin
              SetForeignUR(Information, UR(Information));
              SetForeignUR(InformationSource, UR(InformationSource));
              SetForeignUR(InformationX, UR(InformationX));
              SetForeignUR(InformationY, UR(InformationY));
              Die;
            end
            else
              Die;
            // neinformujem o narušení stromu -- nedoděláno
            {
            begin
              SetForeignUR(State, sRunning);
              SetForeignUR(TargetX, UR(ParentX));
              SetForeignUR(TargetY, UR(ParentY));
              SetUR(Information, iTargetNotThere);
              SetUR(InformationX, X);
              SetUR(InformationY, Y);
            end;
            }
          end;

        end;

      end; // Messenger

      //==================================================
      // COLLECTOR
      //==================================================
      kCollector:
      begin
        if Age = 0 then SetColor(0, 255, 128);

        if Mass < 5 then
        begin
          Eat(5);
          Build(5)
        end;
        if Energy < 5 then
        begin
          Eat(5);
          Digest(5);
        end;

        case UR(State) of

          //--------------------------------------------------
          // sCollecting
          //--------------------------------------------------
          sCollecting:
          begin
            if UR(TargetX) < X then Move(W, 1);
            if UR(TargetX) > X then Move(E, 1);
            if UR(TargetY) < Y then Move(N, 1);
            if UR(TargetY) > Y then Move(S, 1);
            if (UR(TargetX) = X) and (UR(TargetY) = Y) then
            begin
              SetUR(State, sRunning);
              SetUR(TargetX, UR(ParentX));
              SetUR(TargetY, UR(ParentY));
            end;
            if (UR(ParentX) <> X) or (UR(ParentY) <> Y) then
              Eat(FoodAt(0, 0));
            if FoodIn = 255 then
            begin
              SetUR(State, sRunning);
              SetUR(TargetX, UR(ParentX));
              SetUR(TargetY, UR(ParentY));
            end;
          end;

          //--------------------------------------------------
          // sRunning
          //--------------------------------------------------
          sRunning:
          begin
            if UR(TargetX) < X then Move(W, 1);
            if UR(TargetX) > X then Move(E, 1);
            if UR(TargetY) < Y then Move(N, 1);
            if UR(TargetY) > Y then Move(S, 1);
            if (UR(TargetX) = X) and (UR(TargetY) = Y) then
            begin
              Secrete(FoodIn - 16);
              SetUR(State, sCollecting);
              AX := X + Random(2 * CollectorRadius) - CollectorRadius;
              if AX < 0 then AX := 0;
              if AX > MaxX then AX := MaxX;
              AY := Y + Random(2 * CollectorRadius) - CollectorRadius;
              if AY < 0 then AY := 0;
              if AY > MaxY then AY := MaxY;
              SetUR(TargetX, AX);
              SetUR(TargetY, AY);
              Move(TMoveDirection(Random(4)), 1); // uhnout - nikdo nesmí stát na pozici BinTree kvůli poslům
            end
            else
              Eat(FoodAt(0, 0));
          end;

        end;
      end;

    end;
  end;
end;

exports
  OrgRequiredVersion,
  OrgRun;

end.
