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); }