import 'dart:async'; import 'package:rimworld_modman/logger.dart'; import 'package:rimworld_modman/mod.dart'; import 'package:rimworld_modman/mod_list.dart'; /// A class that helps find the minimum set of mods that exhibit a bug. /// /// Provides two main algorithms: /// - Binary search / bisect: Divides mods into smaller subsets to find problematic ones quickly. /// - Linear search / batching: Tests mods in small groups to systematically identify issues. /// /// These approaches help RimWorld mod users identify which mods are causing problems /// when many mods are installed. class ModListTroubleshooter { final ModList originalModList; ModList currentModList; int _startIndex = 0; int _endIndex = 0; ModListTroubleshooter(ModList modList) : originalModList = modList, currentModList = modList.copyWith(), _startIndex = 0, _endIndex = modList.activeMods.length; ModList binaryForward() { final midIndex = (_startIndex + _endIndex) ~/ 2; final subset = originalModList.activeMods.keys.toList().sublist( midIndex, _endIndex, ); currentModList.disableAll(); currentModList.enableMods(subset); _startIndex = midIndex; return currentModList; } ModList binaryBackward() { final midIndex = ((_startIndex + _endIndex) / 2).ceil(); final subset = originalModList.activeMods.keys.toList().sublist( _startIndex, midIndex, ); currentModList.disableAll(); currentModList.enableMods(subset); _endIndex = midIndex; return currentModList; } ModList linearForward({int stepSize = 20}) { final totalMods = originalModList.activeMods.length; if (_startIndex >= totalMods || _startIndex == _endIndex) { int newStart = totalMods - stepSize; if (newStart < 0) { newStart = 0; } _startIndex = newStart; } _startIndex = _startIndex.clamp(0, totalMods); _endIndex = (_startIndex + stepSize).clamp(0, totalMods); final subset = originalModList.activeMods.keys.toList().sublist( _startIndex, _endIndex, ); currentModList.disableAll(); currentModList.enableMods(subset); _startIndex = _endIndex; return currentModList; } ModList linearBackward({int stepSize = 20}) { final totalMods = originalModList.activeMods.length; if (_endIndex <= 0 || _startIndex == _endIndex) { int newEnd = stepSize; if (newEnd > totalMods) { newEnd = totalMods; } _endIndex = newEnd; } _endIndex = _endIndex.clamp(0, totalMods); _startIndex = (_endIndex - stepSize).clamp(0, _endIndex); final subset = originalModList.activeMods.keys.toList().sublist( _startIndex, _endIndex, ); currentModList.disableAll(); currentModList.enableMods(subset); _endIndex = _startIndex; return currentModList; } void reset() { currentModList = originalModList.copyWith(); } }