unit UClientAI;

{$mode delphi}

interface

uses
  Classes, SysUtils, UGameClient, UGame, Math, UPlayer, UMap;

type
  { TComputer }

  TComputer = class(TClient)
  protected
    procedure DoTurnStart; override;
  public
    //Targets: TFPGObjectList<TPlayer>;
    CellProcessDirection: Boolean;
    procedure AttackNeutral;
    procedure AttackPlayers;
    procedure InnerMoves;
    procedure IncreaseMoves;
    procedure Process;
    procedure FallBack;
    function AttackersCount(Cell: TPlayerCell): Integer;
  end;


implementation

{ TComputer }

procedure TComputer.DoTurnStart;
begin
  Process;
  Protocol.TurnEnd;
end;

procedure TComputer.AttackNeutral;
var
  AllCells: TPlayerCells;
  TotalPower: Integer;
  AttackPower: Integer;
  TotalAttackPower: Integer;
  CanAttack: Integer;
  TargetPower: Integer;
  TargetCells: TPlayerCells;
  Cell: TPlayerCell;
  NeighborCell: TPlayerCell;
const
  AttackDiff = 1;
begin
  AllCells := ControlPlayer.PlayerMap.Cells;
  TargetCells := TPlayerCells.Create;
  TargetCells.FreeObjects := False;

  // Get list of all attack target cells
  for Cell in AllCells do
  with Cell do begin
    if (MapCell.Terrain <> ttVoid) and (MapCell.Player = nil) then begin
      CanAttack := 0;
      for NeighborCell in Neighbors do
      if NeighborCell.MapCell.Player = ControlPlayer then begin
        Inc(CanAttack);
      end;
      if CanAttack > 0 then TargetCells.Add(Cell);
    end;
  end;

  // Sort ascending to attack cells with lower power first
  // Low power cells are better for expanding our territory
  TargetCells.Sort(CellCompare);

  for Cell in TargetCells do
  with Cell do begin
      // Attack to not owned cell yet
      // Count own possible power
      TotalPower := 0;
      for NeighborCell in Neighbors do
      if NeighborCell.MapCell.Player = ControlPlayer then
        TotalPower := TotalPower + NeighborCell.GetAvialPowerForMove;

      // Attack if target is weaker
      if Assigned(MapCell.OneUnit) then TargetPower := MapCell.OneUnit.Power
        else TargetPower := 0;
      if TotalPower >= (TargetPower + AttackDiff) then begin
        TotalAttackPower := 0;
        for NeighborCell in Neighbors do
        if NeighborCell.MapCell.Player = ControlPlayer then begin
          // Use only necessary power
          AttackPower := TargetPower - TotalAttackPower + AttackDiff;
          if NeighborCell.GetAvialPower < AttackPower then
            AttackPower := NeighborCell.GetAvialPower;
          ControlPlayer.SetMove(NeighborCell, Cell, AttackPower, False);
          TotalAttackPower := TotalAttackPower + AttackPower;
        end;
      end;
  end;

  FreeAndNil(TargetCells);
end;

procedure TComputer.AttackPlayers;
var
  AllCells: TPlayerCells;
  TotalPower: Integer;
  AttackPower: Integer;
  TotalAttackPower: Integer;
  CanAttack: Integer;
  TargetCells: TPlayerCells;
  TargetCell: TPlayerCell;
  NeighborCell: TPlayerCell;
  DefendCount: Integer;
