private IMove EvaluateMove() { // throw new NotImplementedException(); var multiMove = new MultiMove(); multiMove.AddMove(new Message($"Fight for {FactoryState.FactoryId}")); var moveTroops = new MoveTroops(); multiMove.AddMove(moveTroops); for (int i = 0; i < StepsToPredict; i++) { //взять мои фабрики на этом расстоянии var myFactoriesForAttack = graph.Factories[FactoryState.FactoryId].GetLinks() .Where(x => x.Distance <= i && graph.Factories[x.DestinationId].Side == Side.MyOwn) .Select(x => graph.Factories[x.DestinationId]) .ToList(); //если могут захватить - ура if (myFactoriesForAttack.Any() && myFactoriesForAttack.Sum(x => x.TroopsCanBeUsed) > FactoryState.Enemies[i]) { int troopsUsed, needed; needed = troopsUsed = FactoryState.Enemies[i] ?? int.MaxValue; foreach (var myFactory in myFactoriesForAttack) { //TODO: sort factories by distance or smth else var sendedTroops = Math.Min(needed + 1, myFactory.TroopsCanBeUsed); // TODO: захват нейтралов совместный. +1 не совсем точно. нужно только для захвата но не деф. myFactory.TroopsCanBeUsed = graph.Factories[myFactory.Id].TroopsCanBeUsed = myFactory.TroopsCanBeUsed - sendedTroops; var shortestPath = GraphLinks.Links[myFactory.Id, FactoryState.FactoryId]; moveTroops.AddTroop(new Troop { Dst = FactoryState.FactoryId, DstInCommand = shortestPath.FirstFactoryId, Side = Side.MyOwn, Remaining = shortestPath.Distance + 1, Src = myFactory.Id, Size = sendedTroops }); needed -= sendedTroops; if (needed <= 0) { break; } } priorityValue = CostFunction.GetJobEstimatePriority( graph.Factories[FactoryState.FactoryId].Income * (graph.Factories[FactoryState.FactoryId].Side == Side.Neutral ? 1 : 2), troopsUsed, i); break; } } return(multiMove); }
public static IEnumerable <IMove> ProposeMoves(this Graph graph) { var stepsToPredict = Math.Max(graph.GetMaxCountSteps(), GraphLinks.MaxDistance); bool canSendBomb = graph.BombsAvailable_MyOwn > 0; var bombFactoriesCandidate = new List <Factory>(); var factories = new List <FactoryStates>(); var multiMove = new MultiMove(); yield return(multiMove); { //Get List available for attack/defense var holdState = Graph.GetCopy(graph); graph.UpdateAvailableTroops(holdState, true); for (int i = 0; i < stepsToPredict; i++) { holdState.DoNextMove(); graph.UpdateAvailableTroops(holdState); } // multiMove.AddMove(new Message("CBU:"+graph.Factories.Sum(x=>x.TroopsCanBeUsed))); if (canSendBomb) { var sendedBombTo = graph.Bombs.Any(x => x.Side == Side.MyOwn) ? graph.Bombs.Select(x => x.Dst).First() : -1; bombFactoriesCandidate = holdState.Factories.Where(x => x.Side == Side.Enemy && x.Income > 1 && x.Id != sendedBombTo).ToList(); } // если не моя по истечению ходов то стоит ее проверить. foreach (var factoryTarget in holdState.Factories.Where(x => x.Side != Side.MyOwn && x.Income > 0)) { var testGraph = Graph.GetCopy(graph); var move = new MoveTroops(); // шлем со всех туда. foreach (var myFactory in testGraph.Factories.Where(x => x.Side == Side.MyOwn && x.Id != factoryTarget.Id)) { var availableTroops = graph.Factories[myFactory.Id].TroopsCanBeUsed; if (availableTroops <= 0) { continue; } //TODO: слать с переправами var linkTo = myFactory.GetLinks().FirstOrDefault(link => link.DestinationId == factoryTarget.Id); if (linkTo.Distance > 0) { // if any move.AddTroop(new Troop { Dst = factoryTarget.Id, Side = myFactory.Side, Src = myFactory.Id, Remaining = linkTo.Distance + 1, Size = availableTroops }); } } move.ChangeGraph(testGraph); testGraph.DoSteps(testGraph.GetMaxCountSteps()); // Если изменился статус фабрики - запоминаем if (testGraph.Factories[factoryTarget.Id].Side == Side.MyOwn) { factories.Add(new FactoryStates(factoryTarget.Id, stepsToPredict)); } } } //Select & send the bomb if (canSendBomb) { var bombTo = bombFactoriesCandidate .OrderByDescending(x => x.Income) .ThenByDescending(f => f.GetLinks().Where(x => graph.Factories[x.DestinationId].Side == Side.MyOwn).Min(x => x.Distance)) // Самая удаленная от нас .FirstOrDefault(); if (bombTo != null) { var bombLinkSources = bombTo.GetLinks().Where(x => graph.Factories[x.DestinationId].Side == Side.MyOwn).OrderBy(x => x.Distance); if (bombLinkSources.Any()) { var bombLinkSettings = bombLinkSources.FirstOrDefault(); multiMove.AddMove( new SendBomb(new Bomb { Src = bombLinkSettings.DestinationId, Dst = bombTo.Id, Side = Side.MyOwn, Remaining = bombLinkSettings.Distance + 1, })); } } } { var jobs = new List <IJob>(); // Get moves to minimum attack if (factories.Any()) { multiMove.AddMove(new Message("Init Atack/Defense")); var holdState = Graph.GetCopy(graph); for (int i = 0; i < stepsToPredict; i++) { holdState.DoNextMove(); foreach (var factoryState in factories) { var factory = holdState.Factories[factoryState.FactoryId]; if (factory.Side == Side.MyOwn) { factoryState.Enemies[i] = null; } else { factoryState.Enemies[i] = factory.TroopsCount; } } } // fill nulls be next first value foreach (var factoryState in factories) { for (int i = stepsToPredict - 2; i >= 0; i--) { if (!factoryState.Enemies[i].HasValue) { factoryState.Enemies[i] = factoryState.Enemies[i + 1]; } } } //multiMove.AddMove(new Message($"FS #{factories.Count}: " + string.Join(", ", factories.Select(x => $"({x.FactoryId})" + string.Join(".", x.Enemies.Select(t=> t ?? -1)))))); factories.ForEach(x => { jobs.Add(new AtackFactory(x, stepsToPredict)); }); } jobs.AddRange( graph.Factories .Where(x => x.Side == Side.MyOwn && x.Income < 3) //TODO: condition can be changed, income check when bomb .Select(x => { if (x.TroopsCount >= 10 && x.TroopsCanBeUsed >= 9) { return((IJob) new UpgradeFactoryJob(x.Id)); } else { return((IJob) new WaitToUpgradeFactoryJob(x.Id)); } }) ); //Evaluate jobs var jobGraph = Graph.GetCopy(graph); while (true) { jobs.ForEach(x => { x.EvaluateInnerState(jobGraph); }); var first = jobs.OrderByDescending(x => x.GetPriorityValue()).FirstOrDefault(); if (first == null || first.GetPriorityValue() == int.MinValue) { break; } var jobMove = first.GetMove(); Logger.ErrorLog("Best job Cost: " + first.GetPriorityValue() + " ACT: " + jobMove.GetConsoleCommand(), LogReportLevel.BestJobCost); jobMove.ChangeGraph(jobGraph); multiMove.AddMove(jobMove); jobs.Remove(first); yield return(multiMove); } multiMove.AddMove(new Message("Init basic moves")); // Just move troops { // TODO: независимо для каждой фабрики выбирать foreach (var factory in graph.Factories.Where(x => x.Side == Side.MyOwn && x.TroopsCanBeUsed > 0)) { foreach (var factoryLink in factory.GetLinks()) { var checkedMove = new MoveTroops(new Troop { Dst = factoryLink.DestinationId, DstInCommand = factoryLink.FirstFactoryId, Side = Side.MyOwn, Remaining = factoryLink.Distance + 1, Src = factory.Id, Size = factory.TroopsCanBeUsed }); multiMove.AddMove(checkedMove); yield return(multiMove); multiMove.RemoveMove(checkedMove); } } // var l = graph.Factories.Where(x => x.Side == Side.MyOwn && x.TroopsCanBeUsed > 0) // .OrderByDescending(x => { // var links = x.GetLinks().Where(t => graph.Factories[t.DestinationId].Side == Side.Enemy); // if (!links.Any()) return 0; // // return links.Min(t => t.Distance); // }); } yield return(multiMove); } }