private static void SetAttack(Entity entity, EntityType attackedEntityType, int attackedEntitySize, HashSet <Entity> enemiesUnderAttack, Dictionary <int, EntityAction> entityActions)
        {
            if (entityActions.ContainsKey(entity.Id))
            {
                return;
            }

            Entity?bestEnemy = null;
            var    enemies   = enemiesUnderAttack.Where(e => e.EntityType == attackedEntityType).ToList();

            foreach (var enemy in enemies)
            {
                if (bestEnemy == null ||
                    enemy.Health < bestEnemy.Value.Health)
                {
                    bestEnemy = enemy;
                }

                if (enemy.Health == bestEnemy.Value.Health &&
                    entity.Position.Distance(enemy.Position) > entity.Position.Distance(bestEnemy.Value.Position))
                {
                    bestEnemy = enemy;
                }
            }

            if (bestEnemy != null)
            {
                var attackAction = new AttackAction(bestEnemy.Value.Id, null);
                entityActions.Add(entity.Id, new EntityAction(null, null, attackAction, null));

                var newEnemyEntity = bestEnemy.Value.Health > 5
                    ? new Entity
                {
                    Active     = bestEnemy.Value.Active,
                    EntityType = bestEnemy.Value.EntityType,
                    Health     = bestEnemy.Value.Health - 5,
                    Id         = bestEnemy.Value.Id,
                    Position   = bestEnemy.Value.Position,
                    PlayerId   = bestEnemy.Value.PlayerId
                }
                    : (Entity?)null;

                if (attackedEntitySize > 1)
                {
                    for (int y = 0; y < attackedEntitySize; y++)
                    {
                        for (int x = 0; x < attackedEntitySize; x++)
                        {
                            ScoreMap.Set(bestEnemy.Value.Position.X + x, bestEnemy.Value.Position.Y + y, newEnemyEntity);
                        }
                    }
                }
                else
                {
                    ScoreMap.Set(bestEnemy.Value.Position, newEnemyEntity);
                }
            }
        }
        private static void SetMoveAction(Entity entity, Vec2Int approxTarget, Dictionary <int, EntityAction> entityActions, DebugInterface debugInterface)
        {
            if (entityActions.ContainsKey(entity.Id))
            {
                return;
            }

            var     bestTarget = ASearchMove(entity, approxTarget, out var cameFrom, out var costSoFar);
            Vec2Int moveTarget = GetMoveTarget(entity.Position, bestTarget, cameFrom, Blue, debugInterface);

            ScoreMap.Set(entity.Position, null);
            ScoreMap.Set(moveTarget, entity);

            var moveAction = new MoveAction(moveTarget, false, false);

            entityActions.Add(entity.Id, new EntityAction(moveAction, null, null, null));
        }
        public Action GetAction(PlayerView playerView, DebugInterface debugInterface)
        {
            if (Params.IsDebug)
            {
                debugInterface.Send(new DebugCommand.SetAutoFlush(true));
            }

            if (playerView.CurrentTick == 10 && playerView.Players.Length == 2)
            {
                Params.RangedBaseBuildingLimit = 35;
                Params.MaxBuilderUnitsCount   += 30;
                Params.MaxRangedUnitsCount    += 20;
                Params.MaxHouseCount          += 10;
            }

            var entityActions = new Dictionary <int, EntityAction>();

            ScoreMap.InitMap(playerView);
            DrawScoreMap(debugInterface);
            IsDanger = DangerCheck();

            // repairing
            Entity? builder     = null;
            Vec2Int?moveTarget  = null;
            int     minDistance = int.MaxValue;

            foreach (Entity entity in playerView.Entities)
            {
                if (entity.PlayerId != ScoreMap.MyId || entity.EntityType != EntityType.BuilderUnit)
                {
                    continue;
                }

                BuilderUnitActions.SetRepair(playerView, entity, entityActions);
                if (!ScoreMap.AnyRepairScoreMoreThanOne || entityActions.ContainsKey(entity.Id))
                {
                    continue;
                }

                var approxTarget = BuilderUnitActions.GetApproxTarget(entity, entityActions);

                var bestTarget = ASearchMove(entity, approxTarget, out var cameFrom, out var costSoFar);
                var cell       = ScoreMap.Get(bestTarget);
                if (cell.RepairScore > 1 && costSoFar[bestTarget] < minDistance)
                {
                    builder     = entity;
                    moveTarget  = GetMoveTarget(builder.Value.Position, bestTarget, cameFrom, Blue, debugInterface);
                    minDistance = costSoFar[bestTarget];
                }
            }

            if (builder != null && moveTarget != null)
            {
                ScoreMap.Set(builder.Value.Position, null);
                ScoreMap.Set(moveTarget.Value, builder.Value);

                var moveAction = new MoveAction(moveTarget.Value, false, false);
                entityActions.Add(builder.Value.Id, new EntityAction(moveAction, null, null, null));
            }

            // building turret
            if ((playerView.Players.Length > 2 || playerView.CurrentTick >= 300) &&
                ScoreMap.MyActiveRangedBases.Count > 0 &&
                (ScoreMap.Limit >= Params.TurretBuildingLimit || playerView.CurrentTick >= 300) &&
                ScoreMap.MyResource >= ScoreMap.TurretProperties.InitialCost &&
                ScoreMap.MyNotActiveTurrets.Count <= 1)
            {
                BuilderUnitActions.SetBuild(EntityType.Turret, ScoreMap.TurretProperties.Size, entityActions, debugInterface);
                ScoreMap.MyResource -= ScoreMap.TurretProperties.InitialCost;
            }

            // building ranged base
            if (ScoreMap.MyActiveRangedBases.Count == 0 &&
                ScoreMap.MyResource >= ScoreMap.RangedBaseProperties.InitialCost &&
                ScoreMap.MyNotActiveRangedBases.Count == 0)
            {
                BuilderUnitActions.SetBuild(EntityType.RangedBase, ScoreMap.RangedBaseProperties.Size, entityActions, debugInterface);
                ScoreMap.MyResource -= ScoreMap.RangedBaseProperties.InitialCost;
            }

            // building house
            if (((ScoreMap.MyActiveRangedBases.Count == 0 &&
                  ScoreMap.Limit >= Params.RangedBaseBuildingLimit &&
                  ScoreMap.MyResource >=
                  ScoreMap.RangedBaseProperties.InitialCost + ScoreMap.HouseProperties.InitialCost) ||

                 ((ScoreMap.MyActiveRangedBases.Count > 0 ||
                   ScoreMap.MyNotActiveRangedBases.Count > 0 ||
                   ScoreMap.Limit < Params.RangedBaseBuildingLimit) &&
                  ScoreMap.MyResource >= ScoreMap.HouseProperties.InitialCost)
                 ) &&
                ScoreMap.Limit + 10 >= ScoreMap.AvailableLimit &&
                ScoreMap.MyNotActiveHouses.Count <= 1 &&
                ScoreMap.MyNotActiveHouses.Count + ScoreMap.MyActiveHouses.Count < Params.MaxHouseCount)
            {
                BuilderUnitActions.SetBuild(EntityType.House, ScoreMap.HouseProperties.Size, entityActions, debugInterface);
                ScoreMap.MyResource -= ScoreMap.HouseProperties.InitialCost;
            }

            foreach (Entity entity in playerView.Entities)
            {
                if (entity.PlayerId != ScoreMap.MyId)
                {
                    continue;
                }

                switch (entity.EntityType)
                {
                case EntityType.BuilderUnit:
                {
                    BuilderUnitActions.SetAttack(entity, entityActions);
                    var approxTarget = BuilderUnitActions.GetApproxTarget(entity, entityActions);
                    SetMoveAction(entity, approxTarget, entityActions, debugInterface);
                    continue;
                }

                case EntityType.MeleeUnit:
                {
                    CombatUnitAction.SetAttack(entity, 1, 1, entityActions);
                    var approxTarget = CombatUnitAction.GetAttackTarget(entity, entityActions);
                    SetMoveAction(entity, approxTarget, entityActions, debugInterface);
                    continue;
                }

                case EntityType.RangedUnit:
                {
                    CombatUnitAction.SetAttack(entity, 5, 1, entityActions);
                    var approxTarget = CombatUnitAction.GetAttackTarget(entity, entityActions);
                    SetMoveAction(entity, approxTarget, entityActions, debugInterface);
                    continue;
                }

                case EntityType.Turret:
                {
                    if (!entity.Active)
                    {
                        continue;
                    }

                    CombatUnitAction.SetAttack(entity, 5, 2, entityActions);
                    continue;
                }

                case EntityType.BuilderBase:
                {
                    int unitCost = ScoreMap.BuilderUnitProperties.InitialCost + ScoreMap.MyBuilderUnits.Count;

                    if ((IsDanger && ScoreMap.MyResource >= unitCost * 2 ||
                         !IsDanger && ScoreMap.MyResource >= unitCost) &&
                        ScoreMap.MyBuilderUnits.Count < ScoreMap.BuilderUnitTargets.Count &&
                        ScoreMap.MyBuilderUnits.Count < Params.MaxBuilderUnitsCount)
                    {
                        var approxTarget = BuilderUnitActions.GetApproxTarget(entity, entityActions);
                        SetBuildUnitAction(entity, approxTarget, EntityType.BuilderUnit, unitCost, entityActions);
                    }

                    if (!entityActions.ContainsKey(entity.Id))
                    {
                        entityActions.Add(entity.Id, new EntityAction(null, null, null, null));
                    }

                    continue;
                }

                case EntityType.MeleeBase:
                {
                    int unitCost = ScoreMap.MeleeUnitProperties.InitialCost + ScoreMap.MyMeleeUnits.Count;

                    if ((IsDanger && ScoreMap.MyResource >= unitCost ||
                         !IsDanger && ScoreMap.MyResource >= unitCost * 2) &&
                        ScoreMap.MyRangedUnits.Count * 2 >= Params.MaxRangedUnitsCount &&
                        ScoreMap.MyMeleeUnits.Count < Params.MaxMeleeUnitsCount)
                    {
                        var approxTarget = CombatUnitAction.GetAttackTarget(entity, entityActions);
                        SetBuildUnitAction(entity, approxTarget, EntityType.MeleeUnit, unitCost, entityActions);
                    }

                    if (!entityActions.ContainsKey(entity.Id))
                    {
                        entityActions.Add(entity.Id, new EntityAction(null, null, null, null));
                    }

                    continue;
                }

                case EntityType.RangedBase:
                {
                    if (!entity.Active)
                    {
                        continue;
                    }

                    int unitCost = ScoreMap.RangedUnitProperties.InitialCost + ScoreMap.MyRangedUnits.Count;

                    if ((IsDanger && ScoreMap.MyResource >= unitCost ||
                         !IsDanger && ScoreMap.MyResource >= unitCost * 3) &&
                        ScoreMap.MyRangedUnits.Count < Params.MaxRangedUnitsCount)
                    {
                        var approxTarget = CombatUnitAction.GetAttackTarget(entity, entityActions);
                        SetBuildUnitAction(entity, approxTarget, EntityType.RangedUnit, unitCost, entityActions);
                    }

                    if (!entityActions.ContainsKey(entity.Id))
                    {
                        entityActions.Add(entity.Id, new EntityAction(null, null, null, null));
                    }

                    continue;
                }
                }
            }

            return(new Action(entityActions));
        }