static Cell FindNearestCell(ACircularUnit my) { double ds = Const.MapSize / GridSize; var I = (int)(my.X / ds + Const.Eps); var J = (int)(my.Y / ds + Const.Eps); int seldI = int.MaxValue, seldJ = int.MaxValue; double minDist = int.MaxValue; var obstacles = _obstacles .Concat(BuildingsObserver.Buildings) .Concat(TreesObserver.Trees) .ToArray(); for (var di = 0; di < 2; di++) { for (var dj = 0; dj < 2; dj++) { var dst = WizardPath.GetSegmentWeight(_points[I + di, J + dj], my, true); if (dst < minDist && obstacles.All(ob => !Geom.SegmentCircleIntersects(my, _points[I + di, J + dj], ob, ob.Radius + my.Radius + MagicConst.RadiusAdditionalEpsilon)) ) { minDist = dst; seldI = di; seldJ = dj; } } } if (seldI == int.MaxValue) { return(null); } return(new Cell(I + seldI, J + seldJ)); }
double _getProjectilesDamage(List <AWizard> myStates) { var totalDamage = 0.0; foreach (var arr in ProjectilesPaths1) { if (arr[0].GetDistanceTo2(ASelf) > Geom.Sqr(1000)) { continue; } var fireballMinDist = 1000.0; AProjectile fireballMinDistState = null; AWizard fireballMinDistMyState = null; for (var ticksPassed = 0; ticksPassed < ProjectilesCheckTicks; ticksPassed++) { var cur = myStates[Math.Min(ticksPassed, myStates.Count - 1)]; for (var mt = 0; mt <= arr[0].MicroTicks; mt++) { var microTick = ticksPassed * arr[0].MicroTicks + mt; var proj = arr[microTick]; if (!proj.Exists) { ticksPassed = ProjectilesCheckTicks; // выход из внешнего цикла break; } if (proj.Type == ProjectileType.Fireball) { var dist = cur.GetDistanceTo(proj); if (dist < fireballMinDist && (!proj.IsFriendly || proj.RemainingDistance < Const.Eps)) // для своих фаерболов точно известно когда взорвется { fireballMinDist = dist; fireballMinDistState = proj; fireballMinDistMyState = cur; } } else { if (proj.IntersectsWith(cur) || microTick > 0 && Geom.SegmentCircleIntersects(arr[microTick - 1], proj, cur, cur.Radius + proj.Radius)) { totalDamage += proj.Damage; ticksPassed = ProjectilesCheckTicks; // выход из внешнего цикла break; } } } } if (fireballMinDistState != null) { totalDamage += AProjectile.GetFireballDamage(fireballMinDistState, fireballMinDistMyState); } } return(totalDamage); }
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))); }
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 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); }
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); }