type GroupData = record
	groupId: integer;
    groupHaltTime: integer;
  end;
  
type GroupIdleData = record
	groupId: integer;
    groupIdleCount: integer;
  end;

type GroupHouseAttackData = record
	groupId: integer;
    houseId: integer;
  end;
  
type FirstAttackPhaseData = record
	firstAttackPhasePosition: TKMPoint;
    firstAttackPhaseDirection: TKMDirection;
  end;

type AiDefenceGroupParameters = record
	aiDefenceGroupId: integer;
    aiDefenceLeashBreakCount: integer;
    aiDefenceLeashBreakCountResetTiming: integer;
    aiDefencePositionsAssignmentsX: integer;
	aiDefencePositionsAssignmentsY: integer;
	aiDefencePositionsAssignmentsRadius: integer;
  end;
 
type Rectangle = record
	topLeftCorner: TKMPoint;
    bottomRightCorner: TKMPoint;
  end;

var alliancesPerPlayerIdMap: array  [0..129]  of array  [0..129]  of boolean;

procedure FillAlliancesPerPlayerIdMap;
var i: integer;
var j: integer;
begin  	
	begin
	   for i := 0 to States.StatPlayerCount - 1 do
		for j := 0 to States.StatPlayerCount - 1 do
			if (States.PlayerAllianceCheck(i,j)) then
				begin
					alliancesPerPlayerIdMap[i][j]:= true; 
				end
			else
				begin
					alliancesPerPlayerIdMap[i][j]:= false;
				end;
	end;
end;

function distance(x1: integer; y1: integer; x2: integer; y2: integer): double;
begin
  Result := sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
end;
	
function IsPointInRangeCircle(centerX: integer; centerY: integer; radius: double; pointX: integer; pointY: integer): boolean;
begin
	Result := Utils.Sqr(pointX - centerX) + Utils.Sqr(pointY - centerY) < Utils.Sqr(radius); 
end;	
	
function IsPointInRangeRectangle(rectangle: Rectangle; point: TKMPoint): boolean;
begin
	Result := (point.X > rectangle.topLeftCorner.X) and (point.X < rectangle.bottomRightCorner.X) and (point.Y > rectangle.topLeftCorner.Y) and (point.Y < rectangle.bottomRightCorner.Y);  
end;	

function AddElementToGroupHouseAttackDataArray(A: array of GroupHouseAttackData; value: GroupHouseAttackData): array of GroupHouseAttackData;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;	

function AddElementToGroupIdleDataArray(A: array of GroupIdleData; value: GroupIdleData): array of GroupIdleData;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;

function AddElementToTKMDefencePositionInfoArray(A: array of TKMDefencePositionInfo; value: TKMDefencePositionInfo): array of TKMDefencePositionInfo;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;	
	

function AddElementToGroupDataArray(A: array of GroupData; value: GroupData): array of GroupData;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;	

function AddElementToIntegerArray(A: array of integer; value: integer): array of integer;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;

function AddElementToRectangleArray(A: array of Rectangle; value: Rectangle): array of Rectangle;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;	
	
	
function AddElementToAiDefenceGroupParametersArray(A: array of AiDefenceGroupParameters; value: AiDefenceGroupParameters): array of AiDefenceGroupParameters;
begin
	SetLength(A, Length(A) + 1);
	A[High(A)] := value;
	Result := A;
end;	

function GetIndexInGroupIdleDataArray(A: array of GroupIdleData; groupId: integer): integer;
var i: integer;
var returnValue: integer;
begin
	returnValue := -1;
	for i := 0 to Length(A) - 1 do
		begin
			if (A[i].groupId = groupId) then
				begin
					returnValue := i;
					Break;
				end;
		end;
	Result := returnValue;
end;

function DeleteIndexesFromGroupIdleDataArray(A: array of GroupIdleData; indexesToDelete: array of integer): array of GroupIdleData;
var len: integer;
var i,j: integer;
var resultArray: array of GroupIdleData;
begin
  len := Length(A);
  if((len > 0) and (Length(indexesToDelete) > 0)) then 
	begin
		SetLength(resultArray, len - Length(indexesToDelete));
		j:=0;
		for i := 0 to len - 1 do
			begin
				if (not Utils.ArrayHasElementI(i,indexesToDelete)) then
					begin
						resultArray[j] := A[i]; 
						j:=j+1;
					end;
			end;
		SetLength(A, Length(resultArray));
		Result := resultArray;
	end;
end;

function ComparePoints(p1: TKMPoint; p2: TKMPoint): boolean;
begin
	if((p1.X = p2.X) and (p1.Y = p2.Y)) then 
		begin
			Result := true;
		end
	else
		begin
			Result := false;
		end;
end;

function DeleteIndexesFromGroupDataArray(A: array of GroupData; indexesToDelete: array of integer): array of GroupData;
var len: integer;
var i,j: integer;
var resultArray: array of GroupData;
begin
  len := Length(A);
  if((len > 0) and (Length(indexesToDelete) > 0)) then 
	begin
		SetLength(resultArray, len - Length(indexesToDelete));
		j:=0;
		for i := 0 to len - 1 do
			begin
				if (not Utils.ArrayHasElementI(i,indexesToDelete)) then
					begin
						resultArray[j] := A[i]; 
						j:=j+1;
					end;
			end;
		SetLength(A, Length(resultArray));
		Result := resultArray;
	end;
end;

function GetIndexInGroupDataArray(A: array of GroupData; groupId: integer): integer;
var i: integer;
var returnValue: integer;
begin
	returnValue := -1;
	for i := 0 to Length(A) - 1 do
		begin
			if (A[i].groupId = groupId) then
				begin
					returnValue := i;
					Break;
				end;
		end;
	Result := returnValue;
