From ac276df80077bd11efa571078150b5a1eb825e43 Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Fri, 2 Jan 2026 15:35:11 +0100 Subject: [PATCH 1/4] new expedition repartition calcul --- TBot.Ogame.Infrastructure/Models/Ships.cs | 54 ++- TBot/Workers/ExpeditionsWorker.cs | 522 +++++++++++++++------- 2 files changed, 403 insertions(+), 173 deletions(-) diff --git a/TBot.Ogame.Infrastructure/Models/Ships.cs b/TBot.Ogame.Infrastructure/Models/Ships.cs index b2c5f968..a36f4d27 100644 --- a/TBot.Ogame.Infrastructure/Models/Ships.cs +++ b/TBot.Ogame.Infrastructure/Models/Ships.cs @@ -147,11 +147,9 @@ public Ships Add(Buildables buildable, long quantity) { public Ships Remove(Buildables buildable, int quantity) { foreach (PropertyInfo prop in this.GetType().GetProperties()) { if (prop.Name == buildable.ToString()) { - long val = (long) prop.GetValue(this); - if (val >= quantity) - prop.SetValue(this, val); - else - prop.SetValue(this, 0); + long val = (long)prop.GetValue(this); + long newVal = val - quantity; + prop.SetValue(this, newVal >= 0 ? newVal : 0); } } return this; @@ -177,9 +175,8 @@ public void SetAmount(Buildables buildable, long number) { public bool HasAtLeast(Ships ships, long times = 1) { foreach (PropertyInfo prop in this.GetType().GetProperties()) { - if ((long) prop.GetValue(this) * times < (long) prop.GetValue(ships)) { + if ((long) prop.GetValue(this) < (long) prop.GetValue(ships) *times) return false; - } } return true; } @@ -193,6 +190,45 @@ public override string ToString() { } return output; } - } -} + public Ships Merge(Ships other) { + Ships result = this; + foreach (PropertyInfo prop in other.GetType().GetProperties()) { + result.Add((Buildables)Enum.Parse(typeof(Buildables), prop.Name), (long)prop.GetValue(other)); + } + return result; + } + + public Ships unMerge(Ships other) { + Ships result = this; + foreach (PropertyInfo prop in other.GetType().GetProperties()) { + result.Remove((Buildables) Enum.Parse(typeof(Buildables), prop.Name), (int)(long) prop.GetValue(other)); + } + return result; + } + + public Ships Multiply(int quantity) { + Ships result = this; + foreach (PropertyInfo prop in this.GetType().GetProperties()) { + prop.SetValue(result, (long) Math.Ceiling((double)(long) prop.GetValue(result) *quantity)); + } + return result; + } + + public Ships Divide(int quantity) { + Ships result = this; + foreach (PropertyInfo prop in this.GetType().GetProperties()) { + prop.SetValue(result, (long) Math.Ceiling((double)(long) prop.GetValue(result) /quantity)); + } + return result; + } + + public Ships Clone() { + Ships clone = new Ships(); + foreach (PropertyInfo prop in this.GetType().GetProperties()) { + prop.SetValue(clone, prop.GetValue(this)); + } + return clone; + } + } +} \ No newline at end of file diff --git a/TBot/Workers/ExpeditionsWorker.cs b/TBot/Workers/ExpeditionsWorker.cs index bcfa777f..676000fb 100644 --- a/TBot/Workers/ExpeditionsWorker.cs +++ b/TBot/Workers/ExpeditionsWorker.cs @@ -4,6 +4,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Diagnostics; using Microsoft.Extensions.Logging; using Tbot.Common.Settings; using Tbot.Helpers; @@ -50,7 +51,6 @@ public override LogSender GetLogSender() { return LogSender.Expeditions; } - protected override async Task Execute() { bool stop = false; bool delay = false; @@ -157,7 +157,7 @@ protected override async Task Execute() { _tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdatePlanets(UpdateTypes.LFBonuses); origins.Add(_tbotInstance.UserData.celestials .OrderBy(planet => planet.Coordinate.Type == Celestials.Moon) - .ThenByDescending(planet => _calculationService.CalcFleetCapacity(planet.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, null, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo)) + .ThenByDescending(planet => _calculationService.CalcFleetCapacity(planet.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, planet.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo)) .First() ); } @@ -166,191 +166,303 @@ protected override async Task Execute() { _tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdatePlanets(UpdateTypes.LFBonuses); origins.Add(_tbotInstance.UserData.celestials .OrderBy(planet => planet.Coordinate.Type == Celestials.Moon) - .ThenByDescending(planet => _calculationService.CalcFleetCapacity(planet.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, null, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo)) + .ThenByDescending(planet => _calculationService.CalcFleetCapacity(planet.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, planet.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo)) .First() ); } - if ((bool) _tbotInstance.InstanceSettings.Expeditions.RandomizeOrder) { - origins = origins.Shuffle().ToList(); + + Ships fleetForOneExpedition = GetShipsForOneExpedition(origins.First().LFBonuses); + DoLog(LogLevel.Information, $"The optimal composition of one expedition: {fleetForOneExpedition.ToString()}"); + + if (fleetForOneExpedition.IsEmpty()) { + DoLog(LogLevel.Warning, "Unable to send expeditions: no ships available to form a proper expedition fleet"); + stop = true; + return; + } + + Ships shipsToKeep = new(); + Dictionary> expToSendFromEachOrigin = new(); + Buildables primaryShip = Buildables.LargeCargo; + if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.PrimaryShip.ToString(), true, out primaryShip)) { + primaryShip = Buildables.LargeCargo; + } + if ((long) _tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep > 0) { + shipsToKeep.SetAmount(primaryShip, (long) _tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep); + } + Buildables secondaryShip = Buildables.Null; + if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.SecondaryShip, true, out secondaryShip)) { + secondaryShip = Buildables.Null; + } + // vérifier les ToKeep !! + if (!((bool) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Active)) + origins.RemoveAll(celestial => celestial.Ships.GetAmount(primaryShip) <= shipsToKeep.GetAmount(primaryShip)); + else + origins.RemoveAll(celestial => !celestial.Ships.HasAtLeast(fleetForOneExpedition, 1)); + origins.RemoveAll(celestial => celestial.Ships.IsEmpty()); + + DoLog(LogLevel.Information, $"Only {origins.Count()} origin(s) have enough ships to send expeditions"); + + if (origins.Count() == 0) { + DoLog(LogLevel.Warning, "Unable to send expeditions: no ships available"); + delay = true; + return; } - LFBonuses lfBonuses = origins.First().LFBonuses; - Dictionary originExps = new(); - int quot = (int) Math.Floor((float) expsToSend / (float) origins.Count()); foreach (var origin in origins) { - originExps.Add(origin, quot); + int tempExpsToSend = 0; + Celestial celestial = origin; + if (!((bool) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Active)) { + celestial.Ships = celestial.Ships.unMerge(shipsToKeep); + } + + for (int i = 0; i < expsToSend; i++) { + if (celestial.Ships.HasAtLeast(fleetForOneExpedition, i + 1)) + tempExpsToSend++; + } + + if (tempExpsToSend == 0) { + Ships availableShips = fleetForOneExpedition.Clone().unMerge(fleetForOneExpedition.Clone().unMerge(celestial.Ships)); + expToSendFromEachOrigin.Add(celestial, new Dictionary { { availableShips, tempExpsToSend } }); + DoLog(LogLevel.Warning, $"Not enough ships from {celestial.ToString()} to send a proper expeditions: missing {fleetForOneExpedition.Clone().unMerge(availableShips).ToString()}"); + } else { + expToSendFromEachOrigin.Add(celestial, new Dictionary { { fleetForOneExpedition.Clone(), tempExpsToSend } }); + } } - int rest = (int) Math.Floor((float) expsToSend % (float) origins.Count()); - for (int i = 0; i < rest; i++) { - originExps[origins[i]]++; + + DoLog(LogLevel.Information, $"Number of expeditions to send from each origin: "); + foreach (var kvp in expToSendFromEachOrigin) { + DoLog(LogLevel.Information, $"{kvp.Key.ToString()}: {kvp.Value.Values.First().ToString()} expeditions with {kvp.Value.Keys.First().ToString()}"); } - int delayExpedition = 0; - foreach (var origin in originExps.Keys) { - int expsToSendFromThisOrigin = originExps[origin]; - if (expsToSendFromThisOrigin == 0) { - if (delayExpedition > 0) - delayExpedition--; - else + + Dictionary> cleanExpToSendFromEachOrigin = new(); + + cleanExpToSendFromEachOrigin = expToSendFromEachOrigin + .Where(d => d.Value.Values.First() > 0) + .OrderByDescending(d => d.Value.Values.First()) + .ToDictionary(d => d.Key, d => d.Value); + + if (cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.First()) < expsToSend && !((bool) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Active)) { + int properMission = cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.Sum()); + DoLog(LogLevel.Warning, $"Not enough ships available to send {expsToSend.ToString()} proper expeditions (only {properMission.ToString()} proper possible)"); + + List expToSends = expToSendFromEachOrigin.Keys.ToList(); + foreach (Celestial expToSend in expToSends) { + if (expToSendFromEachOrigin[expToSend].Values.First() <= 0) continue; + Ships ships = expToSendFromEachOrigin[expToSend].Keys.First(); + int value = expToSendFromEachOrigin[expToSend].Values.First(); + expToSendFromEachOrigin.Remove(expToSend); + ships = fleetForOneExpedition.Clone().unMerge(fleetForOneExpedition.Clone().unMerge(expToSend.Ships.Clone().unMerge(fleetForOneExpedition.Clone().Multiply(value)))); + expToSendFromEachOrigin.Add(expToSend, new Dictionary { { ships, 0 } }); } - else if (origin.Ships.IsEmpty()) { - DoLog(LogLevel.Warning, "Unable to send expeditions: no ships available"); - delayExpedition++; - continue; + + expToSends = expToSendFromEachOrigin + .Where(d => !d.Key.Ships.IsEmpty()) + .Where(d => d.Value.Keys.First().GetFleetPoints() < fleetForOneExpedition.GetFleetPoints()) + .OrderByDescending(d => d.Value.Keys.First().GetFleetPoints()) + .ToDictionary(d => d.Key, d => d.Value) + .Keys.ToList(); + + if (expToSendFromEachOrigin.Where(d => !d.Key.Ships.IsEmpty()).Where(d => d.Value.Keys.First().GetFleetPoints() < fleetForOneExpedition.GetFleetPoints()).ToDictionary(d => d.Key, d => d.Value).Count() == 1 || expsToSend - properMission == 1) { + if (cleanExpToSendFromEachOrigin.ContainsKey(expToSends.First())) + cleanExpToSendFromEachOrigin[expToSends.First()].Add(expToSendFromEachOrigin[expToSends.First()].Keys.First().Divide(expsToSend - properMission), expsToSend - properMission); + else + cleanExpToSendFromEachOrigin.Add(expToSends.First(), new Dictionary { { expToSendFromEachOrigin[expToSends.First()].Keys.First().Divide(expsToSend - properMission), expsToSend - properMission } }); } else { - Ships fleet; - if ((bool) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Active) { - fleet = new( - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LightFighter, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.HeavyFighter, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Cruiser, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battleship, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battlecruiser, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Bomber, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Destroyer, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Deathstar, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.SmallCargo, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LargeCargo, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.ColonyShip, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Recycler, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.EspionageProbe, - 0, - 0, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Reaper, - (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Pathfinder - ); - if (!origin.Ships.HasAtLeast(fleet, expsToSendFromThisOrigin)) { - DoLog(LogLevel.Warning, $"Unable to send expeditions: not enough ships in origin {origin.ToString()}"); - delayExpedition++; - continue; - } - } else { - Buildables primaryShip = Buildables.LargeCargo; - if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.PrimaryShip.ToString(), true, out primaryShip)) { - DoLog(LogLevel.Warning, "Unable to parse PrimaryShip. Falling back to default LargeCargo"); - primaryShip = Buildables.LargeCargo; - } - if (primaryShip == Buildables.Null) { - DoLog(LogLevel.Warning, "Unable to send expeditions: primary ship is Null"); - delayExpedition++; - continue; - } - var availableShips = origin.Ships.GetMovableShips(); - if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "PrimaryToKeep") && (int) _tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep > 0) { - availableShips.SetAmount(primaryShip, Math.Max(0, availableShips.GetAmount(primaryShip) - (long) _tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep)); - } - DoLog(LogLevel.Warning, $"Available {primaryShip.ToString()} in origin {origin.ToString()}: {availableShips.GetAmount(primaryShip)} ({_tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep} must be kept at dock)"); - fleet = _calculationService.CalcFullExpeditionShips(availableShips, primaryShip, expsToSendFromThisOrigin, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches, lfBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo); - if (fleet.GetAmount(primaryShip) < (long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend || availableShips.GetAmount(primaryShip) < 1) { - fleet.SetAmount(primaryShip, (long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend); - if (availableShips.GetAmount(primaryShip) < 1) { - DoLog(LogLevel.Warning, $"Unable to send expeditions: no ships available in origin {origin.ToString()}"); - delayExpedition++; - continue; - } - if (!availableShips.HasAtLeast(fleet, expsToSendFromThisOrigin)) { - DoLog(LogLevel.Warning, $"Unable to send expeditions: available {primaryShip.ToString()} in origin {origin.ToString()} under set min number of {(long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend}"); - delayExpedition++; - continue; - } + expToSendFromEachOrigin = expToSendFromEachOrigin + .Where(d => !d.Key.Ships.IsEmpty()) + .Where(d => d.Value.Keys.First().GetFleetPoints() < fleetForOneExpedition.GetFleetPoints()) + .OrderBy(d => d.Value.Keys.First().GetFleetPoints()) + .ToDictionary(d => d.Key, d => d.Value); + int tempExpValue = expsToSend - properMission; + long tempIntValue = 0; + int rate = 0; + + for (int i = 1; tempIntValue < tempExpValue; i++) { + rate++; + long tempShips = expToSendFromEachOrigin.First().Value.Keys.First().GetFleetPoints() /i; + foreach (var exp in expToSendFromEachOrigin) { + if (exp.Key.ID == expToSendFromEachOrigin.First().Key.ID) + tempIntValue = i; + tempIntValue += (long) Math.Floor((double) (exp.Value.Keys.First().GetFleetPoints() / (expToSendFromEachOrigin.First().Value.Keys.First().GetFleetPoints() /i))); } - Buildables secondaryShip = Buildables.Null; - if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.SecondaryShip, true, out secondaryShip)) { - DoLog(LogLevel.Warning, "Unable to parse SecondaryShip. Falling back to default Null"); - secondaryShip = Buildables.Null; + } + + if (tempIntValue == tempExpValue) { + foreach (var exp in expToSendFromEachOrigin) { + int value = (int) Math.Floor((double) (exp.Value.Keys.First().GetFleetPoints() / (expToSendFromEachOrigin.First().Value.Keys.First().GetFleetPoints() /rate))); + Ships ships = exp.Value.Keys.First().Divide(value); + if (cleanExpToSendFromEachOrigin.ContainsKey(exp.Key)) + cleanExpToSendFromEachOrigin[exp.Key].Add(ships, value); + else + cleanExpToSendFromEachOrigin.Add(exp.Key, new Dictionary { { ships, value } }); } - if (secondaryShip != Buildables.Null) { - long secondaryToSend = Math.Min( - (long) Math.Round( - availableShips.GetAmount(secondaryShip) / (float) expsToSendFromThisOrigin, - 0, - MidpointRounding.ToZero - ), - (long) Math.Round( - fleet.GetAmount(primaryShip) * (float) _tbotInstance.InstanceSettings.Expeditions.SecondaryToPrimaryRatio, - 0, - MidpointRounding.ToZero - ) - ); - if (secondaryToSend < (long) _tbotInstance.InstanceSettings.Expeditions.MinSecondaryToSend) { - DoLog(LogLevel.Warning, $"Unable to send expeditions: available {secondaryShip.ToString()} in origin {origin.ToString()} under set number of {(long) _tbotInstance.InstanceSettings.Expeditions.MinSecondaryToSend}"); - delayExpedition++; - continue; - } else { - fleet.Add(secondaryShip, secondaryToSend); - if (!availableShips.HasAtLeast(fleet, expsToSendFromThisOrigin)) { - DoLog(LogLevel.Warning, $"Unable to send expeditions: not enough ships in origin {origin.ToString()}"); - delayExpedition++; - continue; - } - } + } else if (tempIntValue > tempExpValue) { + tempIntValue = 0; + rate = rate > 1 ? rate-- : rate; + foreach (var exp in expToSendFromEachOrigin) { + int value = 0; + if (exp.Key.ID == expToSendFromEachOrigin.Last().Key.ID) + value = tempExpValue - (int) tempIntValue; + else + value = (int) Math.Floor((double) (exp.Value.Keys.First().GetFleetPoints() / (expToSendFromEachOrigin.First().Value.Keys.First().GetFleetPoints() /rate))); + Ships ships = exp.Value.Keys.First().Divide(value); + tempIntValue += value; + if (cleanExpToSendFromEachOrigin.ContainsKey(exp.Key)) + cleanExpToSendFromEachOrigin[exp.Key].Add(ships, value); + else + cleanExpToSendFromEachOrigin.Add(exp.Key, new Dictionary { { ships, value } }); } } + } + DoLog(LogLevel.Information, $"{properMission.ToString()} expeditions will be properly sent, {(cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.Sum()) - properMission).ToString()} as close as possible to proper configuration"); + } else { - DoLog(LogLevel.Information, $"{expsToSendFromThisOrigin.ToString()} expeditions with {fleet.ToString()} will be sent from {origin.ToString()}"); - List syslist = new(); - for (int i = 0; i < expsToSendFromThisOrigin; i++) { - Coordinate destination; - if ((bool) _tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Active) { - var rand = new Random(); - - int range = (int) _tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Range; - while (expsToSendFromThisOrigin > range * 2) - range += 1; - - destination = new Coordinate { - Galaxy = origin.Coordinate.Galaxy, - System = rand.Next(origin.Coordinate.System - range, origin.Coordinate.System + range + 1), - Position = 16, - Type = Celestials.DeepSpace - }; - destination.System = GeneralHelper.WrapSystem(destination.System); - while (syslist.Contains(destination.System)) - destination.System = rand.Next(origin.Coordinate.System - range, origin.Coordinate.System + range + 1); - syslist.Add(destination.System); + if ((bool) _tbotInstance.InstanceSettings.Expeditions.RandomizeOrder) + cleanExpToSendFromEachOrigin = cleanExpToSendFromEachOrigin.Shuffle().ToDictionary(); + + if (cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.First()) > expsToSend) { + + expToSendFromEachOrigin = cleanExpToSendFromEachOrigin.OrderBy(d => d.Value.Values.First()).ToDictionary(d => d.Key, d => d.Value); + int maxExpPossible = cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.First()); + int quot = (int) Math.Floor((float) expsToSend / (float) cleanExpToSendFromEachOrigin.Count()); + int rest = (int) Math.Floor((float) expsToSend % (float) cleanExpToSendFromEachOrigin.Count()); + int toDelay = 0; + int result = 0; + cleanExpToSendFromEachOrigin = new(); + + for (int i = 0; i < expToSendFromEachOrigin.Count(); i++) { + var wave = expToSendFromEachOrigin.ElementAt(i); + quot += (toDelay > 0 ? 1 : 0); + if (wave.Value.Values.First() < quot) { + result = wave.Value.Values.First(); + if ((int) Math.Floor((float) (quot - wave.Value.Values.First()) / (float) (expToSendFromEachOrigin.Count() - (i + 1))) > 0) + quot += (int) Math.Floor((float) (quot - wave.Value.Values.First()) / (float) (expToSendFromEachOrigin.Count() - (i + 1))); + else + toDelay = quot - wave.Value.Values.First(); } else { - destination = new Coordinate { - Galaxy = origin.Coordinate.Galaxy, - System = origin.Coordinate.System, - Position = 16, - Type = Celestials.DeepSpace - }; + result = quot; } - _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); - Resources payload = new(); - if ((long) _tbotInstance.InstanceSettings.Expeditions.FuelToCarry > 0) { - payload.Deuterium = (long) _tbotInstance.InstanceSettings.Expeditions.FuelToCarry; + if (cleanExpToSendFromEachOrigin.ContainsKey(wave.Key)) + cleanExpToSendFromEachOrigin.Remove(wave.Key); + cleanExpToSendFromEachOrigin.Add(wave.Key, new Dictionary { { wave.Value.Keys.First(), result } }); + } + + List tempCelestial = cleanExpToSendFromEachOrigin.Keys.ToList(); + while (cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.First()) < expsToSend) { + foreach (var tempCel in tempCelestial) { + if (cleanExpToSendFromEachOrigin.Values.Sum(d => d.Values.First()) < expsToSend) { + Ships ships = cleanExpToSendFromEachOrigin[tempCel].Keys.First(); + int value = cleanExpToSendFromEachOrigin[tempCel].Values.First(); + if (value < expToSendFromEachOrigin[tempCel].Values.First()) + value++; + cleanExpToSendFromEachOrigin.Remove(tempCel); + cleanExpToSendFromEachOrigin.Add(tempCel, new Dictionary { { ships, value } }); + } } - if (_tbotInstance.UserData.slots.ExpFree > 0) { - var fleetId = await _fleetScheduler.SendFleet(origin, fleet, destination, Missions.Expedition, Speeds.HundredPercent, payload); + } + } + } - if (fleetId == (int) SendFleetCode.AfterSleepTime) { - stop = true; - return; + /* + DoLog(LogLevel.Debug, $"--------------------------------------"); + foreach (var originss in cleanExpToSendFromEachOrigin) { + DoLog(LogLevel.Debug, $" ---->>>> {originss.Key.ToString()}"); + var origin = originss.Value; + DoLog(LogLevel.Debug, $"Proper: {origin.Values.First().ToString()} x"); + foreach (var (key, value) in origin) { + DoLog(LogLevel.Debug, $" -- {value.ToString()} x {key.ToString()}"); + } + } + DoLog(LogLevel.Debug, $"--------------------------------------"); + */ + + Dictionary> originExps = new(); + if ((bool) _tbotInstance.InstanceSettings.Expeditions.RandomizeOrder) + cleanExpToSendFromEachOrigin = cleanExpToSendFromEachOrigin.Shuffle().ToDictionary(); + else + cleanExpToSendFromEachOrigin = cleanExpToSendFromEachOrigin.OrderBy(planet => planet.Key.Coordinate.Type == Celestials.Moon).ToDictionary(); + + for (int o = 0; o < cleanExpToSendFromEachOrigin.Count(); o++) { + var origin = cleanExpToSendFromEachOrigin.ElementAt(o); + if (origin.Key.Ships.IsEmpty()) { + DoLog(LogLevel.Information, $"Unable to send expeditions from {origin.Key.ToString()}: no ships available"); + continue; + } + foreach (var wave in origin.Value) { + int expsToSendFromThisOrigin = wave.Value; + if (expsToSendFromThisOrigin == 0) { + continue; + } else { + Ships fleet = wave.Key; + DoLog(LogLevel.Information, $"{expsToSendFromThisOrigin.ToString()} expeditions with {fleet.ToString()} will be sent from {origin.Key.ToString()}"); + List syslist = new(); + for (int i = 0; i < expsToSendFromThisOrigin; i++) { + Coordinate destination; + if ((bool) _tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Active) { + var rand = new Random(); + + int range = (int) _tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Range; + while (expsToSendFromThisOrigin > range * 2) + range += 1; + + destination = new Coordinate { + Galaxy = origin.Key.Coordinate.Galaxy, + System = rand.Next(origin.Key.Coordinate.System - range, origin.Key.Coordinate.System + range + 1), + Position = 16, + Type = Celestials.DeepSpace + }; + destination.System = GeneralHelper.WrapSystem(destination.System); + while (syslist.Contains(destination.System)) + destination.System = rand.Next(origin.Key.Coordinate.System - range, origin.Key.Coordinate.System + range + 1); + syslist.Add(destination.System); + } else { + destination = new Coordinate { + Galaxy = origin.Key.Coordinate.Galaxy, + System = origin.Key.Coordinate.System, + Position = 16, + Type = Celestials.DeepSpace + }; } - if (fleetId == (int) SendFleetCode.NotEnoughSlots) { - delay = true; - return; + _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); + Resources payload = new(); + if ((long) _tbotInstance.InstanceSettings.Expeditions.FuelToCarry > 0) { + payload.Deuterium = (long) _tbotInstance.InstanceSettings.Expeditions.FuelToCarry; } - - var minWaitNextFleet = (int) _tbotInstance.InstanceSettings.Expeditions.MinWaitNextFleet; - var maxWaitNextFleet = (int) _tbotInstance.InstanceSettings.Expeditions.MaxWaitNextFleet; + if (_tbotInstance.UserData.slots.ExpFree > 0) { + var fleetId = await _fleetScheduler.SendFleet(origin.Key, fleet, destination, Missions.Expedition, Speeds.HundredPercent, payload); + + if (fleetId == (int) SendFleetCode.AfterSleepTime) { + stop = true; + return; + } + if (fleetId == (int) SendFleetCode.NotEnoughSlots) { + delay = true; + return; + } - if (minWaitNextFleet < 0) - minWaitNextFleet = 0; - if (maxWaitNextFleet < 1) - maxWaitNextFleet = 1; + DoLog(LogLevel.Information, $"Sending expedition fleet from {origin.Key.ToString()} to {destination.ToString()} with fleet {fleet.ToString()}"); - var rndWaitTimeMs = (int) RandomizeHelper.CalcRandomIntervalSecToMs(minWaitNextFleet, maxWaitNextFleet); - DoLog(LogLevel.Information, $"Wait {((float) rndWaitTimeMs / 1000).ToString("0.00")}s for next Expedition"); - await Task.Delay(rndWaitTimeMs, _ct); + var minWaitNextFleet = (int) _tbotInstance.InstanceSettings.Expeditions.MinWaitNextFleet; + var maxWaitNextFleet = (int) _tbotInstance.InstanceSettings.Expeditions.MaxWaitNextFleet; - } else { - DoLog(LogLevel.Information, "Unable to send expeditions: no expedition slots available."); - delay = true; - return; + if (minWaitNextFleet < 0) + minWaitNextFleet = 0; + if (maxWaitNextFleet < 1) + maxWaitNextFleet = 1; + + var rndWaitTimeMs = (int) RandomizeHelper.CalcRandomIntervalSecToMs(minWaitNextFleet, maxWaitNextFleet); + + DoLog(LogLevel.Information, $"Wait {((float) rndWaitTimeMs / 1000).ToString("0.00")}s for next Expedition"); + await Task.Delay(rndWaitTimeMs, _ct); + + } else { + DoLog(LogLevel.Information, "Unable to send expeditions: no expedition slots available."); + delay = true; + return; + } } } } @@ -373,20 +485,22 @@ protected override async Task Execute() { .ToList(); } else { orderedFleets = orderedFleets - .OrderBy(fleet => fleet.BackIn) + .OrderBy(fleet => fleet.BackIn) .ToList(); } _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots(); if ((orderedFleets.Count() == 0) || (_tbotInstance.UserData.slots.ExpFree > 0 && (!((bool) _tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) && !((bool) _tbotInstance.InstanceSettings.Expeditions.WaitForMajorityOfExpeditions)))) { - interval = RandomizeHelper.CalcRandomInterval(IntervalType.AboutFiveMinutes); + interval = RandomizeHelper.CalcRandomInterval(IntervalType.AboutFiveMinutes); } else { var minWaitNextRound = (int) _tbotInstance.InstanceSettings.Expeditions.MinWaitNextRound; var maxWaitNextRound = (int) _tbotInstance.InstanceSettings.Expeditions.MaxWaitNextRound; - if (minWaitNextRound < 0) minWaitNextRound = 0; - if (maxWaitNextRound < 1) maxWaitNextRound = 1; + if (minWaitNextRound < 0) + minWaitNextRound = 0; + if (maxWaitNextRound < 1) + maxWaitNextRound = 1; interval = (int) ((1000 * orderedFleets.First().BackIn) + RandomizeHelper.CalcRandomIntervalSecToMs(minWaitNextRound, maxWaitNextRound)); @@ -431,5 +545,85 @@ protected override async Task Execute() { } } } + + private Ships GetShipsForOneExpedition(LFBonuses lfBonuses) { + Ships fleet = new( + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + (long) long.MaxValue, + 0, + 0, + (long) long.MaxValue, + (long) long.MaxValue + ); + if ((bool) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Active) { + return new( + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LightFighter, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.HeavyFighter, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Cruiser, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battleship, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battlecruiser, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Bomber, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Destroyer, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Deathstar, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.SmallCargo, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LargeCargo, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.ColonyShip, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Recycler, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.EspionageProbe, + 0, + 0, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Reaper, + (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Pathfinder + ); + } else { + Buildables primaryShip = Buildables.LargeCargo; + if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.PrimaryShip.ToString(), true, out primaryShip)) { + DoLog(LogLevel.Warning, "Unable to parse PrimaryShip. Falling back to default LargeCargo"); + primaryShip = Buildables.LargeCargo; + } + if (primaryShip == Buildables.Null) { + DoLog(LogLevel.Warning, "Unable to send expeditions: primary ship is Null"); + return new(); + } + + fleet = _calculationService.CalcFullExpeditionShips(fleet.GetMovableShips(), primaryShip, 1, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches, lfBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo); + + if (fleet.GetAmount(primaryShip) < (long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend || fleet.GetAmount(primaryShip) < 1) { + fleet.SetAmount(primaryShip, (long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend); + } + + Buildables secondaryShip = Buildables.Null; + if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.SecondaryShip, true, out secondaryShip)) { + DoLog(LogLevel.Warning, "Unable to parse SecondaryShip. Falling back to default Null"); + secondaryShip = Buildables.Null; + } + if (secondaryShip != Buildables.Null) { + long secondaryToSend = Math.Min( + fleet.GetAmount(secondaryShip), + (long) Math.Round( + fleet.GetAmount(primaryShip) * (float) _tbotInstance.InstanceSettings.Expeditions.SecondaryToPrimaryRatio, + 0, + MidpointRounding.ToZero + ) + ); + if (secondaryToSend < (long) _tbotInstance.InstanceSettings.Expeditions.MinSecondaryToSend) { + secondaryToSend = (long) _tbotInstance.InstanceSettings.Expeditions.MinSecondaryToSend; + } + fleet.Add(secondaryShip, secondaryToSend); + } + } + return fleet; + } } } From 9ce6739202bbf8d2a16bce7370ec539a7dbdfcf7 Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Fri, 2 Jan 2026 17:10:53 +0100 Subject: [PATCH 2/4] Update workflow --- .github/workflows/snapshot.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index afc88037..b38f565b 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -43,7 +43,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-win64.zip publish/win64/ - name: Upload win64 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-win64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-win64.zip @@ -61,7 +61,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-win32.zip publish/win32/ - name: Upload win32 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-win32 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-win32.zip @@ -81,7 +81,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linux64.zip publish/linux64/ - name: Upload linux64 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-linux64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linux64.zip @@ -101,7 +101,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linuxarm.zip publish/linuxarm/ - name: Upload linuxarm - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-linuxarm path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linuxarm.zip @@ -121,7 +121,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linuxarm64.zip publish/linuxarm64/ - name: Upload linuxarm64 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-linuxarm64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linuxarm64.zip @@ -141,7 +141,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-osx64.zip publish/osx64/ - name: Upload osx64 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-osx64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-osx64.zip @@ -161,7 +161,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-osx-arm64.zip publish/osx-arm64/ - name: Upload osx-arm64 - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: TBot-${{ env.SNAP_VERSION }}-osx-arm64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-osx-arm64.zip From 8bbaabafb6953dadce7f68d3db24335f7b74425f Mon Sep 17 00:00:00 2001 From: Cheeeh <53708593+Cheeeh@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:39:55 +0100 Subject: [PATCH 3/4] Downgrade actions/upload-artifact to v2 useless --- .github/workflows/snapshot.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index b38f565b..afc88037 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -43,7 +43,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-win64.zip publish/win64/ - name: Upload win64 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-win64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-win64.zip @@ -61,7 +61,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-win32.zip publish/win32/ - name: Upload win32 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-win32 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-win32.zip @@ -81,7 +81,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linux64.zip publish/linux64/ - name: Upload linux64 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-linux64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linux64.zip @@ -101,7 +101,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linuxarm.zip publish/linuxarm/ - name: Upload linuxarm - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-linuxarm path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linuxarm.zip @@ -121,7 +121,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linuxarm64.zip publish/linuxarm64/ - name: Upload linuxarm64 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-linuxarm64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linuxarm64.zip @@ -141,7 +141,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-osx64.zip publish/osx64/ - name: Upload osx64 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-osx64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-osx64.zip @@ -161,7 +161,7 @@ jobs: with: args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-osx-arm64.zip publish/osx-arm64/ - name: Upload osx-arm64 - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v2 with: name: TBot-${{ env.SNAP_VERSION }}-osx-arm64 path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-osx-arm64.zip From 9177c4a023daeeb03044502cfe7f4a6feb12def2 Mon Sep 17 00:00:00 2001 From: Cheeeh Date: Thu, 15 Jan 2026 15:48:11 +0100 Subject: [PATCH 4/4] Fix Over-increment of a variable --- TBot/Workers/ExpeditionsWorker.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/TBot/Workers/ExpeditionsWorker.cs b/TBot/Workers/ExpeditionsWorker.cs index 676000fb..2c173b5a 100644 --- a/TBot/Workers/ExpeditionsWorker.cs +++ b/TBot/Workers/ExpeditionsWorker.cs @@ -334,15 +334,16 @@ protected override async Task Execute() { for (int i = 0; i < expToSendFromEachOrigin.Count(); i++) { var wave = expToSendFromEachOrigin.ElementAt(i); - quot += (toDelay > 0 ? 1 : 0); - if (wave.Value.Values.First() < quot) { + int tempQuot = quot + (toDelay > 0 ? 1 : 0); + toDelay = 0; + if (wave.Value.Values.First() < tempQuot) { result = wave.Value.Values.First(); - if ((int) Math.Floor((float) (quot - wave.Value.Values.First()) / (float) (expToSendFromEachOrigin.Count() - (i + 1))) > 0) - quot += (int) Math.Floor((float) (quot - wave.Value.Values.First()) / (float) (expToSendFromEachOrigin.Count() - (i + 1))); + if ((int) Math.Floor((float) (tempQuot - wave.Value.Values.First()) / (float) (expToSendFromEachOrigin.Count() - (i + 1))) > 0) + tempQuot += (int) Math.Floor((float) (tempQuot - wave.Value.Values.First()) / (float) (expToSendFromEachOrigin.Count() - (i + 1))); else - toDelay = quot - wave.Value.Values.First(); + toDelay = tempQuot - wave.Value.Values.First(); } else { - result = quot; + result = tempQuot; } if (cleanExpToSendFromEachOrigin.ContainsKey(wave.Key)) cleanExpToSendFromEachOrigin.Remove(wave.Key);