Example #1
0
        public bool _ethalonCanCastMagicMissile(ACircularUnit opp, bool checkCooldown)
        {
            var distTo = GetDistanceTo(opp);

            if (distTo > CastRange + opp.Radius + MyStrategy.Game.MagicMissileRadius)
            {
                return(false);
            }

            var angleTo    = GetAngleTo(opp);
            var deltaAngle = Math.Atan2(opp.Radius, distTo);
            var angles     = new[] { angleTo, angleTo + deltaAngle, angleTo - deltaAngle };

            foreach (var angle in angles)
            {
                if (Math.Abs(angle) > MyStrategy.Game.StaffSector / 2)
                {
                    continue;
                }

                var proj = new AProjectile(this, angle, ProjectileType.MagicMissile);
                if (CheckProjectileCantDodge(proj, opp as ACombatUnit))
                {
                    return(true);
                }
            }
            return(false);
        }
Example #2
0
        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);
        }
Example #3
0
 public bool CheckProjectileCantDodge(AProjectile proj, ACombatUnit opp)
 {
     return((opp is AWizard ? Utility.Range(-Math.PI / 2, Math.PI / 2, 6) : new[] { 0.0 }).All(changeAngle =>
     {
         var path = proj.Emulate(opp, changeAngle);
         return path.Any(x => x.State == AProjectile.ProjectilePathState.Shot && x.Target.Faction != Faction);
     }));
 }
Example #4
0
 public AProjectile(AProjectile unit) : base(unit)
 {
     SpeedX            = unit.SpeedX;
     SpeedY            = unit.SpeedY;
     Speed             = Geom.Hypot(SpeedX, SpeedY);
     Type              = unit.Type;
     OwnerUnitId       = unit.OwnerUnitId;
     RemainingDistance = unit.RemainingDistance;
     Damage            = unit.Damage;
     MinDamage         = unit.MinDamage;
 }
Example #5
0
        public static double GetFireballDamage(AProjectile proj, ACombatUnit unit) // без учета сколько жизней осталось
        {
            var    dist   = proj.GetDistanceTo(unit) - unit.Radius;
            double damage = 0;

            if (dist <= MyStrategy.Game.FireballExplosionMaxDamageRange)
            {
                damage += proj.Damage;
            }
            else if (dist <= MyStrategy.Game.FireballExplosionMinDamageRange)
            {
                var ratio = 1 - (dist - MyStrategy.Game.FireballExplosionMaxDamageRange) / (MyStrategy.Game.FireballExplosionMinDamageRange - MyStrategy.Game.FireballExplosionMaxDamageRange);
                damage += ratio * (proj.Damage - proj.MinDamage) + proj.MinDamage;
            }
            return(damage);
        }
Example #6
0
        public void InitializeProjectiles()
        {
            var projectiles = ProjectilesObserver.Projectiles;

            ProjectilesPaths1 = new AProjectile[projectiles.Length][];
            for (var i = 0; i < projectiles.Length; i++)
            {
                var proj = projectiles[i];
                ProjectilesPaths1[i]    = new AProjectile[ProjectilesCheckTicks * proj.MicroTicks + 1];
                ProjectilesPaths1[i][0] = new AProjectile(proj);
                for (var j = 1; j < ProjectilesPaths1[i].Length; j++)
                {
                    ProjectilesPaths1[i][j] = new AProjectile(ProjectilesPaths1[i][j - 1]);
                    ProjectilesPaths1[i][j].MicroMove();
                }
            }
        }
Example #7
0
        bool TryCutTrees(bool cutNearest, FinalMove move)
        {
            var self         = new AWizard(ASelf);
            var nearestTrees = TreesObserver.Trees.Where(
                t => self.GetDistanceTo(t) < self.CastRange + t.Radius + Game.MagicMissileRadius
                ).ToArray();

            if (nearestTrees.Length == 0)
            {
                return(false);
            }

            if (self.RemainingActionCooldownTicks == 0)
            {
                if (self.GetStaffAttacked(nearestTrees).Length > 0)
                {
                    move.Action = ActionType.Staff;
                    return(true);
                }
                if (self.RemainingMagicMissileCooldownTicks == 0)
                {
                    var proj = new AProjectile(self, 0, ProjectileType.MagicMissile);
                    var path = EmulateProjectileWithNearest(proj);
                    if (path.Count == 0 || path[path.Count - 1].EndDistance < self.CastRange - Const.Eps)
                    {
                        move.MinCastDistance = path[path.Count - 1].EndDistance;
                        move.Action          = ActionType.MagicMissile;
                        return(true);
                    }
                }
            }
            if (cutNearest)
            {
                var nearest = nearestTrees.OrderBy(t => self.GetDistanceTo2(t)).FirstOrDefault();
                move.MoveTo(null, nearest);
            }

            return(false);
        }
Example #8
0
 List <AProjectile.ProjectilePathSegment> EmulateProjectileWithNearest(AProjectile projectile)
 {
     return(projectile.Emulate(Combats.Where(x => x.GetDistanceTo(ASelf) - x.Radius < 900).ToArray(), 0.0));
 }
Example #9
0
        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));
        }
Example #10
0
        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
            });
        }