begin
if ControlPlayer.Defensive then Exit;

  AllCells := ControlPlayer.PlayerMap.Cells;
  TargetCells := TPlayerCells.Create;
  TargetCells.FreeObjects := False;

  // Get list of all attack target cells
  for TargetCell in AllCells do begin
    if (TargetCell.MapCell.Terrain <> ttVoid) and (TargetCell.MapCell.Player <> ControlPlayer) and
      (TargetCell.MapCell.Player <> nil) then begin
      CanAttack := 0;
      for NeighborCell in TargetCell.Neighbors do
      if NeighborCell.MapCell.Player = ControlPlayer then begin
        Inc(CanAttack);
      end;
      if CanAttack > 0 then TargetCells.Add(TargetCell);
    end;
  end;

  // Sort descending to attack cells with higher power first
  // Higher power enemy cells can grow faster and is more dangerous
  TargetCells.Sort(CellCompareDescending);

  for TargetCell in TargetCells do begin
      // Attack to not owned cell yet
      // Count own possible power
      TotalPower := 0;
      for NeighborCell in TargetCell.Neighbors do
      if NeighborCell.MapCell.Player = ControlPlayer then begin
        TotalPower := TotalPower + NeighborCell.GetAvialPower;
      end;
      // Attack if target is weaker
      if Assigned(TargetCell.MapCell.OneUnit) then
        DefendCount := TargetCell.MapCell.OneUnit.Power
        else DefendCount := 0;
      if Game.AttackProbability(TotalPower, DefendCount) >=
        ComputerAggroProbability[ControlPlayer.Agressivity] then begin
        // Try to limit total attacking power to necessary minimum
        while Game.AttackProbability(TotalPower - 1, DefendCount) >=
          ComputerAggroProbability[ControlPlayer.Agressivity] do
          Dec(TotalPower);

        // Collect required attack units from our cells
        TotalAttackPower := 0;
        for NeighborCell in TargetCell.Neighbors do
        if NeighborCell.MapCell.Player = ControlPlayer then begin
          // Use only necessary power
          AttackPower := TotalPower - TotalAttackPower;
          if NeighborCell.GetAvialPower < AttackPower then
            AttackPower := NeighborCell.GetAvialPower;
          Self.ControlPlayer.SetMove(NeighborCell, TargetCell, AttackPower, False);
          TotalAttackPower := TotalAttackPower + AttackPower;
          if TotalAttackPower >= TotalPower then Break;
        end;
      end;
  end;

  FreeAndNil(TargetCells);
end;

procedure TComputer.InnerMoves;
var
  AllCells: TPlayerCells;
  I, J: Integer;
  C: Integer;
  CanAttack: Integer;
  TargetCells: TPlayerCells;
  NewTargetCells: TPlayerCells;
  Cells2: TPlayerCells;
  MovedPower: Integer;
begin
  // We need to move available power to borders to be available for attacks
  // or defense
  AllCells := ControlPlayer.PlayerMap.Cells;
  TargetCells := TPlayerCells.Create;
  TargetCells.FreeObjects := False;
  NewTargetCells := TPlayerCells.Create;
  NewTargetCells.FreeObjects := False;

  // Get list of all enemy border cells
  for C := 0 to AllCells.Count - 1 do
  with AllCells[C] do begin
    if (MapCell.Player <> ControlPlayer) and (MapCell.Terrain <> ttVoid) then begin
      CanAttack := 0;
      for I := 0 to Neighbors.Count - 1 do
      if ((Neighbors[I].MapCell.Player = ControlPlayer) or
      (Neighbors[I].MapCell.Player = nil)) and (Neighbors[I].MapCell.Terrain <> ttVoid) then begin
        Inc(CanAttack);
      end;
      if CanAttack > 0 then TargetCells.Add(AllCells[C]);
    end;
  end;

  if CellProcessDirection then begin
    // Reverse array
    for I := 0 to (TargetCells.Count div 2) - 1 do
      TargetCells.Exchange(I, TargetCells.Count - 1 - I);
  end;

  Game.Map.Cells.ClearMark;

  while TargetCells.Count > 0 do begin
    // Set mark for selected border cells
    for C := 0 to TargetCells.Count - 1 do
      TargetCells[C].MapCell.Mark := True;

    // Move all power from unmarked cells and mark them
    NewTargetCells.Count := 0;
    for C := 0 to TargetCells.Count - 1 do
    with TargetCells[C] do begin
      for I := 0 to Neighbors.Count - 1 do begin
        if (Neighbors[I].MapCell.Terrain <> ttVoid) and (not Neighbors[I].MapCell.Mark) then begin
          if (TargetCells[C].MapCell.Player = ControlPlayer) and
          (Neighbors[I].MapCell.Player = ControlPlayer) then begin
            // Do not take units from front line
            Cells2 := Neighbors[I].Neighbors;
            CanAttack := 0;
            for J := 0 to Cells2.Count - 1 do
            if ((Cells2[J].MapCell.Player <> ControlPlayer) or (Cells2[J].MapCell.Player = nil))
            and (Cells2[J].MapCell.Terrain <> ttVoid) then begin
              Inc(CanAttack);
            end;
            if CanAttack = 0 then begin
              MovedPower := Neighbors[I].GetAvialPower;
              if (TargetCells[C].GetAvialPower + TargetCells[C].GetAttackPower + MovedPower) > Game.Map.MaxPower then
                MovedPower := Game.Map.MaxPower - TargetCells[C].GetAvialPower - TargetCells[C].GetAttackPower;
              TPlayer(MapCell.Player).SetMove(Neighbors[I], TargetCells[C], MovedPower, False);
            end;
          end;
          Neighbors[I].MapCell.Mark := True;
          NewTargetCells.Add(Neighbors[I]);
        end;
      end;
    end;

    // Use source cells NewTargetCells as new TargetCells
    FreeAndNil(TargetCells);
    TargetCells := NewTargetCells;
    NewTargetCells := TPlayerCells.Create;
    NewTargetCells.FreeObjects := False;
  end;

  FreeAndNil(TargetCells);
  FreeAndNil(NewTargetCells);
