From 8ad552b0da70395a43d80901fa1091b6348c567d Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sun, 21 Jun 2020 00:44:24 +0200 Subject: [PATCH] Slightly improve ASF's algorithm for picking users to match --- ArchiSteamFarm/Statistics.cs | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/ArchiSteamFarm/Statistics.cs b/ArchiSteamFarm/Statistics.cs index 2f87e9467..462f82538 100644 --- a/ArchiSteamFarm/Statistics.cs +++ b/ArchiSteamFarm/Statistics.cs @@ -34,9 +34,8 @@ using Newtonsoft.Json; namespace ArchiSteamFarm { internal sealed class Statistics : IAsyncDisposable { - private const ushort MaxItemsForFairBots = ArchiWebHandler.MaxItemsInSingleInventoryRequest * WebBrowser.MaxTries; // Determines which fair bots we'll skip when matching due to excessive number of inventory requests they need to make, which are likely to fail in the process or cause excessive delays + private const ushort MaxItemsForFairBots = ArchiWebHandler.MaxItemsInSingleInventoryRequest * WebBrowser.MaxTries; // Determines which fair bots we'll deprioritize when matching due to excessive number of inventory requests they need to make, which are likely to fail in the process or cause excessive delays private const byte MaxMatchedBotsHard = 40; // Determines how many bots we can attempt to match in total, where match attempt is equal to analyzing bot's inventory - private const byte MaxMatchedBotsSoft = MaxMatchedBotsHard / 2; // Determines how many consecutive empty matches we need to get before we decide to skip bots from the same category private const byte MaxMatchingRounds = 10; // Determines maximum amount of matching rounds we're going to consider before leaving the rest of work for the next batch private const byte MinAnnouncementCheckTTL = 6; // Minimum amount of hours we must wait before checking eligibility for Announcement, should be lower than MinPersonaStateTTL private const byte MinHeartBeatTTL = 10; // Minimum amount of minutes we must wait before sending next HeartBeat @@ -395,17 +394,11 @@ namespace ArchiSteamFarm { return false; } - bool skipAnyBots = false; - byte emptyMatches = 0; byte totalMatches = 0; HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisRound = new HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)>(); - foreach (ListedUser listedUser in listedUsers.Where(listedUser => (listedUser.SteamID != Bot.SteamID) && (listedUser.MatchEverything || (listedUser.ItemsCount <= MaxItemsForFairBots)) && acceptedMatchableTypes.Any(listedUser.MatchableTypes.Contains) && (!triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet GivenAssetIDs, ISet ReceivedAssetIDs) attempt) || (attempt.Tries < byte.MaxValue)) && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderBy(listedUser => triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet GivenAssetIDs, ISet ReceivedAssetIDs) attempt) ? attempt.Tries : 0).ThenByDescending(listedUser => listedUser.MatchEverything).ThenByDescending(listedUser => listedUser.Score)) { - if (listedUser.MatchEverything && skipAnyBots) { - continue; - } - + foreach (ListedUser listedUser in listedUsers.Where(listedUser => (listedUser.SteamID != Bot.SteamID) && acceptedMatchableTypes.Any(listedUser.MatchableTypes.Contains) && (!triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet GivenAssetIDs, ISet ReceivedAssetIDs) attempt) || (attempt.Tries < byte.MaxValue)) && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderBy(listedUser => triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet GivenAssetIDs, ISet ReceivedAssetIDs) attempt) ? attempt.Tries : 0).ThenByDescending(listedUser => listedUser.MatchEverything).ThenByDescending(listedUser => listedUser.MatchEverything || (listedUser.ItemsCount < MaxItemsForFairBots)).ThenByDescending(listedUser => listedUser.Score)) { HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> wantedSets = ourTradableState.Keys.Where(set => !skippedSetsThisRound.Contains(set) && listedUser.MatchableTypes.Contains(set.Type)).ToHashSet(); if (wantedSets.Count == 0) { @@ -627,8 +620,6 @@ namespace ArchiSteamFarm { triedSteamIDs[listedUser.SteamID] = (++previousAttempt.Tries, previousAttempt.GivenAssetIDs, previousAttempt.ReceivedAssetIDs); - emptyMatches = 0; - Bot.ArchiLogger.LogGenericTrace(Bot.SteamID + " <- " + string.Join(", ", itemsToReceive.Select(item => item.RealAppID + "/" + item.Type + "-" + item.ClassID + " #" + item.Amount)) + " | " + string.Join(", ", itemsToGive.Select(item => item.RealAppID + "/" + item.Type + "-" + item.ClassID + " #" + item.Amount)) + " -> " + listedUser.SteamID); (bool success, HashSet mobileTradeOfferIDs) = await Bot.ArchiWebHandler.SendTradeOffer(listedUser.SteamID, itemsToGive, itemsToReceive, listedUser.TradeToken, true).ConfigureAwait(false); @@ -662,15 +653,6 @@ namespace ArchiSteamFarm { triedSteamIDs[listedUser.SteamID] = (byte.MaxValue, null, null); } - if (++emptyMatches >= MaxMatchedBotsSoft) { - if (skipAnyBots) { - break; - } - - skipAnyBots = true; - emptyMatches = 0; - } - continue; } @@ -692,7 +674,8 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ActivelyMatchingItemsRound, skippedSetsThisRound.Count)); - return skippedSetsThisRound.Count > 0; + // Keep matching when we either traded something this round (so it makes sense for a refresh) or if we didn't try all available bots yet (so it makes sense to keep going) + return (skippedSetsThisRound.Count > 0) || triedSteamIDs.Values.All(data => data.Tries < 2); } [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]