Example #11
0
        private List <ProjectilePathSegment> _emulate(ACombatUnit[] nearestUnits, double wizardsChangeAngle, TargetsSelector minionsTargetsSelector)
        {
            if (Type != ProjectileType.MagicMissile && Type != ProjectileType.FrostBolt && Type != ProjectileType.Fireball)
            {
                throw new Exception("Unsupported projectile type " + Type);
            }

            // NOTE: nearestUnits уже склонированы
            // NOTE: nearestUnits и они же в minionsTargetsSelector - одни и те же instance

            var list              = new List <ProjectilePathSegment>();
            var projectile        = new AProjectile(this);
            var owner             = nearestUnits.FirstOrDefault(x => x.Id == OwnerUnitId);
            var nearestCandidates = nearestUnits.Where(x => x.Id != OwnerUnitId).ToArray();

            while (projectile.Exists)
            {
                projectile.Move(proj =>
                {
                    if (Type == ProjectileType.Fireball)
                    {
                        double selfDamage = 0;
                        double oppDamage  = 0;
                        var selfBurned    = 0;
                        var oppBurned     = 0;
                        var selfDeads     = 0;
                        var oppDeads      = 0;

                        ACombatUnit importantTarget = null;

                        foreach (var unit in nearestCandidates)
                        {
                            var damage = GetFireballDamage(proj, unit);

                            if (damage > 0)
                            {
                                var deads = 0;
                                if (damage >= unit.Life - Const.Eps)
                                {
                                    // killed
                                    deads++;
                                    damage = unit.Life;
                                }

                                if (unit.Faction == proj.Faction)
                                {
                                    if (unit is AWizard)
                                    {
                                        selfDamage += damage;
                                        selfBurned++;
                                        selfDeads += deads;
                                    }
                                }
                                else if (Utility.HasConflicts(proj, unit))
                                {
                                    oppDamage += damage;
                                    oppBurned++;
                                    oppDeads += deads;
                                    if (importantTarget == null || _targetImportance(unit) > _targetImportance(importantTarget))
                                    {
                                        importantTarget = unit;
                                    }
                                }
                            }
                        }
                        if (owner != null)
                        {
                            var damage = GetFireballDamage(proj, owner);
                            if (damage > 0)
                            {
                                selfBurned++;
                                if (damage >= owner.Life - Const.Eps)
                                {
                                    // killed
                                    selfDamage += owner.Life;
                                    selfDeads++;
                                }
                                else
                                {
                                    selfDamage += damage;
                                }
                            }
                        }
                        list.Add(new ProjectilePathSegment
                        {
                            StartDistance      = list.Count == 0 ? 0 : list.Last().EndDistance,
                            EndDistance        = list.Count == 0 ? 0 : list.Last().EndDistance,
                            OpponentDamage     = oppDamage,
                            SelfDamage         = selfDamage,
                            OpponentDeadsCount = oppDeads,
                            SelfDeadsCount     = selfDeads,
                            State          = (selfDamage + oppDamage < Const.Eps) ? ProjectilePathState.Free : ProjectilePathState.Fireball,
                            OpponentBurned = oppBurned,
                            SelfBurned     = selfBurned,
                            Target         = importantTarget,
                        });
                    }
                    else
                    {
                        var inter = proj.GetFirstIntersection(nearestCandidates);

                        if (inter != null)
                        {
                            if (list.Count == 0 || list.Last().State != ProjectilePathState.Shot)
                            {
                                var opp = inter as ACombatUnit;
                                list.Add(new ProjectilePathSegment
                                {
                                    StartDistance  = list.Count == 0 ? 0 : list.Last().EndDistance,
                                    EndDistance    = list.Count == 0 ? 0 : list.Last().EndDistance,
                                    State          = ProjectilePathState.Shot,
                                    Target         = Utility.CloneCombat(opp),
                                    SelfDamage     = proj.Faction == inter.Faction ? Math.Min(opp.Life, Damage) : 0,
                                    OpponentDamage = Utility.HasConflicts(proj, opp) ? Math.Min(opp.Life, Damage) : 0,
                                });
                            }
                        }
                        else
                        {
                            if (list.Count == 0 || list.Last().State != ProjectilePathState.Free)
                            {
                                list.Add(new ProjectilePathSegment
                                {
                                    StartDistance = list.Count == 0 ? 0 : list.Last().EndDistance,
                                    EndDistance   = list.Count == 0 ? 0 : list.Last().EndDistance,
                                    State         = ProjectilePathState.Free,
                                });
                            }
                        }
                    }
                    list.Last().EndDistance += proj.Speed / proj.MicroTicks;

                    return(true);
                });
                foreach (var unit in nearestCandidates)
                {
                    if (unit is AWizard)
                    {
                        var wizard = unit as AWizard;
                        var dir    = (wizard - this).RotateClockwise(wizardsChangeAngle) + wizard; // вдоль снаряда
                        wizard.MoveTo(dir, null, w =>
                        {
                            var tree = TreesObserver.GetNearestTree(w);
                            return((tree == null || !w.IntersectsWith(tree)) && wizard.GetFirstIntersection(nearestCandidates) == null);
                        });
                    }
                    else if (unit is AMinion)
                    {
                        var target = minionsTargetsSelector.Select(unit);
                        unit.EthalonMove(target);
                    }
                }
            }
            return(list);
        }