private static bool CanPass(Point from, Point to) { if (to.X < Const.WizardRadius || to.Y < Const.WizardRadius || to.X > Const.MapSize - Const.WizardRadius || to.Y > Const.MapSize - Const.WizardRadius) { return(false); } if (from.GetDistanceTo2(_startState) > Geom.Sqr(_startState.VisionRange)) // нет смысла проверять препятствия далеко { return(true); } return(_obstacles.All(ob => !Geom.SegmentCircleIntersects(from, to, ob, ob.Radius + Const.WizardRadius + MagicConst.RadiusAdditionalEpsilon))); }
public bool CanStaffAttack(ACircularUnit unit, bool checkCooldown = true) { if (!CanUseStaff(checkCooldown)) { return(false); } if (GetDistanceTo2(unit) > Geom.Sqr(MyStrategy.Game.StaffRange + unit.Radius)) { return(false); } if (Math.Abs(GetAngleTo(unit)) > MyStrategy.Game.StaffSector / 2) { return(false); } return(true); }
public override bool EthalonCanHit(ACircularUnit target, bool checkCooldown = true) { if (RemainingFrozen > 0) { return(false); } if (checkCooldown && RemainingActionCooldownTicks > 0) { return(false); } var angleTo = GetAngleTo(target); if (Math.Abs(angleTo) > MyStrategy.Game.OrcWoodcutterAttackSector / 2) { return(false); } return(GetDistanceTo2(target) <= Geom.Sqr(MyStrategy.Game.OrcWoodcutterAttackRange + target.Radius)); }
private ATree _getNearestTree(Point a, Point b) { var dir = b - a; for (var i = 0; i <= SegmentDivideParts; i++) { Point p = dir * (1.0 * i / SegmentDivideParts) + a; var trees = TreesObserver.GetNearestTrees(p); foreach (var tree in trees) { if (Geom.SegmentCircleIntersects(a, b, tree, tree.Radius + Const.WizardRadius + MagicConst.RadiusAdditionalEpsilon)) { return(tree); } } } return(null); }
private void _simplify(ACircularUnit[] obstacles, double maxLength) { double length = 0; for (var i = 2; i < Count; i++) { var a = this[i - 2]; var b = this[i - 1]; var c = this[i]; if (_getNearestTree(a, b) != null) { break; } var bcTree = _getNearestTree(b, c); var acTree = _getNearestTree(a, c); if (bcTree != null || acTree != null) { if (acTree == bcTree) { RemoveAt(i - 1); } break; } if (length + a.GetDistanceTo(b) > maxLength) { break; } if (obstacles.All(ob => !Geom.SegmentCircleIntersects(a, c, ob, Const.WizardRadius + ob.Radius + MagicConst.RadiusAdditionalEpsilon))) { RemoveAt(i - 1); i--; } else { length += a.GetDistanceTo(b); } } }
public static double GetSegmentWeight(Point a, Point b, bool checkStart) { var dir = b - a; var res = dir.Length; ATree prev = null; for (var i = (checkStart ? 0 : 1); i <= SegmentDivideParts; i++) { var p = dir * (1.0 * i / SegmentDivideParts) + a; var tree = TreesObserver.GetNearestTree(p); if (tree != null && tree != prev && Geom.SegmentCircleIntersects(a, b, tree, tree.Radius + Const.WizardRadius + MagicConst.RadiusAdditionalEpsilon)) { res += Math.Ceiling(tree.Life / 12) * MagicConst.TreeObstacleWeight; prev = tree; } } return(res); }
public double GetDistanceTo(Point point) { if (A.Equals(B)) { return(A.GetDistanceTo(point)); } var a = A - point; var b = B - point; double aa, bb, cc; Geom.GetABC(a, b, out aa, out bb, out cc); if (Geom.Sign((a.Y + bb) * aa - (a.X + aa) * bb) * Geom.Sign((b.Y + bb) * aa - (b.X + aa) * bb) <= 0) { return(Math.Abs(cc / Math.Sqrt(aa * aa + bb * bb))); } return(Math.Min(a.Length, b.Length)); }
public static double DecodeFbCastDist(long ownerId) { var ownerPrev = MyStrategy.MyWizardsPrevState.FirstOrDefault(x => x.Id == ownerId); var owner = MyStrategy.MyWizards.FirstOrDefault(x => x.Id == ownerId); if (ownerPrev == null || owner == null) { return(0.0); // an impossible state } try { var angle = Geom.GetAngleBetween(owner.Angle, ownerPrev.Angle); var val = (long)((angle + Const.Eps) * 1e8) % 1000; return(val); } catch (Exception) { // на случай long overflow return(0.0); } }
public static Point _getHalfEllipseDxDy(double a, double b, double c, double angle) { // http://russianaicup.ru/forum/index.php?topic=708.msg6889#msg6889 if (Math.Abs(angle) > Math.PI / 2) { return(new Point(Math.Sin(angle) * c, Math.Cos(angle) * c)); } if (Math.Abs(angle) < Const.Eps) { return(new Point(0, b)); } var tan = Math.Tan(angle); double dx = Math.Sqrt(1.0 / (Geom.Sqr(1.0 / a) + Geom.Sqr(1.0 / tan / b))); if (angle < 0) { dx *= -1; } return(new Point(dx, Math.Abs(dx / tan))); }
public bool Move(Func <AProjectile, bool> check = null) { var prev = new Point(this); var nearestTree = TreesObserver.GetNearestTree(this); for (var i = 0; i < MicroTicks; i++) { if (!Exists) { return(false); } MicroMove(); if (nearestTree != null && GetDistanceTo2(nearestTree) <= Math.Sqrt(Radius + nearestTree.Radius)) { // снаряд ударился об дерево RemainingDistance = 0; return(false); } if (!Exists) { return(false); } if (check != null && !check(this)) { return(false); } } if (nearestTree != null && Geom.SegmentCircleIntersects(prev, this, nearestTree, nearestTree.Radius + Radius)) { // снаряд ударился об дерево (это более точная проверка) RemainingDistance = 0; return(false); } return(true); }
public static void Update() { var newState = new Dictionary <long, ABuilding>(); NewBuildings.Clear(); DisappearedBuildings.Clear(); foreach (var bld in MyStrategy.World.Buildings) { var a = new ABuilding(bld); var key = _getCoordinatesKey(a); if (!_prevState.ContainsKey(key)) { // новое здание NewBuildings.Add(a); } newState[key] = a; if (MyStrategy.World.TickIndex == 0) { var opposit = new ABuilding(bld); opposit.X = Const.MapSize - opposit.X; opposit.Y = Const.MapSize - opposit.Y; opposit.Faction = opposit.Faction == Faction.Academy ? Faction.Renegades : Faction.Academy; opposit.Id *= -1; // temp Id opposit.IsTeammate = false; var oppositKey = _getCoordinatesKey(opposit); if (!_prevState.ContainsKey(oppositKey)) { // новое здание NewBuildings.Add(opposit); } newState[oppositKey] = opposit; } } foreach (var it in _prevState) { var bld = it.Value; var key = _getCoordinatesKey(bld); if (!MyStrategy.IsPointVisible(bld)) { // его не видно, считаем что осталось if (bld.RemainingActionCooldownTicks == 0) { // значит, если кто-то был поблизости, башня 100% выстрелила if (MyStrategy.Combats.Any(x => x.IsTeammate && bld.GetDistanceTo2(x) <= Geom.Sqr(bld.CastRange))) { bld.RemainingActionCooldownTicks = (bld.IsBase ? MyStrategy.Game.FactionBaseCooldownTicks : MyStrategy.Game.GuardianTowerCooldownTicks) - 1; } } else { bld.RemainingActionCooldownTicks--; } newState[key] = bld; } else if (!newState.ContainsKey(key)) { // видно, но исчезло DisappearedBuildings.Add(bld); } } _prevState = newState; OpponentBase = Buildings.FirstOrDefault(x => x.IsBase && x.IsOpponent); MyBase = Buildings.FirstOrDefault(x => x.IsBase && x.IsTeammate); foreach (var building in Buildings) { if (building.IsBase) { building._isAssailable = Buildings.Count(x => x.Faction == building.Faction && x.Order == 1) < 3; } else { building._isAssailable = Buildings.Count( x => x.Faction == building.Faction && x.Order + 1 == building.Order && x.Lane == building.Lane) == 0; } } }
private bool _TryGoByGradient(Func <AWizard, double> costFunction, Func <AWizard, bool> firstMoveCondition, FinalMove move) { var self = new AWizard(ASelf); var obstacles = Combats.Where(x => x.Id != Self.Id && !(x is ABuilding)).Cast <ACircularUnit>() .Concat(BuildingsObserver.Buildings) .Where(x => self.GetDistanceTo2(x) < Geom.Sqr(x.Radius + 150)) .ToArray(); var danger = costFunction(self); // for debug List <double> selVec = null; var minDanger = double.MaxValue; Point selMoveTo = null; foreach (var angle in Utility.Range(self.Angle, Math.PI * 2 + self.Angle, 24, false)) { var moveTo = self + Point.ByAngle(angle) * self.VisionRange; var nearest = Combats .Where(x => x.GetDistanceTo(self) < Math.Max(self.VisionRange, x.VisionRange) * 1.3) .Select(Utility.CloneCombat) .ToArray(); var tergetsSelector = new TargetsSelector(nearest); var opponents = nearest.Where(x => x.IsOpponent).ToArray(); var vec = new List <double>(); const int steps = 18; var my = (AWizard)nearest.FirstOrDefault(x => x.Id == self.Id); var ok = true; var canMove = true; while (vec.Count < steps) { if (canMove) { canMove = my.MoveTo(moveTo, null, w => w.GetFirstIntersection(obstacles) == null); if (TreesObserver.GetNearestTrees(my).Any(t => t.IntersectsWith(my))) { break; } } else { my.SkipTick(); } var tmp = OpponentCombats;//HACK OpponentCombats = opponents; vec.Add(costFunction(my)); OpponentCombats = tmp; foreach (var x in opponents) { var tar = tergetsSelector.Select(x); if (tar != null || x is AWizard) { x.EthalonMove(tar); } } if (vec.Count == 1 && firstMoveCondition != null && !firstMoveCondition(my)) { ok = false; break; } } if (!ok || vec.Count == 0) { continue; } while (vec.Count < steps) { vec.Add(CantMoveDanger); } var newDanger = 0.0; for (var k = 0; k < steps; k++) { newDanger += vec[k] * Math.Pow(0.87, k); } newDanger += 3 * vec[0]; if (newDanger < minDanger) { minDanger = newDanger; selMoveTo = Utility.PointsEqual(my, self) ? null : moveTo; selVec = vec; } } if (selVec != null) { move.Speed = move.StrafeSpeed = 0; move.MoveTo(selMoveTo, null); return(true); } return(false); }
public override ACombatUnit SelectTarget(ACombatUnit[] candidates) { var accessible = candidates .Where(x => x.Faction != Faction.Neutral && x.Faction != Faction && x.IsAlive && GetDistanceTo2(x) <= Geom.Sqr(CastRange)) .ToArray(); ACombatUnit sel = null; foreach (var x in accessible) { if (x.Life > Damage) { if (sel == null || x.Life < sel.Life || Utility.Equals(x.Life, sel.Life) && x.Id == MyStrategy.Self.Id) { sel = x; } } } if (sel != null) { return(sel); } foreach (var x in accessible) { if (x.Life <= Damage) { if (sel == null || x.Life > sel.Life || Utility.Equals(x.Life, sel.Life) && x.Id == MyStrategy.Self.Id) { sel = x; } } } return(sel); }
public override bool EthalonCanHit(ACircularUnit target, bool checkCooldown = true) { return((!checkCooldown || RemainingActionCooldownTicks == 0) && GetDistanceTo2(target) <= Geom.Sqr(CastRange)); }
MovingInfo _findCastTarget2(AWizard self, Point moveTo, ProjectileType projectileType) { var move = new FinalMove(new Move()); if (projectileType == ProjectileType.MagicMissile) { AUnit mmSelTarget = null; Point mmSelFirstMoveTo = null; var mmMinTicks = int.MaxValue; double mmMinPriority = int.MaxValue; foreach (var opp in OpponentCombats) { if (self.GetDistanceTo2(opp) > Geom.Sqr(self.CastRange + opp.Radius + 40) || !opp.IsAssailable) { continue; } var nearest = Combats .Where(x => self.GetDistanceTo2(x) < Geom.Sqr(Math.Max(x.VisionRange, self.VisionRange) * 1.3)) .Select(Utility.CloneCombat) .ToArray(); var targetsSelector = new TargetsSelector(nearest) { EnableMinionsCache = true }; var nearstOpponents = nearest .Where(x => x.IsOpponent) .ToArray(); var canHitNow = opp.EthalonCanHit(self, checkCooldown: !(opp is AWizard)); var ticks = 0; var my = nearest.FirstOrDefault(x => x.Id == self.Id) as AWizard; var his = nearest.FirstOrDefault(x => x.Id == opp.Id); if (my == null || his == null) { continue; } Point firstMoveTo = null; var buildingsHit = false; while (!my.EthalonCanCastMagicMissile(his, checkCooldown: false)) { if (ticks > 40) { break; } var m = moveTo; var stopIfCannotMove = true; if (m == null && my.EthalonCanCastMagicMissile(his, checkCooldown: false, checkAngle: false)) { stopIfCannotMove = false; m = my + (my - his); var tmp = new AWizard(my); tmp.MoveTo(m, his, w => !CheckIntersectionsAndTress(w, nearest)); if (EstimateDanger(my, false) <= EstimateDanger(tmp, false)) { m = null; } } if (m == null) { m = his; } if (ticks == 0) { firstMoveTo = m; } if (!my.MoveTo(m, his, w => !CheckIntersectionsAndTress(w, nearest)) && Utility.PointsEqual(m, his) && stopIfCannotMove) { break; } foreach (var x in nearest) { if (x.Id == my.Id) { continue; } var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x.IsOpponent && x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); if (x.IsOpponent) { x.EthalonMove(tar ?? my); } else if (tar != null) { x.EthalonMove(tar); } else { x.SkipTick(); } } ticks++; } if (his is AWizard && (his as AWizard).IsBesieded) { ticks -= 15; // чтобы дать больше приоритета визарду } var priority = GetCombatPriority(self, his); if (ticks < mmMinTicks || ticks == mmMinTicks && priority < mmMinPriority) { if (my.EthalonCanCastMagicMissile(his)) { if (nearstOpponents.All(x => { if (canHitNow && x.Id == opp.Id) // он и так доставал { return(true); } if (!x.EthalonCanHit(my) && (!(x is ABuilding) || !buildingsHit)) { return(true); } if (his.Id == x.Id && CanRush(my, x)) { return(true); } var target = targetsSelector.Select(x); if (target != null && target.Id != my.Id) { return(true); } return(false); }) ) { mmMinTicks = ticks; mmMinPriority = priority; mmSelTarget = opp; mmSelFirstMoveTo = firstMoveTo; } } } } if (mmSelTarget != null) { mmMinTicks = Math.Max(0, mmMinTicks); move.MoveTo(moveTo ?? mmSelFirstMoveTo, mmSelTarget); return(new MovingInfo(mmSelTarget, mmMinTicks, move) { TargetId = mmSelTarget.Id }); } } const int walkLimit = 9; if (projectileType == ProjectileType.Fireball && self.FireballSkillLevel == 5 && Math.Max(self.RemainingActionCooldownTicks, self.RemainingFireballCooldownTicks) <= walkLimit) { var fbMaxDamage = 0.0; Point fbSelTarget = null; var fbMinTicks = int.MaxValue; foreach (var ang in Utility.Range(-Game.StaffSector, Game.StaffSector, 10)) { var nearest = Combats .Where(x => self.GetDistanceTo2(x) < Geom.Sqr(Math.Max(x.VisionRange, self.VisionRange) * 1.3)) .Select(Utility.CloneCombat) .ToArray(); var targetsSelector = new TargetsSelector(nearest) { EnableMinionsCache = true }; var ticks = 0; var my = nearest.FirstOrDefault(x => x.Id == self.Id) as AWizard; var dir = my + Point.ByAngle(my.Angle + ang) * 1000; while (ticks <= walkLimit) { if (my.CanUseFireball()) { var proj = new AProjectile(my, 0, ProjectileType.Fireball); var path = proj.Emulate(nearest, 0.0); var damage = path.Where(x => _isFireballGoodSeg(my, x)) .Select(x => x.OpponentDamage) .DefaultIfEmpty(0) .Max(); if (damage > fbMaxDamage) { fbMaxDamage = damage; fbSelTarget = dir; fbMinTicks = ticks; } } foreach (var x in nearest) { if (x.Id == my.Id) { continue; } if (x is AMinion) { x.EthalonMove(targetsSelector.Select(x)); } else { x.SkipTick(); } } if (!my.MoveTo(dir, dir, w => !CheckIntersectionsAndTress(w, nearest))) { break; } if (nearest.Any(x => x.IsOpponent && x is ABuilding && x.EthalonCanHit(my) && targetsSelector.Select(x) == my)) { break; } ticks++; } } if (fbSelTarget != null) { move.MoveTo(fbSelTarget, fbSelTarget); return(new MovingInfo(fbSelTarget, fbMinTicks, move) { Damage = fbMaxDamage }); } } return(new MovingInfo(null, int.MaxValue, move)); }
MovingInfo _findCastTarget(AWizard self, ProjectileType projectileType) { var actionType = Utility.GetActionByProjectileType(projectileType); var move = new FinalMove(new Move()); if (self.RemainingActionCooldownTicks > 0 || self.RemainingCooldownTicksByAction[(int)actionType] > 0 || self.Mana < Const.ProjectileInfo[(int)projectileType].ManaCost || !self.IsActionAvailable(actionType) ) { return(new MovingInfo(null, int.MaxValue, move)); } var angles = new List <double>(); foreach (var x in OpponentCombats) { var distTo = self.GetDistanceTo(x); if (distTo > self.CastRange + x.Radius + Const.ProjectileInfo[(int)projectileType].DamageRadius + 3) { continue; } var angleTo = self.GetAngleTo(x); if (Math.Abs(angleTo) > Math.PI / 3) { continue; } var deltaAngle = Math.Atan2(x.Radius, distTo); angles.AddRange(new[] { angleTo, angleTo + deltaAngle, angleTo - deltaAngle }.Where(a => Math.Abs(a) <= Game.StaffSector / 2)); } if (angles.Count > 0) { angles.AddRange(Utility.Range(-Game.StaffSector / 2, Game.StaffSector / 2, 16)); } ACombatUnit selTarget = null; double selMinDist = 0, selMaxDist = self.CastRange + 20, selCastAngle = 0, selMaxDamage = 0; if (projectileType == ProjectileType.Fireball) { var maxDamage = 0.0; var maxBurned = 0; foreach (var angle in angles) { var proj = new AProjectile(new AWizard(self), angle, projectileType); var path = EmulateProjectileWithNearest(proj); for (var i = 0; i < path.Count; i++) { var seg = path[i]; if (_isFireballGoodSeg(self, seg)) { if (seg.OpponentBurned > maxBurned || seg.OpponentBurned == maxBurned && seg.OpponentDamage > maxDamage //|| seg.OpponentBurned == maxBurned && Utility.Equals(seg.OpponentDamage, maxDamage) //TODO: combare by angle and priority ) { maxBurned = seg.OpponentBurned; maxDamage = seg.OpponentDamage; selCastAngle = angle; selMinDist = selMaxDist = seg.StartDistance; selTarget = seg.Target; selMaxDamage = seg.OpponentDamage; } } } } } else { double selPriority = int.MaxValue, selAngleTo = 0; foreach (var angle in angles) { var proj = new AProjectile(new AWizard(self), angle, projectileType); var path = EmulateProjectileWithNearest(proj); for (var i = 0; i < path.Count; i++) { if (path[i].State == AProjectile.ProjectilePathState.Free) { continue; } // TODO: если можно убить нескольких, убивать того, у кого больше жизней var combat = path[i].Target; if (!combat.IsAssailable) { continue; } var myAngle = self.Angle + angle; var hisAngle = self.Angle + self.GetAngleTo(combat); var angleTo = Geom.GetAngleBetween(myAngle, hisAngle); var priority = GetCombatPriority(self, combat); if (combat.IsOpponent && (priority < selPriority || Utility.Equals(priority, selPriority) && angleTo < selAngleTo) && self.CheckProjectileCantDodge(proj, Combats.FirstOrDefault(x => x.Id == combat.Id)) ) { selTarget = combat; selCastAngle = angle; selAngleTo = angleTo; selMinDist = i == 0 || path[i - 1].State == AProjectile.ProjectilePathState.Free && path[i - 1].Length < 40 ? path[i].StartDistance - 1 : path[i].StartDistance - 20; selMaxDist = i >= path.Count - 2 ? (self.CastRange + 500) : (path[i + 1].EndDistance + path[i].EndDistance) / 2; selPriority = priority; selMaxDamage = path[i].OpponentDamage; } } } } if (selTarget == null) { return(new MovingInfo(null, int.MaxValue, move)); } move.Action = actionType; move.MinCastDistance = selMinDist; move.MaxCastDistance = selMaxDist; move.CastAngle = selCastAngle; #if DEBUG _lastProjectileTick = World.TickIndex; _lastProjectilePoints = new[] { self + Point.ByAngle(self.Angle + selCastAngle) * selMinDist, self + Point.ByAngle(self.Angle + selCastAngle) * Math.Min(Self.CastRange, selMaxDist), }; #endif return(new MovingInfo(selTarget, 0, move) { Damage = selMaxDamage, TargetId = selTarget.Id }); }
MovingInfo _findStaffTarget(AWizard self) { var potentialColliders = Combats .Where(x => x.Id != self.Id && self.GetDistanceTo2(x) < Geom.Sqr(Game.StaffRange * 6)) .ToArray(); int minTicks = int.MaxValue; var move = new FinalMove(new Move()); var attacked = self.GetStaffAttacked(potentialColliders).Cast <ACombatUnit>().ToArray(); ACircularUnit selTarget = attacked.FirstOrDefault(x => x.IsOpponent); if (selTarget != null) // если уже можно бить { move.Action = ActionType.Staff; return(new MovingInfo(selTarget, 0, move)); } if (self.MmSkillLevel == 5) { // т.к. стрелять можно без задержки // возможно, нужно сделать исключение, если прокачан посох return(new MovingInfo(null, int.MaxValue, move)); } Point selMoveTo = null; foreach (var opp in OpponentCombats) { var dist = self.GetDistanceTo(opp); if (dist > Game.StaffRange * 5 || !opp.IsAssailable) { continue; } var range = opp.Radius + Game.StaffRange; foreach (var delta in new[] { -range, -range / 2, 0, range / 2, range }) { var angle = Math.Atan2(delta, dist); var moveTo = self + (opp - self).Normalized().RotateClockwise(angle) * self.VisionRange; var nearstCombats = Combats .Where(x => x.GetDistanceTo(self) <= Math.Max(x.VisionRange, self.VisionRange) * 1.2) .Select(Utility.CloneCombat) .ToArray(); var targetsSelector = new TargetsSelector(nearstCombats) { EnableMinionsCache = true }; var nearstOpponents = nearstCombats.Where(x => x.IsOpponent).ToArray(); var my = nearstCombats.FirstOrDefault(x => x.Id == self.Id) as AWizard; var his = nearstCombats.FirstOrDefault(x => x.Id == opp.Id); var allowRush = opp is AFetish || opp is AWizard; var canHitNow = opp.EthalonCanHit(self, checkCooldown: !allowRush); var ticks = 0; var ok = true; var buildingsHit = false; while (ticks < (allowRush ? 65 : 35) && my.GetDistanceTo2(his) > Geom.Sqr(Game.StaffRange + his.Radius)) { foreach (var x in nearstOpponents) // свои как-бы стоят на месте { var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); x.EthalonMove(tar ?? my); } if (!my.MoveTo(moveTo, his, w => !CheckIntersectionsAndTress(w, potentialColliders))) { ok = false; break; } ticks++; } if (ok && !(opp is AOrc)) { while (Math.Abs(my.GetAngleTo(his)) > Game.StaffSector / 2) { my.MoveTo(null, his); foreach (var x in nearstOpponents) { var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); x.EthalonMove(tar ?? my); } ticks++; } } Func <ACombatUnit, bool> check = x => { if ((opp is AWizard) && (opp as AWizard).IsBesieded && !(x is ABuilding)) { return(true); } if (canHitNow && x.Id == opp.Id) // он и так доставал { return(true); } if (!x.EthalonCanHit(my) && (!(x is ABuilding) || !buildingsHit)) { return(true); } if (his.Id == x.Id && my.StaffDamage >= his.Life) { return(true); } var target = targetsSelector.Select(x); if (target != null && target.Id != my.Id) { return(true); } return(false); }; if (opp is AWizard) { ticks -= 5; if ((opp as AWizard).IsBesieded) { ticks -= 10; } } if (ok && ticks < minTicks) { if (my.CanStaffAttack(his)) { if (nearstOpponents.All(check)) { // успею-ли я вернуться обратно while (my.GetDistanceTo(self) > my.MaxForwardSpeed)//TODO:HACK { my.MoveTo(self, null); foreach (var x in nearstOpponents) { var tar = targetsSelector.Select(x); buildingsHit = buildingsHit || (x is ABuilding && tar != null && tar.Id == my.Id && x.EthalonCanHit(my)); if (tar != null) { x.EthalonMove(tar); } else { x.SkipTick(); } } } if (nearstOpponents.All(check)) { selTarget = opp; selMoveTo = moveTo; minTicks = ticks; } } } } } } if (selTarget != null) { bool angleOk = Math.Abs(self.GetAngleTo(selTarget)) <= Game.StaffSector / 2, distOk = self.GetDistanceTo2(selTarget) <= Geom.Sqr(Game.StaffRange + selTarget.Radius); if (!distOk) { move.MoveTo(selMoveTo, selTarget); } else if (!angleOk) { move.MoveTo(null, selTarget); } } return(new MovingInfo(selTarget, Math.Max(0, minTicks), move)); }
MovingInfo FindBonusTarget(AWizard self) { var minTime = int.MaxValue; var selGo = 0; Point selMoveTo = null; foreach (var _bonus in BonusesObserver.Bonuses) { if (_bonus.GetDistanceTo(self) - self.Radius - _bonus.Radius > Game.StaffRange * 3) { continue; } if (_bonus.RemainingAppearanceTicks > 60) { continue; } var nearest = Combats .Where(x => x.Id != self.Id && self.GetDistanceTo2(x) < Geom.Sqr(self.VisionRange)) .ToArray(); foreach (var angle in Utility.Range(self.Angle, Math.PI * 2 + self.Angle, 24, false)) { var bonus = new ABonus(_bonus); var my = new AWizard(self); var moveTo = my + Point.ByAngle(angle) * self.VisionRange; int time = 0; int go = 0; while (my.GetDistanceTo(bonus) > my.Radius + bonus.Radius && time < 60) { if (!my.MoveTo(moveTo, null, w => !CheckIntersectionsAndTress(w, nearest))) { break; } var wait = !bonus.Exists; bonus.SkipTick(); time++; if (my.GetDistanceTo(bonus) <= my.Radius + bonus.Radius) { while (!bonus.Exists) { bonus.SkipTick(); time++; } if (wait) { time++; } if (time < minTime) { minTime = time; selMoveTo = moveTo; selGo = go; } break; } go++; } } } var moving = new MovingInfo(selMoveTo, minTime, new FinalMove(new Move())); if (selMoveTo != null) { if (minTime == 1 || selGo > 0) { moving.Move.MoveTo(selMoveTo, null); } else { moving.Target = self; } } return(moving); }
WizardPath _goAround(ACombatUnit target, bool goAgainst) { var my = new AWizard(ASelf); var selLane = Utility.IsBase(target) ? MessagesObserver.GetLane() : RoadsHelper.GetLane(target); var nearestBuilding = OpponentBuildings.ArgMin(b => b.GetDistanceTo2(my)); var buildings = new List <ABuilding>(); if (nearestBuilding.GetDistanceTo(my) > nearestBuilding.VisionRange) { buildings.Add(nearestBuilding); } if (target.IsOpponent && target.Id != nearestBuilding.Id && target is ABuilding) { buildings.Add((ABuilding)target); } var threshold = Self.CastRange - 200; if (ASelf.GetDistanceTo(target) < Self.CastRange || !goAgainst) { threshold = 0; } var path = DijkstraFindPath(ASelf, pos => { // точка ОК, если с неё можно стрелять var dist2 = pos.GetDistanceTo2(target); if (dist2 < Geom.Sqr(Self.CastRange) && dist2 > Geom.Sqr(threshold)) { var distToLine = RoadsHelper.Roads.Where(seg => seg.LaneType == selLane).Min(seg => seg.GetDistanceTo(pos)); if (distToLine < 200 && (!goAgainst || BuildingsObserver.MyBase.GetDistanceTo2(pos) < BuildingsObserver.MyBase.GetDistanceTo2(target)) && TreesObserver.Trees .Where(x => x.GetDistanceTo2(pos) < Geom.Sqr(Self.CastRange)) .All(x => !Geom.SegmentCircleIntersects(pos, target, x, x.Radius + Game.MagicMissileRadius)) ) { return(DijkstraStopStatus.TakeAndStop); } } return(DijkstraStopStatus.Continue); }, MoveCostFunc(buildings, selLane)).FirstOrDefault(); if (path == null && my.GetDistanceTo(target) - my.Radius - target.Radius <= 1) { path = new WizardPath { my } } ; // из-за эпсилон, если стою близко у цели, то он как бы с ней пересекается, но это не так if (path == null || path.Count == 0) { return(null); } if (path.Count == 1) { FinalMove.Turn = my.GetAngleTo(target); return(null); } var obstacles = Combats.Where(x => x.Id != Self.Id).Cast <ACircularUnit>() .Where(x => my.GetDistanceTo2(x) < Geom.Sqr(my.VisionRange)) //??? .ToArray(); path.Simplify(obstacles, MagicConst.SimplifyMaxLength); var nextPoint = path[1]; var nextNextPoint = path.Count > 2 ? path[2] : target; FinalMove.MoveTo(nextPoint, my.GetDistanceTo(nextNextPoint) < Self.VisionRange * 1.2 ? nextNextPoint : nextPoint); var nextTree = path.GetNearestTree(); CutTreesInPath(nextTree, FinalMove); #if DEBUG Visualizer.Visualizer.SegmentsDrawQueue.Add(new object[] { path, Pens.Blue, 3 }); #endif return(path); } void CutTreesInPath(ATree nextTree, FinalMove move) { if (nextTree == null) { return; } var my = new AWizard(ASelf); var angleTo = my.GetAngleTo(nextTree); if (my.GetDistanceTo(nextTree) < my.VisionRange && Math.Abs(angleTo) > Game.StaffSector / 2) { move.MoveTo(null, nextTree); } if (my.RemainingActionCooldownTicks == 0 && Math.Abs(angleTo) <= Game.StaffSector / 2) { if (my.GetDistanceTo(nextTree) <= Game.StaffRange + nextTree.Radius && my.RemainingStaffCooldownTicks == 0) { move.Action = ActionType.Staff; } else if (my.GetDistanceTo(nextTree) <= my.CastRange + nextTree.Radius && my.RemainingMagicMissileCooldownTicks == 0) { move.Action = ActionType.MagicMissile; move.CastAngle = angleTo; move.MinCastDistance = Math.Min(my.CastRange - 1, my.GetDistanceTo(nextTree)); } } } MovingInfo GoToBonus() { TimerStart(); var ret = _goToBonus(); TimerEndLog("GoToBonus", 1); return(ret); } bool _skipBonusCond(ABonus bonus) { var oppFirst = BuildingsObserver.Buildings.FirstOrDefault(x => x.IsOpponent && x.Lane == MessagesObserver.GetLane() && x.Order == 0); if (oppFirst == null || ASelf.GetDistanceTo(oppFirst) <= oppFirst.CastRange) { return(true); } var myFirst = BuildingsObserver.Buildings.FirstOrDefault(x => x.IsTeammate && x.Lane == MessagesObserver.GetLane() && x.Order == 0); if (myFirst == null || OpponentWizards.Any(x => x.GetDistanceTo(myFirst) <= myFirst.Radius)) { return(true); } // TODO return(false); } ABonus SelectBonus(AWizard self) { var bonus = BonusesObserver.Bonuses.ArgMin(b => b.GetDistanceTo2(self)); if (bonus.RemainingAppearanceTicks > MagicConst.GoToBonusMaxTicks + MagicConst.BonusTimeReserve) { return(null); } if (self.GetDistanceTo(BuildingsObserver.OpponentBase) < BuildingsObserver.OpponentBase.CastRange * 1.4) { return(null); } if (Game.IsSkillsEnabled && _skipBonusCond(bonus)) { return(null); } return(bonus); } MovingInfo _goToBonus() { var bonus = SelectBonus(ASelf); var selMovingInfo = new MovingInfo(null, int.MaxValue, new FinalMove(new Move())); if (bonus == null) { return(selMovingInfo); } if (Const.IsFinal) { var teammates = MyWizards .Where(x => x.Id != ASelf.Id) .Where(x => { var b = SelectBonus(x); return(b != null && b.Id == bonus.Id); }) .ToArray(); if (teammates.Any(x => ASelf.GetDistanceTo(bonus) > x.GetDistanceTo(bonus))) { return(selMovingInfo); } } var my = new AWizard(ASelf); var nearestBuilding = OpponentBuildings.ArgMin(b => b.GetDistanceTo2(my)); var path = DijkstraFindPath(ASelf, pos => { // точка ОК, если бонус совсем близко if (pos.GetDistanceTo2(bonus) < Geom.Sqr(bonus.Radius + Self.Radius + 35)) { return(DijkstraStopStatus.TakeAndStop); } return(DijkstraStopStatus.Continue); }, MoveCostFunc(new [] { nearestBuilding }, MessagesObserver.GetLane())).FirstOrDefault(); if (path == null) { GoDirect(bonus, selMovingInfo.Move); selMovingInfo.Target = bonus; return(selMovingInfo); } var obstacles = Combats.Where(x => x.Id != Self.Id).Cast <ACircularUnit>() .Where(x => my.GetDistanceTo2(x) < Geom.Sqr(my.VisionRange)) .ToArray(); path.Add(bonus); path.Simplify(obstacles, MagicConst.SimplifyMaxLength); var time = (int)(path.GetLength() / my.MaxForwardSpeed); if (time < MagicConst.GoToBonusMaxTicks) { selMovingInfo.Time = time; var nextPoint = path[1]; var nextNextPoint = path.Count > 2 ? path[2] : nextPoint; selMovingInfo.Move = new FinalMove(new Move()); selMovingInfo.Move.MoveTo(nextPoint, my.GetDistanceTo(nextNextPoint) < my.Radius + 20 ? nextNextPoint : nextPoint); selMovingInfo.Target = nextPoint; var nextTree = path.GetNearestTree(); CutTreesInPath(nextTree, selMovingInfo.Move); } if (selMovingInfo.Time <= bonus.RemainingAppearanceTicks - MagicConst.BonusTimeReserve) { selMovingInfo.Target = null; } #if DEBUG if (selMovingInfo.Target != null) { Visualizer.Visualizer.SegmentsDrawQueue.Add(new object[] { path, Pens.Red, 3 }); } #endif return(selMovingInfo); }
private void _move(Wizard self, World world, Game game, Move move) { World = world; Game = game; Self = self; FinalMove = new FinalMove(move); Const.Initialize(); MagicConst.TreeObstacleWeight = Const.IsFinal ? 25 : 35; Wizards = world.Wizards .Select(x => new AWizard(x)) .ToArray(); foreach (var wizard in Wizards) { foreach (var other in Wizards) { if (wizard.Faction != other.Faction) { continue; } if (wizard.GetDistanceTo2(other) > Geom.Sqr(Game.AuraSkillRange)) { continue; } for (var i = 0; i < 5; i++) { wizard.AurasFactorsArr[i] = Math.Max(wizard.AurasFactorsArr[i], other.SkillsLearnedArr[i] / 2); } } var orig = World.Wizards.FirstOrDefault(w => w.Id == wizard.Id); var player = World.Players.FirstOrDefault(p => orig != null && p.Id == orig.OwnerPlayerId); if (player != null && player.IsStrategyCrashed) { wizard.RemainingFrozen = 100500; } } OpponentWizards = Wizards .Where(x => x.IsOpponent) .ToArray(); MyWizards = Wizards .Where(x => x.IsTeammate) .ToArray(); Minions = world.Minions .Select(x => x.Type == MinionType.OrcWoodcutter ? (AMinion) new AOrc(x) : new AFetish(x)) .ToArray(); NeutralMinions = Minions .Where(x => x.Faction == Faction.Neutral) .ToArray(); Combats = Minions.Cast <ACombatUnit>() .Concat(Wizards) .Concat(BuildingsObserver.Buildings)//TODO перед BuildingsObserver.Update???? .ToArray(); MyCombats = Combats .Where(x => x.IsTeammate) .ToArray(); NeutralMinionsObserver.Update(); OpponentMinions = Minions .Where(x => x.IsOpponent) .ToArray(); OpponentCombats = Combats .Where(x => x.IsOpponent) .ToArray(); RoadsHelper.Initialize(); BuildingsObserver.Update(); OpponentBuildings = BuildingsObserver.Buildings .Where(x => x.IsOpponent) .ToArray(); TreesObserver.Update(); ProjectilesObserver.Update(); BonusesObserver.Update(); MessagesObserver.Update(); InitializeProjectiles(); InitializeDijkstra(); ASelf = Wizards.FirstOrDefault(x => x.Id == Self.Id); if (ASelf == null) { throw new Exception("Self not found in wizards list"); } InitializeDangerEstimation(); SupportObserver.Update(); if (Self.IsMaster && World.TickIndex == 0) { MasterSendMessages(); } if (Self.IsMaster) { MasterCheckRearrange(); } var nearestBonus = BonusesObserver.Bonuses.ArgMin(b => b.GetDistanceTo(ASelf)); var opponentsAroundBonus = OpponentWizards.Where(w => nearestBonus.GetDistanceTo(w) < ASelf.VisionRange * 1.5).ToArray(); var teammatesAroundBonus = MyWizards.Where(w => ASelf.GetDistanceTo(w) < ASelf.VisionRange * 1.5).ToArray(); WizardPath path = null; AUnit pathTarget = null; var goAway = GoAwayDetect(); var bonusMoving = goAway ? new MovingInfo(null, int.MaxValue, null) : GoToBonus(); var target = FindTarget(new AWizard(ASelf), bonusMoving.Target); if (target == null && bonusMoving.Target == null && !goAway) { var nearest = OpponentCombats .Where( x => Utility.IsBase(x) || RoadsHelper.GetLane(x) == MessagesObserver.GetLane() || RoadsHelper.GetLaneEx(ASelf) == ALaneType.Middle && RoadsHelper.GetLaneEx(x) == ALaneType.Middle && CanRush(ASelf, x)) .Where(x => x.IsAssailable && x.Faction != Faction.Neutral) .OrderBy( x => x.GetDistanceTo(self) + (x is AWizard ? -40 : (x is ABuilding && !((ABuilding)x).IsBesieded) ? 1500 : 0)) .ToArray(); foreach (var n in nearest) { path = GoAgainst(n); if (path != null) { pathTarget = n; break; } } if (nearest.Length > 0 && path == null) { GoDirect(nearest[0], FinalMove); } } TimerStart(); if (!TryDodgeProjectile()) { if (target == null) { TryPreDodgeProjectile(); } if (goAway) { GoAway(); } else if (target == null || target.Type == TargetType.Teammate || (FinalMove.Action == ActionType.Staff || FinalMove.Action == ActionType.MagicMissile || FinalMove.Action == ActionType.Fireball || FinalMove.Action == ActionType.FrostBolt) && target.Type == TargetType.Opponent) { if (bonusMoving.Target != null) { NextBonusWaypoint = bonusMoving.Target; FinalMove.Turn = bonusMoving.Move.Turn; if (bonusMoving.Move.Action != ActionType.None && bonusMoving.Move.Action != null) { FinalMove.Action = bonusMoving.Move.Action; FinalMove.MinCastDistance = bonusMoving.Move.MinCastDistance; FinalMove.MaxCastDistance = bonusMoving.Move.MaxCastDistance; FinalMove.CastAngle = bonusMoving.Move.CastAngle; } NextBonusWaypoint = ASelf + (NextBonusWaypoint - ASelf).Normalized() * (Self.Radius + 30); if (nearestBonus.GetDistanceTo(ASelf) < ASelf.VisionRange * 1.5 && nearestBonus.GetDistanceTo(ASelf) > 100 && opponentsAroundBonus.Length <= 1 && ASelf.Life + 10 >= (opponentsAroundBonus.FirstOrDefault() ?? ASelf).Life && OpponentMinions.Count(x => x.GetDistanceTo(ASelf) < Game.FetishBlowdartAttackRange) == 0 ) { FinalMove.MoveTo(NextBonusWaypoint, null); } else { TryGoByGradient(x => EstimateDanger(x), null, FinalMove); } } else { var all = Combats.Select(Utility.CloneCombat).ToArray(); var my = all.FirstOrDefault(x => x.Id == ASelf.Id) as AWizard; my?.Move(FinalMove.Speed, FinalMove.StrafeSpeed); var ts = new TargetsSelector(all); var skipBuildings = path == null || path.GetLength() < 300 || path.GetLength() < 600 && OpponentBuildings.Any(x => { var tar = ts.Select(x); return(tar != null && tar.Id == ASelf.Id); }); if (TryGoByGradient(x => EstimateDanger(x), x => HasAnyTarget(x, skipBuildings), FinalMove)) { var cutTreeMovingInfo = FindTreeTarget(ASelf); if (cutTreeMovingInfo.Target != null) { FinalMove.Turn = cutTreeMovingInfo.Move.Turn; if (cutTreeMovingInfo.Move.Action != null && FinalMove.Action == null) { FinalMove.Action = cutTreeMovingInfo.Move.Action; } // не будет мешать TryPreDodgeProjectile? } } } } PostDodgeProjectile(); } TimerEndLog("Go", 1); if (ASelf.CanLearnSkill) { move.SkillToLearn = MessagesObserver.GetSkill(); } }
double EstimateDanger(AWizard my, bool considerWizardBesieded = true) { double res = 0; var nearestTower = OpponentCombats.FirstOrDefault(x => x is ABuilding && x.GetDistanceTo2(my) <= Geom.Sqr(x.CastRange)); foreach (var opp in OpponentCombats) { var dist = opp.GetDistanceTo(my); if (dist > 2 * my.VisionRange) { continue; } if (opp is AWizard) { var wizard = opp as AWizard; var inner = (Game.StaffRange + my.Radius) + 1; // (куда достаёт посохом) + запас var outer = (opp.CastRange + my.Radius + Game.MagicMissileRadius) + 1; // (куда достанет MagicMissile) + запас if (GoAwayCond(my, wizard)) { outer += GoAwaySafeDist - Game.MagicMissileRadius; } var coeff = 45; if (!wizard.IsBesieded || !considerWizardBesieded || (nearestTower != null && !Utility.IsBase(nearestTower) && BuildingsObserver.MyBase.GetDistanceTo2(my) > BuildingsObserver.MyBase.GetDistanceTo2(nearestTower) && BuildingsObserver.MyBase.GetDistanceTo2(wizard) > BuildingsObserver.MyBase.GetDistanceTo2(nearestTower) ) ) { if (dist < inner) { res += (wizard.StaffDamage + wizard.MagicMissileDamage) * 2; } else if (dist < outer) { res += (wizard.MagicMissileDamage + coeff - (dist - inner) / (outer - inner) * coeff) * 2; } } else { var otr = my.VisionRange * 1.3; if (dist < otr) { res -= 10 + 60 - dist / otr * 60; } } } else if (opp is ABuilding) { var building = opp as ABuilding; if (building.IsBase && !Game.IsSkillsEnabled) { var outer = building.CastRange; if (dist <= outer) { res += 90 - dist / outer * 90 + building.Damage; } } else if (building.IsBesieded) { var inner = my.Radius + building.Radius + 2 * Game.DartRadius + 3; // свои фетиши могут стрелять var outer = building.VisionRange * 1.2; if (dist < inner) { res += 20 - dist / inner * 20; } else if (dist < outer) { res -= 2 - dist / outer * 2; } } else { var inner = Game.StaffRange + building.Radius; // откуда можно достать посохом var outer = building.CastRange; // куда достреливает башня double delta = -80; if (dist < inner) { res += building.Damage - delta; } else if (dist <= outer) { res += (dist - inner) / (outer - inner) * delta + building.Damage - delta; } } } else if (opp is AOrc) { var inner = Game.OrcWoodcutterAttackRange + my.Radius + Game.MinionSpeed + 20 /*запас*/; var outer = 800; if (dist < inner) { res += Game.OrcWoodcutterDamage + 15 - dist / inner * 15; } else if (dist < outer && my.MmSkillLevel < 5) { res -= 3 - (dist - inner) / (outer - inner) * 3; } } else if (opp is AFetish) { var inner = opp.CastRange + my.Radius + Game.DartRadius + 10; if (dist < inner) { var fetishTarget = MinionsTargetsSelector.Select(opp); if (fetishTarget == null || Geom.Sqr(dist - 5) < opp.GetDistanceTo2(fetishTarget)) { res += 15 - dist / inner * 15 + Game.DartDirectDamage; } } } } // не прижиматься к своим foreach (var co in MyCombats) { var dist = my.GetDistanceTo(co) - co.Radius - my.Radius; if (my.Id != co.Id && dist < 15) { res += 1 - dist / 15 * 1; } } if (Game.IsSkillsEnabled) { // прижиматься за главную башню var cr = new Point(World.Width - 258, 258); var cornerMaxDist = 800; var distToCorner = my.GetDistanceTo(cr); if (distToCorner < cornerMaxDist) { res -= 10 - (distToCorner / cornerMaxDist) * 10; } } // держаться подальше от места появления минионов var spawnDelta = Game.FactionMinionAppearanceIntervalTicks * 0.33; var spawnRemains = World.TickIndex % Game.FactionMinionAppearanceIntervalTicks; if (spawnRemains < spawnDelta) { foreach (var pt in MagicConst.MinionAppearencePoints) { var dist = pt.GetDistanceTo(my); var inner = 450; if (dist < inner) { res += 14 - dist / inner * 14; } } } // двигаться по пути к бонусу if (NextBonusWaypoint != null) { var dist = my.GetDistanceTo(NextBonusWaypoint); var outer = 100.0; if (dist < outer) { res -= GoToBonusDanger - dist / outer * GoToBonusDanger; } } var nearestRoad = RoadsHelper.Roads.ArgMin(seg => seg.GetDistanceTo(my)); var linePadding = nearestRoad.LaneType == ALaneType.Middle || nearestRoad.LaneType == ALaneType.Middle2 ? 250.0 : 160.0; // не прижиматься к лесу if (MagicConst.TreesFreeCircles.All(x => x.GetDistanceTo(my) > x.Radius)) { var distToLine = nearestRoad.GetDistanceTo(my); var outerPadding = 500; if (distToLine > linePadding && distToLine < outerPadding) { res += (distToLine - linePadding) / (outerPadding - linePadding) * 10; } else if (distToLine >= outerPadding) { res += 10; } } { // прижиматься к центру дорожки var distToLine = RoadsHelper.Roads.Where(seg => seg.LaneType != ALaneType.Middle2).Min(seg => seg.GetDistanceTo(my)); var outerPadding = 500; if (distToLine > linePadding && distToLine < outerPadding) { res -= 1 - (distToLine - linePadding) / (outerPadding - linePadding) * 1; } else if (distToLine <= linePadding) { res -= 1; } } // не прижиматься к стене var distToBorders = Math.Min(Math.Min(my.X, my.Y), Math.Min(Const.MapSize - my.X, Const.MapSize - my.Y)); var bordersPadding = 45; if (distToBorders < bordersPadding) { res += 4 - distToBorders / bordersPadding * 4; } // не перекрывать бонус foreach (var bonus in BonusesObserver.Bonuses) { var inner = bonus.Radius + my.Radius; var dist = my.GetDistanceTo(bonus); if (dist <= inner && !bonus.Exists && bonus.RemainingAppearanceTicks < 100) { res += 30 + 20 - dist / inner * 20; } } // не прижиматься к углам for (var i = 0; i <= 3; i += 3) { var corner = Const.MapCorners[i]; var dist = my.GetDistanceTo(corner); var outer = 500; if (dist < outer) { res += 7 - dist / outer * 7; } } foreach (var tr in BuildingsDangerTriangles) { if (Geom.ContainPoint(tr, my)) { res += new Segment(tr[0], tr[1]).GetDistanceTo(my) / 100 * 10; } } return(res); }
public static WizardPath[] DijkstraFindPath(AWizard start, DijkstraPointStopFunc stopFunc, DijkstraPointCostFunc costFunc) { start = new AWizard(start); _startState = start; _obstacles = Combats .Where(x => !x.IsOpponent && x.Id != start.Id && x.GetDistanceTo2(start) < Geom.Sqr(start.VisionRange)) // (нейтральные включительно) .ToArray(); var startCell = FindNearestCell(start); if (startCell == null) { return new WizardPath[] {} } ; var endCells = new List <Cell>(); DijkstraStart(startCell, cell => { var point = _points[cell.I, cell.J]; var status = stopFunc(point); if (status == DijkstraStopStatus.Take || status == DijkstraStopStatus.TakeAndStop) { endCells.Add(cell); } if (status == DijkstraStopStatus.Stop || status == DijkstraStopStatus.TakeAndStop) { return(true); } return(false); }, costFunc); return(endCells.Select(endCell => { var cellsPath = DijkstraGeneratePath(startCell, endCell); var res = new WizardPath { start }; res.AddRange(cellsPath.Select(cell => _points[cell.I, cell.J])); return res; }).ToArray()); } }
public virtual bool IntersectsWith(ACircularUnit unit) { // если касаются, то false return(GetDistanceTo2(unit) < Geom.Sqr(Radius + unit.Radius)); }