end;

procedure TComputer.IncreaseMoves;
var
  Move: TUnitMove;
  AvailPower: Integer;
begin
  // If available power remains then use all for existed unit moves
  for Move in ControlPlayer.Moves do
  with Move do begin
    if CellFrom.GetAvialPower > 0 then begin
      AvailPower := CellFrom.GetAvialPower;
      CountOnce := CountOnce + Min(AvailPower div CellFrom.MovesFrom.Count, AvailPower);
    end;
  end;
end;

procedure TComputer.Process;
begin
  AttackPlayers;
  AttackNeutral;
  InnerMoves;
  IncreaseMoves;
  //FallBack;
  CellProcessDirection := not CellProcessDirection;
end;

procedure TComputer.FallBack;
var
  C: Integer;
  I: Integer;
  AllCells: TPlayerCells;
  BorderCells: TPlayerCells;
  EnemyPower: Integer;
begin
  BorderCells := TPlayerCells.Create;
  BorderCells.FreeObjects := False;
  AllCells := ControlPlayer.PlayerMap.Cells;

  // Get list of border cells
  for C := 0 to AllCells.Count - 1 do
  with AllCells[C] do begin
    if (MapCell.Terrain <> ttVoid) and (MapCell.Player = ControlPlayer)  then begin
      if AttackersCount(AllCells[C]) > 0 then
        BorderCells.Add(AllCells[C]);
    end;
  end;

  // Move all units back to inner area from weak border cells
  for C := 0 to BorderCells.Count - 1 do
  with TPlayerCell(BorderCells[C]) do begin
    // Calculate enemy power
    // TODO: Do not sum different enemy power to one value
    EnemyPower := 0;
    for I := 0 to Neighbors.Count - 1 do
    if (Neighbors[I].MapCell.Player <> ControlPlayer) and (Neighbors[I].MapCell.Player <> nil) then begin
      Inc(EnemyPower, Neighbors[I].MapCell.OneUnit.Power);
    end;
    if EnemyPower > (GetAvialPower + GetAttackPower) then begin
      // Fallback
      for I := MovesTo.Count - 1 downto 0 do
        TPlayer(MapCell.Player).Moves.Remove(MovesTo[I]);
      for I := 0 to Neighbors.Count - 1 do
      if (Neighbors[I].MapCell.Player = MapCell.Player) and (AttackersCount(Neighbors[I]) = 0) then begin
        TPlayer(MapCell.Player).SetMove(BorderCells[C], Neighbors[I], GetAvialPower, False);
        Break;
      end;
    end;
  end;

  FreeAndNil(BorderCells);
end;

function TComputer.AttackersCount(Cell: TPlayerCell): Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to Cell.Neighbors.Count - 1 do
  if (Cell.Neighbors[I].MapCell.Player <> ControlPlayer) and
    (Cell.Neighbors[I].MapCell.Player <> nil) then begin
      Inc(Result);
  end;
end;


end.

