public string DebugDiffInfo(DangerResult other) { string res = ""; if (!Geom.DoublesEquals(Score, other.Score)) { res += $"Score {Score - other.Score}\n\n"; } if (!Geom.DoublesEquals(MyDurabilityDiff, other.MyDurabilityDiff)) { res += $"MyDurabilityDiff {MyDurabilityDiff - other.MyDurabilityDiff}\n"; } if (!Geom.DoublesEquals(OppDurabilityDiff, other.OppDurabilityDiff)) { res += $"OppDurabilityDiff {OppDurabilityDiff - other.OppDurabilityDiff}\n"; } if (!Geom.DoublesEquals(MyDeadsCount, other.MyDeadsCount)) { res += $"MyDeadsCount {MyDeadsCount - other.MyDeadsCount}\n"; } if (!Geom.DoublesEquals(OppDeadsCount, other.OppDeadsCount)) { res += $"OppDeadsCount {OppDeadsCount - other.OppDeadsCount}\n"; } if (!Geom.DoublesEquals(SumMaxAlmostAttacks, other.SumMaxAlmostAttacks)) { res += $"SumMaxAlmostAttacks {SumMaxAlmostAttacks - other.SumMaxAlmostAttacks}\n"; } if (!Geom.DoublesEquals(NuclearsPotentialDamage, other.NuclearsPotentialDamage)) { res += $"NuclearsPotentialDamage {NuclearsPotentialDamage - other.NuclearsPotentialDamage}\n"; } if (!Geom.DoublesEquals(RectanglesIntersects1, other.RectanglesIntersects1)) { res += $"RectanglesIntersects1 {RectanglesIntersects1 - other.RectanglesIntersects1}\n"; } if (!Geom.DoublesEquals(RectanglesIntersects2, other.RectanglesIntersects2)) { res += $"RectanglesIntersects2 {RectanglesIntersects2 - other.RectanglesIntersects2}\n"; } if (!Geom.DoublesEquals(FacilitiesPointsDiff, other.FacilitiesPointsDiff)) { res += $"FacilitiesPointsDiff {FacilitiesPointsDiff - other.FacilitiesPointsDiff}\n"; } if (!Geom.DoublesEquals(MoveToSum, other.MoveToSum)) { res += $"MoveToSum {MoveToSum - other.MoveToSum}\n"; } if (!Geom.DoublesEquals(MoveToFacilitySum, other.MoveToFacilitySum)) { res += $"MoveToFacilitySum {MoveToFacilitySum - other.MoveToFacilitySum}\n"; } return(res); }
Tuple <AMove[], MyGroup, DangerResult> MainLoopStrategy(bool opt) { VisualSegments.Clear(); var visualVectorsStart = new List <Point>(); var visualVectorsDir = new List <Point>(); var visualVectorsScore = new List <double>(); _isSlowMode = _isSlowMode && Environment.OppVehicles.Count == 0; var baseTicksCount = Const.ActionsBruteforceDepth; if (Environment.Nuclears.Length > 0) { baseTicksCount *= 2; } var selMoves = new[] { new AMove() }; MyGroup selGroup = null; DangerResult selDanger = null; var targetFacilities = GetDanger(Environment, Environment, GroupsManager.MyGroups, MyUngroupedClusters).TargetFacility; for (var s = 0; s < GroupsManager.MyGroups.Count + MyUngroupedClusters.Count; s++) { var availableActions = MoveObserver.AvailableActions; if (Opp.RemainingNuclearStrikeCooldownTicks < 60 && Environment.Nuclears.Length == 0) { availableActions -= 2; } MyGroup group; List <AVehicle> newGroupVehicles = null; if (s < GroupsManager.MyGroups.Count) { group = GroupsManager.MyGroups[s]; } else { newGroupVehicles = MyUngroupedClusters[s - GroupsManager.MyGroups.Count]; if (newGroupVehicles.Count < NewGroupMinSize) { continue; } if (availableActions < 3) // required: clear, move, assign { continue; } group = new MyGroup(GroupsManager.NextGroupId, newGroupVehicles[0].Type); } if (opt && !( Environment.Nuclears.Length > 0 || Environment.Facilities.Length <= 4 || World.TickIndex < 2500 || MoveObserver.MaxAvailableActions <= 12 || _doMainLastGroup != null && _doMainLastGroup.Group == group.Group || // ходил предыдущий раз (newGroupVehicles == null && group.Group % 2 == _doMainsCount % 2 || newGroupVehicles != null && group.Group % 3 == _doMainsCount % 3) // через раз/два )) { continue; } if (_isSlowMode && World.TickIndex < 300 && group.VehicleType == VehicleType.Helicopter) { continue; } Logger.CumulativeOperationStart("First env actions"); var startEnv = Environment.Clone(); var ticksCount = baseTicksCount; if (newGroupVehicles != null) { foreach (var newVeh in newGroupVehicles) { startEnv.AddVehicleGroup(startEnv.VehicleById[newVeh.Id], group.Group); } } var selectedIds = Utility.UnitsHash(startEnv.MyVehicles.Where(x => x.IsSelected)); var needToSelectIds = Utility.UnitsHash(startEnv.GetVehicles(true, group)); List <AMove> selectionMoves = new List <AMove>(); if (selectedIds != needToSelectIds) { if (availableActions < 2) // required: select, move { Logger.CumulativeOperationEnd("First env actions"); continue; } if (newGroupVehicles == null) { selectionMoves.Add(AMovePresets.ClearAndSelectGroup(group.Group)); } else { var selectionMove = AMovePresets.ClearAndSelectType(group.VehicleType, Utility.BoundingRect(newGroupVehicles)); startEnv.ApplyMove(selectionMove); // ниже выполнится лишний раз, но не страшно selectionMoves.Add(selectionMove); foreach (var mg in GroupsManager.MyGroups) { if (mg.VehicleType == group.VehicleType && startEnv.MyVehicles.Where(x => x.IsSelected).Any(x => x.IsGroup(mg))) { selectionMoves.Add(new AMove { Action = ActionType.Deselect, Group = mg.Group }); } } } if (availableActions < selectionMoves.Count + 1) // required: select, move, deselect all { Logger.CumulativeOperationEnd("First env actions"); continue; } foreach (var mv in selectionMoves) { startEnv.ApplyMove(mv); availableActions--; } } availableActions--; Logger.CumulativeOperationEnd("First env actions"); // availableActions теперь - это сколько действий останется после выполнения текущего тика // должно остаться хотябы 2, или 0, если обладатель ядерки selected var myNuclear = startEnv.Nuclears.FirstOrDefault(n => n.IsMy); if (myNuclear != null && availableActions < 2 - (startEnv.VehicleById.ContainsKey(myNuclear.VehicleId) && startEnv.VehicleById[myNuclear.VehicleId].IsSelected ? 2 : 0)) { continue; } var typeRect = Utility.BoundingRect(startEnv.GetVehicles(true, group)); Sandbox partialEnv = null; var sumMaxAlmostAttacksCache = -1.0; if (Environment.Nuclears.Length == 0) { Logger.CumulativeOperationStart("Partial env actions"); partialEnv = new Sandbox( startEnv.Vehicles.Where(x => !x.IsSelected), new ANuclear[] { }, startEnv.Facilities, clone: true ); partialEnv.CheckCollisionsWithOpponent = false; var grps = GroupsManager.MyGroups.Select(g => partialEnv.GetVehicles(true, g).ToArray()).ToArray(); try { for (var i = 0; i < ticksCount; i++) { for (var j = 0; j < grps.Length; j++) { var pgr = grps[j]; if (pgr.Length > 0) { partialEnv.DoMoveApprox(pgr, pgr[0].Action != AVehicle.MoveType.Scale); } } partialEnv.DoFacilities(); partialEnv.DoNuclears(); } partialEnv.DoFight(); sumMaxAlmostAttacksCache = GetSumMaxAlmostAttacks(partialEnv, partialEnv.MyVehicles); } catch (QuadTree <AVehicle> .PointAlreadyExistsException e) { Logger.Log(e.Message); continue; } finally { Logger.CumulativeOperationEnd("Partial env actions"); } } Logger.CumulativeOperationStart("Pre last env actions"); Sandbox preLastEnv = null; AVehicle[] nearInteractors = null; List <AVehicle> currentVehicles = null; AVehicle[] preLastEnvVehiclesCopy = null; if (partialEnv != null) { currentVehicles = startEnv.GetVehicles(true, group); var currentVehiclesCenter = Utility.BoundingRect(currentVehicles).Center; nearInteractors = partialEnv.Vehicles .Where(x => !x.IsMy && x.GetDistanceTo2(currentVehiclesCenter) < Geom.Sqr(2 * G.TacticalNuclearStrikeRadius) || x.IsMy && x.IsAerial == Utility.IsAerial(group.VehicleType) && x.GetDistanceTo2(currentVehiclesCenter) < Geom.Sqr(G.TacticalNuclearStrikeRadius)) .ToArray(); preLastEnv = new Sandbox( partialEnv.Vehicles.Concat(currentVehicles), new ANuclear[] { }, new AFacility[] { }, clone: true ); preLastEnvVehiclesCopy = preLastEnv.Vehicles.Select(x => new AVehicle(x)).ToArray(); } Logger.CumulativeOperationEnd("Pre last env actions"); Func <AMove, double> checkAction = move => { Logger.CumulativeOperationStart("Building nearby environment"); Sandbox env; if (partialEnv == null) { env = startEnv.Clone(); } else { env = new Sandbox(nearInteractors.Concat(currentVehicles), startEnv.Nuclears, partialEnv.Facilities, clone: true); } env.CheckCollisionsWithOpponent = false; env.UseFightOptimization = false; if (partialEnv != null) { foreach (var veh in env.Vehicles) { if (veh.IsGroup(group)) { continue; } veh.ForgotTarget(); // чтобы не шли повторно veh.CanChargeFacility = false; // чтобы не захватывали повторно // TODO: лечение if (veh.RemainingAttackCooldownTicks > 0) // у тех, кто стрелял давно, откатываем кд { veh.RemainingAttackCooldownTicks += ticksCount; } // TODO: если кд только восстановилось } } Logger.CumulativeOperationEnd("Building nearby environment"); Logger.CumulativeOperationStart("End of simulation"); env.ApplyMove(move); try { env.DoTicksApprox(ticksCount, moveApprox: move.Action == ActionType.Move || move.Action == ActionType.Rotate, fightApprox: true); } catch (QuadTree <AVehicle> .PointAlreadyExistsException e) { Logger.Log(e.Message); return(double.PositiveInfinity); } finally { Logger.CumulativeOperationEnd("End of simulation"); } Logger.CumulativeOperationStart("Building last environment"); var cache = sumMaxAlmostAttacksCache; if (partialEnv != null) { var tmp = env; env = preLastEnv; for (var i = 0; i < env.Vehicles.Length; i++) { var veh = env.Vehicles[i]; if (tmp.VehicleById.ContainsKey(veh.Id)) { env.UpdateVehicle(veh, tmp.VehicleById[veh.Id]); } else { env.UpdateVehicle(veh, preLastEnvVehiclesCopy[i]); } } env.Nuclears = tmp.Nuclears; env.Facilities = tmp.Facilities; cache += GetSumMaxAlmostAttacks(env, env.GetVehicles(true, group)); } var myGroups = GroupsManager.MyGroups.AsEnumerable(); var myUngroups = MyUngroupedClusters.AsEnumerable(); if (newGroupVehicles != null) { myGroups = myGroups.ConcatSingle(group); myUngroups = myUngroups.Where(cl => !cl.Equals(newGroupVehicles)); } if (env.Vehicles.Length != Environment.Vehicles.Length) { throw new Exception("Final sandbox size mismatch"); } Logger.CumulativeOperationEnd("Building last environment"); var danger = GetDanger(Environment, env, myGroups.ToList(), myUngroups.ToList(), cache); if (move.Action == ActionType.Move) { visualVectorsStart.Add(typeRect.Center); visualVectorsDir.Add(new Point(move.X, move.Y).Normalized()); visualVectorsScore.Add(danger.Score); } if (selDanger == null || danger.Score < selDanger.Score) { selDanger = danger; selMoves = selectionMoves.Concat(new[] { move, newGroupVehicles == null ? null : AMovePresets.AssignGroup(group.Group) }) .Where(x => x != null) .ToArray(); selGroup = group; } return(danger.Score); }; var typeRectCenter = typeRect.Center; var simpleMode = Environment.Nuclears.Length == 0 && ( Environment.OppVehicles.Count == 0 || Environment.OppVehicles.Min(x => x.GetDistanceTo2(typeRectCenter)) > Geom.Sqr(120)); var moveMaxSpeed = _isSlowMode && Utility.IsAerial(group.VehicleType) ? G.MaxSpeed[(int)group.VehicleType] * 0.6 : 0; Func <int, AMove> idxToMove = idx => AMovePresets.Move( Point.ByAngle(2 * Math.PI / 12 * idx) * startEnv.MyVehicles.Where(x => x.IsSelected).Max(x => x.ActualSpeed) * ticksCount * 40, group.Group == GroupsManager.StartingTanksGroupId && Const.MixArrvsWithGrounds ? Math.Min(G.MaxSpeed[(int)VehicleType.Tank], G.MaxSpeed[(int)VehicleType.Arrv]) : group.Group == GroupsManager.StartingIfvsGroupId && Const.MixArrvsWithGrounds ? Math.Min(G.MaxSpeed[(int)VehicleType.Ifv], G.MaxSpeed[(int)VehicleType.Arrv]) : moveMaxSpeed ); double[] dangers; if (simpleMode) { // проверка на каждые 3 часа dangers = Enumerable.Range(0, 4).Select(i => checkAction(idxToMove(i * 3))).ToArray(); } else { // проверка на каждые 2 часа dangers = Enumerable.Range(0, 6).Select(i => checkAction(idxToMove(i * 2))).ToArray(); // проверка на середины лучших промежутков var dangers2 = dangers.Select((x, i) => new Tuple <double, int>(x, i)).OrderBy(x => x.Item1).Select(x => x.Item2).Take(2).ToArray(); foreach (var i in dangers2.Select(i => i * 2 + 1).Concat(dangers2.Select(i => i * 2 - 1)).Distinct()) { checkAction(idxToMove(i)); } } foreach (var move in Environment.Nuclears .Where(n => n.GetDistanceTo(Utility.Average(startEnv.MyVehicles.Where(x => x.IsSelected))) < G.TacticalNuclearStrikeRadius * 2) .SelectMany(nuclear => new[] { AMovePresets.Scale(nuclear, 1.5), AMovePresets.Scale(nuclear, 3.0), Environment.VehicleById.ContainsKey(nuclear.VehicleId) ? AMovePresets.Scale((Environment.VehicleById[nuclear.VehicleId] + nuclear) / 2, 2.0) : null, })) { if (move != null) { checkAction(move); } } checkAction(AMovePresets.Scale(typeRect.Center, 0.1)); if (_doMainLastUnscale.ContainsKey(group.Group)) { var prevScaleMove = _doMainLastUnscale[group.Group].Item2; checkAction(AMovePresets.Scale(prevScaleMove.Point, 1 / prevScaleMove.Factor)); } checkAction(AMovePresets.Rotate(typeRect.Center, Math.PI / 4)); checkAction(AMovePresets.Rotate(typeRect.Center, -Math.PI / 4)); List <Point> positiveInteractors = new List <Point>(), negativeInteractors = new List <Point>(); foreach (var cluster in OppClusters) { foreach (var oppType in Const.AllTypes) { if (cluster.CountByType[(int)oppType] > 0) { var avg = Utility.Average(cluster.VehicleType(oppType)); if (G.AttackDamage[(int)group.VehicleType, (int)oppType] > 0) { positiveInteractors.Add(avg); } else if (G.AttackDamage[(int)oppType, (int)group.VehicleType] > 0) { negativeInteractors.Add(avg); } } } } foreach (var move in positiveInteractors.OrderBy(cen => cen.GetDistanceTo2(typeRect.Center)) .Take(2) .Select(cen => AMovePresets.MoveTo(typeRect.Center, cen, moveMaxSpeed))) { checkAction(move); } foreach (var move in negativeInteractors.OrderBy(cen => cen.GetDistanceTo2(typeRect.Center)) .Take(1) .Select(cen => AMovePresets.Move((typeRect.Center - cen).Take(150), moveMaxSpeed))) { checkAction(move); } if (targetFacilities.ContainsKey(group.Group)) { checkAction(AMovePresets.MoveTo(typeRect.Center, targetFacilities[group.Group], moveMaxSpeed)); } // проверка действия "ничего не делать" if (selectionMoves.Count == 0) { checkAction(new AMove()); } // полет к ближайшему Arrv if (group.VehicleType == VehicleType.Helicopter || group.VehicleType == VehicleType.Fighter) { var arrvGroupsCenters = GroupsManager.MyGroups .Where(g => g.VehicleType == VehicleType.Arrv) .Select(g => Utility.BoundingRect(Environment.GetVehicles(true, g)).Center) .ToArray(); if (arrvGroupsCenters.Length > 0) { checkAction(AMovePresets.MoveTo(typeRect.Center, arrvGroupsCenters.ArgMin(p => p.GetDistanceTo2(typeRect.Center)), moveMaxSpeed)); } } } #if DEBUG //if (selDanger != null) //{ // var maxScore = visualVectorsScore.Max(); // var minScore = visualVectorsScore.Min(); // if (maxScore > minScore) // { // for (var i = 0; i < visualVectorsStart.Count; i++) // { // VisualSegments.Add(new object[] // { // new List<Point> // { // visualVectorsStart[i], // visualVectorsStart[i] + visualVectorsDir[i]*((visualVectorsScore[i]-minScore)/(minScore-maxScore)*40) // }, // new System.Drawing.Pen(System.Drawing.Color.Green), // 5 // }); // } // } //} #endif return(new Tuple <AMove[], MyGroup, DangerResult>(selMoves, selGroup, selDanger)); }
public static DangerResult GetDanger(Sandbox startEnv, Sandbox env, List <MyGroup> myGroups, List <VehiclesCluster> myUngroups, double sumMaxAlmostAttacksCache = -1) { ExpB = Math.Log(ExpY1 / ExpY2) / (ExpX2 - ExpX1); ExpA = ExpY1 / Math.Exp(-ExpB * ExpX1); Logger.CumulativeOperationStart("GetDanger"); var result = new DangerResult(); if (sumMaxAlmostAttacksCache <= -1) { result.SumMaxAlmostAttacks = GetSumMaxAlmostAttacks(env, env.MyVehicles); } else { result.SumMaxAlmostAttacks = sumMaxAlmostAttacksCache; } Logger.CumulativeOperationStart("Danger1"); var myDurabilityBefore = startEnv.MyVehicles.Sum(x => x.FullDurability); var oppDurabilityBefore = startEnv.OppVehicles.Sum(x => x.FullDurability); var myDurabilityAfter = env.MyVehicles.Sum(x => x.FullDurability); var oppDurabilityAfter = env.OppVehicles.Sum(x => x.FullDurability); result.MyDurabilityDiff = myDurabilityBefore - myDurabilityAfter; result.OppDurabilityDiff = oppDurabilityBefore - oppDurabilityAfter; result.MyDeadsCount = env.MyVehicles.Count(x => !x.IsAlive); result.OppDeadsCount = env.OppVehicles.Count(x => !x.IsAlive); result.FacilitiesPointsDiff = startEnv.Facilities.Sum(x => x.CapturePoints) - env.Facilities.Sum(x => x.CapturePoints); // nuclears foreach (var nuclear in env.Nuclears) { foreach (var target in env.GetAllNeighbours(nuclear.X, nuclear.Y, nuclear.Radius)) { result.NuclearsPotentialDamage += Utility.TrueFactor(target.IsMy) * target.GetNuclearDamage(nuclear); } } // groups intersections var groupsIsAerial = myGroups.Select(g => Utility.IsAerial(g.VehicleType)) .Concat(myUngroups.Select(cl => cl[0].IsAerial)) .ToArray(); var groupsRects = myGroups.Select(g => Utility.BoundingRect(env.GetVehicles(true, g))) .Concat(myUngroups.Select(cl => cl.BoundingRect)) .ToArray(); for (var j = 0; j < groupsRects.Length; j++) { var rect = groupsRects[j]; if (rect.X < G.VehicleRadius || rect.Y < G.VehicleRadius || rect.X + G.VehicleRadius > G.MapSize || rect.Y + G.VehicleRadius > G.MapSize) { result.RectanglesIntersects1++; continue; } for (var i = 0; i < j; i++) { if (groupsIsAerial[i] != groupsIsAerial[j]) { continue; } var rect1 = groupsRects[i].Clone(); var rect2 = groupsRects[j].Clone(); rect1.ExtendRadius(G.VehicleRadius * 1.5); rect2.ExtendRadius(G.VehicleRadius * 1.5); if (rect1.IntersectsWith(rect2)) { result.RectanglesIntersects1++; continue; } rect1.ExtendRadius(G.VehicleRadius * 1.5); rect2.ExtendRadius(G.VehicleRadius * 1.5); if (rect1.IntersectsWith(rect2)) { result.RectanglesIntersects2++; } } } Logger.CumulativeOperationEnd("Danger1"); var clusters = OppClusters; // NOTE: юниты врага считаются неподвижными, поэтому берем инстансы из основного Environment Logger.CumulativeOperationStart("Danger2"); var groundGroupsCenters = new List <Point>(); var groundGroups = new List <List <AVehicle> >(); var groundGroupsIdxes = new List <int>(); var groundGroupsId = new List <MyGroup>(); for (var s = 0; s < myGroups.Count + myUngroups.Count; s++) { VehicleType type; MyGroup gr = null; List <AVehicle> myGroup; if (s < myGroups.Count) { gr = myGroups[s]; type = gr.VehicleType; myGroup = env.GetVehicles(true, gr); } else { myGroup = myUngroups[s - myGroups.Count]; type = myGroup[0].Type; } var myGroupCenter = Utility.Average(myGroup); var list = new List <DangerResult.ScoreDistancePair>(); var myRatio = 1.0 * myGroup.Count / env.MyVehicles.Count; foreach (var cl in clusters) { for (var oppType = 0; oppType < 5; oppType++) { if (cl.CountByType[oppType] == 0) { continue; } var myAttack = G.AttackDamage[(int)type, oppType]; var oppAttack = G.AttackDamage[oppType, (int)type]; var avg = Utility.Average(cl.VehicleType((VehicleType)oppType)); var dist = avg.GetDistanceTo(myGroupCenter); var score = myAttack - oppAttack * 0.49; if (type == VehicleType.Helicopter && oppType == (int)VehicleType.Fighter) { score *= 2.5; } else if (type == VehicleType.Helicopter && oppType == (int)VehicleType.Arrv) { score *= 0.8; } var maxDurability = cl.CountByType[oppType] * G.MaxDurability; var durabilityCoeff = (maxDurability - cl.DurabilitySumByType[oppType]) / G.MaxDurability; if (score < 0) { durabilityCoeff *= -1; } score = score * (cl.CountByType[oppType] + durabilityCoeff) * myRatio; var e = -(myAttack == 0 && dist > 100 || myAttack <oppAttack && dist> (type == VehicleType.Helicopter ? 350 : 140) ? 0 : DangerExp(dist)); list.Add(new DangerResult.ScoreDistancePair(score, e)); } } foreach (var helpGroup in myGroups) { if (type == VehicleType.Helicopter && helpGroup.VehicleType == VehicleType.Ifv) { var fightersCount = env.GetVehicles(false, VehicleType.Fighter).Count; if (fightersCount == 0) { continue; } var helpGroupVehicles = env.GetVehicles(true, helpGroup); var helpGroupVehiclesCenter = Utility.BoundingRect(helpGroupVehicles).Center; var myAttack = G.AttackDamage[(int)type, (int)VehicleType.Fighter]; var oppAttack = G.AttackDamage[(int)VehicleType.Fighter, (int)type]; var score = (myAttack - oppAttack * 0.49) * 2.5; score = score * fightersCount * myRatio; var dist = myGroupCenter.GetDistanceTo(helpGroupVehiclesCenter); var distToFighter = Math.Sqrt(env.GetVehicles(false, VehicleType.Fighter).Min(x => x.GetDistanceTo2(myGroupCenter))); const double n = 400; var coef = Math.Max(0, (n - distToFighter) / n); list.Add(new DangerResult.ScoreDistancePair(coef * score, DangerExp(dist))); } if ((type == VehicleType.Helicopter || type == VehicleType.Fighter) && helpGroup.VehicleType == VehicleType.Arrv) { var helpGroupVehicles = env.GetVehicles(true, helpGroup); var helpGroupVehiclesCenter = Utility.BoundingRect(helpGroupVehicles).Center; var score = -(myGroup.Count * G.MaxDurability - myGroup.Sum(x => x.Durability)) * myRatio; var dist = myGroupCenter.GetDistanceTo(helpGroupVehiclesCenter); list.Add(new DangerResult.ScoreDistancePair(score, DangerExp(dist))); } } result.MoveToInfo.Add(new Tuple <MyGroup, int, List <DangerResult.ScoreDistancePair> >(gr, s, list)); // scale groups var boundingRect = Utility.BoundingRect(myGroup); boundingRect.ExtendRadius(G.VehicleRadius); result.RectanglesAreas.Add(new Tuple <double, int>(boundingRect.Area, myGroup.Count)); if (!Utility.IsAerial(type) && s < myGroups.Count) { groundGroups.Add(myGroup); groundGroupsCenters.Add(myGroupCenter); groundGroupsId.Add(gr); groundGroupsIdxes.Add(s); } } var targetFacilities = env.Facilities.Where(x => !x.IsMy).ToArray(); if (groundGroups.Count > 0 && targetFacilities.Length > 0) { var mat = new double[groundGroups.Count][]; for (var i = 0; i < groundGroups.Count; i++) { mat[i] = new double[targetFacilities.Length]; for (var j = 0; j < targetFacilities.Length; j++) { mat[i][j] = groundGroupsCenters[i].GetDistanceTo(targetFacilities[j].Center); } } var asg = HungarianAssignment.Minimize(mat, 2 * G.MapSize); for (var i = 0; i < groundGroups.Count; i++) { var myGroup = groundGroups[i]; var cen = groundGroupsCenters[i]; var myRatio = 1.0 * myGroup.Count / env.MyVehicles.Count; var flist = new List <DangerResult.ScoreDistancePair>(); if (asg[i] == -1) { continue; } var facility = targetFacilities[asg[i]]; result.TargetFacility[groundGroupsId[i].Group] = facility.Center; var dist = cen.GetDistanceTo(facility.Center); var score = -myRatio * (groundGroupsId[i].VehicleType == VehicleType.Arrv ? 4 : 1); flist.Add(new DangerResult.ScoreDistancePair(score, DangerExp(dist))); result.MoveToFacilitiesInfo.Add(new Tuple <MyGroup, int, List <DangerResult.ScoreDistancePair> >(groundGroupsId[i], groundGroupsIdxes[i], flist)); } } Logger.CumulativeOperationEnd("Danger2"); Logger.CumulativeOperationEnd("GetDanger"); return(result); }