/// <see cref="SCVBuildExecutionBase.ContinueMovingToTarget"/> protected override bool ContinueMovingToTarget() { /// First try to retrieve the target building from the scenario. TerranBuilding targetBuilding = this.Scenario.GetElementOnMap <TerranBuilding>(this.targetBuildingID.Read(), MapObjectLayerEnum.GroundObjects); if (targetBuilding == null) { /// Target building not found -> finish command execution. return(true); } /// Check the distance between the SCV and the target building. RCNumber distance = MapUtils.ComputeDistance(this.RecipientSCV.Area, targetBuilding.Area); if (distance > Weapon.NEARBY_DISTANCE) { /// Distance not reached yet -> continue execution if SCV is still moving. return(!this.RecipientSCV.MotionControl.IsMoving); } /// Check if the target building has an inactive construction job. if (targetBuilding.ConstructionJob == null || targetBuilding.ConstructionJob.AttachedSCV != null) { /// Target building doesn't have an inactive construction job -> finish command execution. return(true); } /// Attach the SCV to the construction job of the target building. targetBuilding.ConstructionJob.AttachSCV(this.RecipientSCV); this.Status = SCVBuildExecutionStatusEnum.Constructing; return(false); }
/// <summary> /// Creates command executions for undefined command type. /// </summary> /// <param name="scvsToHandle">The SCVs to order.</param> /// <param name="fullEntitySet">The set of selected entities.</param> /// <param name="targetPosition">The target position.</param> /// <param name="targetEntityID">The ID of the target entity or -1 if undefined.</param> private IEnumerable <CmdExecutionBase> CreateUndefinedExecutions(RCSet <SCV> scvsToHandle, RCSet <Entity> fullEntitySet, RCNumVector targetPosition, int targetEntityID) { Entity targetEntity = scvsToHandle.First().Scenario.GetElementOnMap <Entity>(targetEntityID, MapObjectLayerEnum.GroundObjects); TerranBuilding targetBuilding = targetEntity as TerranBuilding; MagicBox magicBox = new MagicBox(fullEntitySet, targetPosition); foreach (SCV scv in scvsToHandle.Where(scv => !scv.IsConstructing)) { if (targetBuilding != null && targetBuilding.Owner == scv.Owner && targetBuilding.ConstructionJob != null && !targetBuilding.ConstructionJob.IsFinished && targetBuilding.ConstructionJob.AttachedSCV == null) { /// The target entity is a friendly Terran building that is under construction but its construction is /// not currently in progress -> start a continue build command. yield return(new SCVContinueBuildExecution(scv, targetPosition, targetBuilding.ID.Read())); } else if (targetEntity != null && targetEntity.Owner == scv.Owner && this.IsValidTargetForRepair(targetEntity)) { /// The target entity is a friendly entity and is valid for a repair command -> start a repair command. yield return(new SCVRepairExecution(scv, targetPosition, targetEntityID)); } else if (targetEntity != null && targetEntity.Owner != null && targetEntity.Owner != scv.Owner) { /// The target entity is an enemy entity -> start an attack execution. yield return(new AttackExecution(scv, magicBox.GetTargetPosition(scv), targetEntityID)); } else { /// In any other cases -> start a move execution. yield return(new MoveExecution(scv, magicBox.GetTargetPosition(scv), targetEntityID)); } /// TODO: Handle the cases for Repair, Gather and Return commands! } }
/// <summary> /// Do SCV activities during construction. /// </summary> private void MakeScvActivityDuringConstruction() { /// Start using the build tool of the SCV and decrease the move-timer. TerranBuilding constructedBuilding = this.RecipientSCV.ConstructionJob.ConstructedBuilding; if (this.timeToNextScvMoveDuringConstruction.Read() > 0) { if (!this.recipientSCV.Read().MotionControl.IsMoving&& this.recipientSCV.Read().Armour.Target == null) { this.recipientSCV.Read().Armour.StartAttack(constructedBuilding.ID.Read(), SCV.SCV_BUILD_TOOL_NAME); } this.timeToNextScvMoveDuringConstruction.Write(this.timeToNextScvMoveDuringConstruction.Read() - 1); return; } /// Generate a random position inside the building area and move the SCV there. /// TODO: do not use the default random generator because the engine shall be deterministic! RCIntRectangle buildingQuadRect = constructedBuilding.MapObject.QuadraticPosition; RCIntRectangle buildingCellRect = this.Scenario.Map.QuadToCellRect(buildingQuadRect); RCNumVector movePosition = new RCNumVector( RandomService.DefaultGenerator.Next(buildingCellRect.Left, buildingCellRect.Right), RandomService.DefaultGenerator.Next(buildingCellRect.Top, buildingCellRect.Bottom)); this.recipientSCV.Read().MotionControl.StartMoving(movePosition); this.recipientSCV.Read().Armour.StopAttack(); /// Reset the timer. this.timeToNextScvMoveDuringConstruction.Write(TIME_BETWEEN_SCV_MOVES); }
/// <see cref="Weapon.CanTargetEntity"/> public override bool CanTargetEntity(Entity entityToCheck) { TerranBuilding terranBuilding = entityToCheck as TerranBuilding; return(terranBuilding != null && terranBuilding.ConstructionJob != null && !terranBuilding.ConstructionJob.IsFinished && terranBuilding.ConstructionJob.AttachedSCV == this.Owner); }
/// <see cref="CmdExecutionBase.InitializeImpl"/> protected override void InitializeImpl() { TerranBuilding building = this.recipientSCV.Read().ConstructionJob.ConstructedBuilding; this.recipientSCV.Read().ConstructionJob.DetachSCV(); /// Move the SCV to the bottom-left corner of the building. RCIntRectangle buildingQuadRect = building.MapObject.QuadraticPosition; RCIntRectangle buildingCellRect = this.Scenario.Map.QuadToCellRect(buildingQuadRect); this.recipientSCV.Read().MotionControl.StartMoving(new RCNumVector(buildingCellRect.Left, buildingCellRect.Bottom - 1)); }
private void Attack(GameState gameState, TerranBuilding commandCenter, List <TerranUnit> soldiers, List <Command> commands) { // Initially, await X idle soldiers and send them toward the enemy's starting location. // Once they're there and have no further orders, send them to attack any sighted enemy unit/structure. // Once we run out of those, send them to scout every resource deposit until we find more. var enemyStartLocation = gameState.MapData.Raw.StartLocations.OrderByDescending(point => commandCenter.GetDistance(point)).First(); var idleSoldiers = soldiers.Where(s => s.Raw.Orders.Count == 0).ToList(); if (!soldiers.Any(s => s.GetDistance(enemyStartLocation) < 5f) || gameState.EnemyUnits.Any(e => e.GetDistance(enemyStartLocation) < 10f)) { if (idleSoldiers.Count >= AttackThreshold) { foreach (var soldier in idleSoldiers) { commands.Add(soldier.AttackMove(enemyStartLocation.X, enemyStartLocation.Y)); } } return; } if (gameState.EnemyUnits.Count > 0) { foreach (var soldier in idleSoldiers) { commands.Add(soldier.AttackMove(gameState.EnemyUnits[0].X, gameState.EnemyUnits[0].Y)); } return; } var unscoutedLocations = gameState.MapData.Deposits.Select(d => d.Center).ToList(); foreach (var location in unscoutedLocations) { if (soldiers.Any(s => s.GetDistance(location) < 5f || s.Raw.Orders.Any(o => o.TargetWorldSpacePos.GetDistance(location) < 5f))) { continue; } if (idleSoldiers.Count == 0) { break; } commands.Add(idleSoldiers[0].AttackMove(location)); idleSoldiers.RemoveAt(0); } }
private void BuildWorker(GameState gameState, TerranBuilding commandCenter, List <Command> commands) { if (commandCenter.IsBuildingSomething) { return; } if (workersByMineralDeposit.All(pair => pair.Value.Count >= 2)) { return; } if (gameState.Observation.PlayerCommon.Minerals >= 50 && gameState.Observation.PlayerCommon.FoodUsed < gameState.Observation.PlayerCommon.FoodCap) { commands.Add(commandCenter.Train(TerranUnitType.SCV)); } }
/// <see cref="ProductionJob.StartImpl"/> protected override bool StartImpl() { /// Create the building and begin its construction. bool success = this.ElementFactory.CreateElement(this.buildingProduct.Name, this.OwnerPlayer, this.topLeftQuadTile.Read(), this.starterSCV.Read()); if (success) { TerranBuilding building = this.OwnerPlayer.Scenario.GetFixedEntity <TerranBuilding>(this.topLeftQuadTile.Read()); if (building == null) { throw new InvalidOperationException("Impossible case happened!"); } this.constructedBuilding.Write(building); this.constructedBuilding.Read().OnAttachConstructionJob(this); this.AttachSCV(this.starterSCV.Read()); this.starterSCV.Write(null); } return(success); }
public IReadOnlyList <Command> Act(GameState gameState) { /* Detailed strategy: * * 1. If there are mineral deposits near a base and not being fully mined, supply is not maxed, the base is not currently building anything, and minerals are available, build a worker. * 2. If minerals and supply are available, and there is a Barracks or equivalent not building a basic military unit, start building one. * 3. If supply is maxed out, build a Supply Depot or equivalent. * 4. If minerals are available, build a Barracks or equivalent. * * No expansion. Once you hit the threshold of military units in existence, attack-move your opponent's base. * * Implement for Terran only initially (no Creep/Pylon placement concerns). * * Implement for maps with only two starting locations, so we don't scout. */ var commands = new List <Command>(); var controlledUnits = gameState.RawUnits.Where(u => u.Alliance == Alliance.Self).ToList(); TerranBuilding commandCenter = null; var workers = new List <TerranUnit>(); var soldiers = new List <TerranUnit>(); var soldierProducers = new List <TerranBuilding>(); if (sleep > 0) { sleep -= 1; return(commands); } foreach (var unit in gameState.Units) { if (unit is TerranBuilding terranBuilding) { if (terranBuilding.TerranBuildingType == TerranBuildingType.CommandCenter || terranBuilding.TerranBuildingType == TerranBuildingType.OrbitalCommand || terranBuilding.TerranBuildingType == TerranBuildingType.PlanetaryFortress) { commandCenter = terranBuilding; } else if (terranBuilding.TerranBuildingType == TerranBuildingType.Barracks) { soldierProducers.Add(terranBuilding); } } else if (unit is TerranUnit terranUnit) { if (terranUnit.TerranUnitType == TerranUnitType.SCV) { workers.Add(terranUnit); } else if (terranUnit.TerranUnitType == TerranUnitType.Marine) { soldiers.Add(terranUnit); } } } Deposit closestDeposit = gameState.MapData.Deposits.OrderBy(d => commandCenter.GetDistance(d.Center)).First(); var mineralDeposits = closestDeposit.Resources.Where(u => u.IsMineralDeposit).ToList(); // First update, ignore the default worker orders, which are to mass on the center mineral deposit // (this ends up causing problems since we have to keep track of who is harvesting what ourselves) if (first) { foreach (var worker in workers) { worker.Raw.Orders.Clear(); } workersByMineralDeposit = mineralDeposits.ToDictionary(m => m.Raw.Tag, m => new List <ulong>()); } if (commandCenter == null) { // Accept death as inevitable. return(new List <Command>()); // TODO: Surrender? } // Each possible behavior is implemented as a function that adds a command to the list // if the situation is appropriate. We will then execute whichever command was added first. BuildWorker(gameState, commandCenter, commands); BuildMarine(gameState, soldierProducers, commands); BuildSupplyDepot(gameState, workers, soldierProducers, commands); BuildBarracks(gameState, workers, commands); commands = commands.Take(1).ToList(); // If we tell a worker to build something, make sure we don't think he's harvesting if (commands.Count == 1) { sleep = 1; RemoveWorkerFromHarvestAssignments(commands[0].Unit); } // No matter what else happens, we can always attack, and we should always set idle workers to harvest minerals Attack(gameState, commandCenter, soldiers, commands); SetIdleWorkerToHarvest(gameState, workers, mineralDeposits, commands); if (first) { first = false; // Make sure workers don't automatically harvest minerals, since we're managing assignments ourselves commands.Add(commandCenter.RallyWorkers(commandCenter.Raw.Pos.X, commandCenter.Raw.Pos.Y)); } return(commands); }