end;	

function GetIndexInDefencePositionsArray(playerId: integer; defenceUID: integer): integer;
var defencePosition: TKMDefencePositionInfo;
var returnValue: integer;
var i: integer;
begin
	returnValue := -1;
	for i := 0 to States.StatAIDefencePositionsCount(playerId) - 1 do
		begin
			defencePosition := States.AIDefencePositionGetByIndex(playerId, i);	
			if(defencePosition.UID = defenceUID) then
				begin
					returnValue := i;
					Break;
				end;
		end;
	Result := returnValue;
end;
	
function GetAiDefenceGroupParametersForGroup(A: array of AiDefenceGroupParameters; groupId: integer): integer;
var i: integer;
begin
	for i := 0 to Length(A) - 1 do
		begin
			if (A[i].aiDefenceGroupId = groupId) then
				begin
					Result := i;
					Break;
				end;
		end;
end;	

function HasAiDefenceGroupParametersForGroup(A: array of AiDefenceGroupParameters; groupId: integer): boolean;
var i: integer;
var returnValue: boolean;
begin
	returnValue := false;
	for i := 0 to Length(A) - 1 do
		begin
			if (A[i].aiDefenceGroupId = groupId) then
				begin
					returnValue := true;
					Break;
				end;
		end;
	Result := returnValue;
end;	
		

function DeleteIndexesFromAiDefenceGroupParametersArray(A: array of AiDefenceGroupParameters; indexesToDelete: array of integer): array of AiDefenceGroupParameters;
var len: integer;
var i,j: integer;
var resultArray: array of AiDefenceGroupParameters;
begin
  len := Length(A);
  if((len > 0) and (Length(indexesToDelete) > 0)) then 
	begin
		SetLength(resultArray, len - Length(indexesToDelete));
		j:=0;
		for i := 0 to len - 1 do
			begin
				if (not Utils.ArrayHasElementI(i,indexesToDelete)) then
					begin
						resultArray[j] := A[i]; 
						j:=j+1;
					end;
			end;
		SetLength(A, Length(resultArray));
		Result := resultArray;
	end;
end;

function GetSquareAreaAroundGroupLeader(groupId: integer; areaRadius: integer): Rectangle;
var groupPosition: TKMPoint;
var area: Rectangle;
begin
	if(States.GroupMember(groupId,0) <> -1) then 
		begin
			groupPosition := States.UnitPosition(States.GroupMember(groupId,0));
			area.topLeftCorner.X :=	Utils.MaxI(groupPosition.X - areaRadius, 1);
			area.topLeftCorner.Y := Utils.MaxI(groupPosition.Y - areaRadius, 1);
			area.bottomRightCorner.X := Utils.MinI(groupPosition.X + areaRadius, States.MapWidth() - 1);
			area.bottomRightCorner.Y :=	Utils.MinI(groupPosition.Y + areaRadius, States.MapHeight() - 1);		
		end;
	Result := area;
end;

function GetClosestHousePlanIdFromGroupInRange(groupId: integer; range: Rectangle): integer;
var housePosition: TKMPoint;
var houseID: integer;
var i,j: integer;
begin
	houseId := -1;
	if(range.topLeftCorner.X <> 0) then 
		begin
			for i := range.topLeftCorner.X to range.bottomRightCorner.X do
				begin
					for j := range.topLeftCorner.Y to range.bottomRightCorner.Y do
						begin
							houseID := States.HouseAt(i, j);		
							if((houseID <> -1) and (States.HouseOwner(houseID) = 0) and (States.HouseBuildingProgress(houseID) = 0)) then
								begin
									housePosition := States.HousePosition(houseID);				
									Break;
								end
							else
								begin
									houseID := -1;
								end;
						end;
					if(houseID <> -1) then 
					begin
						Break;
					end;
				end;
		end;
	Result := houseId;	
end;	

function GetClosestHouseFromGroup(opponentPlayerId: integer; groupId: integer): integer;
var currentHousePosition: TKMPoint;
var houseID: integer;
var currentHouseId: integer;
var groupPosition: TKMPoint;
var unitId: integer;
var distanceClosestHouse: double;
var currentDistance: double;
var playerHouseIds: array of integer;
var j: integer;
begin
	houseId := -1;
	unitId := States.GroupMember(groupId,0);
	if(unitId <> -1) then
		begin
			groupPosition := States.UnitPosition(unitId);
			distanceClosestHouse := 9999;
			playerHouseIds := States.PlayerGetAllHouses(opponentPlayerId);
			for j := 0 to Length(playerHouseIds) - 1 do
				begin
					currentHouseId := playerHouseIds[j];
					currentHousePosition := States.HousePosition(currentHouseId);
					currentDistance := distance(currentHousePosition.X, currentHousePosition.Y, groupPosition.X, groupPosition.Y);						
					if(currentDistance < distanceClosestHouse) then
						begin
							distanceClosestHouse := currentDistance;
							houseID := currentHouseId;
						end;
				end;
		end;
	Result := houseId;	
end;	
		
	
function isPlayerDestroyed(playerId: integer): boolean;
var i: integer;
var resultValue: boolean;
var playerHouseIds: array of integer;
begin
	resultValue := true;
	playerHouseIds := States.PlayerGetAllHouses(playerId);
	for i := 0 to Length(playerHouseIds) - 1 do
		begin
			if(not States.HouseDestroyed(playerHouseIds[i])) then
				begin
					resultValue := false;
				end;
		end;
	Result := resultValue;
end;		