Fix linear forwards backwards

This commit is contained in:
2025-03-17 08:47:39 +01:00
parent 2df23dde06
commit 872a59b27c
2 changed files with 113 additions and 38 deletions

View File

@@ -15,6 +15,7 @@ import 'package:rimworld_modman/mod_list.dart';
class ModListTroubleshooter { class ModListTroubleshooter {
final ModList originalModList; final ModList originalModList;
ModList currentModList; ModList currentModList;
// These indices should ALWAYS represent the CURRENT selection of mods
int _startIndex = 0; int _startIndex = 0;
int _endIndex = 0; int _endIndex = 0;
@@ -48,20 +49,25 @@ class ModListTroubleshooter {
return currentModList; return currentModList;
} }
// If the current selection is not equal to our proposed step size
// We do not MOVE but instead just return the correct amount of mods from the start
ModList linearForward({int stepSize = 20}) { ModList linearForward({int stepSize = 20}) {
final totalMods = originalModList.activeMods.length; // If we are not "in step"
if (_endIndex - _startIndex == stepSize) {
if (_startIndex >= totalMods || _startIndex == _endIndex) { // Move the indices forward by the step size, step forward
int newStart = totalMods - stepSize; _startIndex += stepSize;
if (newStart < 0) { _endIndex += stepSize;
newStart = 0; } else {
// Correct the end index to be in step
_endIndex = _startIndex + stepSize;
} }
_startIndex = newStart; if (_endIndex > originalModList.activeMods.length) {
// If we are at the end of the list, move the start index such that we return
// At most the step size amount of mods
_endIndex = originalModList.activeMods.length;
_startIndex = (_endIndex - stepSize).clamp(0, _endIndex);
} }
_startIndex = _startIndex.clamp(0, totalMods);
_endIndex = (_startIndex + stepSize).clamp(0, totalMods);
final subset = originalModList.activeMods.keys.toList().sublist( final subset = originalModList.activeMods.keys.toList().sublist(
_startIndex, _startIndex,
_endIndex, _endIndex,
@@ -69,25 +75,21 @@ class ModListTroubleshooter {
currentModList.disableAll(); currentModList.disableAll();
currentModList.enableMods(subset); currentModList.enableMods(subset);
_startIndex = _endIndex;
return currentModList; return currentModList;
} }
ModList linearBackward({int stepSize = 20}) { ModList linearBackward({int stepSize = 20}) {
final totalMods = originalModList.activeMods.length; if (_endIndex - _startIndex == stepSize) {
_startIndex -= stepSize;
if (_endIndex <= 0 || _startIndex == _endIndex) { _endIndex -= stepSize;
int newEnd = stepSize; } else {
if (newEnd > totalMods) { _startIndex = _endIndex - stepSize;
newEnd = totalMods;
} }
_endIndex = newEnd; if (_startIndex < 0) {
_startIndex = 0;
_endIndex = stepSize.clamp(0, originalModList.activeMods.length);
} }
_endIndex = _endIndex.clamp(0, totalMods);
_startIndex = (_endIndex - stepSize).clamp(0, _endIndex);
final subset = originalModList.activeMods.keys.toList().sublist( final subset = originalModList.activeMods.keys.toList().sublist(
_startIndex, _startIndex,
_endIndex, _endIndex,
@@ -95,8 +97,6 @@ class ModListTroubleshooter {
currentModList.disableAll(); currentModList.disableAll();
currentModList.enableMods(subset); currentModList.enableMods(subset);
_endIndex = _startIndex;
return currentModList; return currentModList;
} }

View File

@@ -75,7 +75,7 @@ void main() {
}); });
test( test(
'Bisect search should end up with half the mods every forward iteration until 1', 'Should end up with half the mods every forward iteration until 1',
() { () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
@@ -103,7 +103,7 @@ void main() {
}, },
); );
test( test(
'Bisect search should end up with half the mods every backward iteration until 1', 'Should end up with half the mods every backward iteration until 1',
() { () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
@@ -131,7 +131,7 @@ void main() {
}, },
); );
test( test(
'Bisect search should end up with half the mods every iteration until 1', 'Should end up with half the mods every iteration until 1',
() { () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
@@ -158,7 +158,7 @@ void main() {
expect(result.activeMods.keys.last, equals('test.mod9')); expect(result.activeMods.keys.last, equals('test.mod9'));
}, },
); );
test('Bisect should handle abuse gracefully', () { test('Should handle abuse gracefully', () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
var result = troubleshooter.binaryBackward(); var result = troubleshooter.binaryBackward();
@@ -225,7 +225,7 @@ void main() {
}); });
test( test(
'Linear search should end up with 10 mods every forward iteration', 'Should end up with 10 mods every forward iteration',
() { () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
@@ -246,7 +246,7 @@ void main() {
}, },
); );
test( test(
'Linear search should end up with 10 mods every backward iteration', 'Should end up with 10 mods every backward iteration',
() { () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
@@ -266,7 +266,7 @@ void main() {
expect(result.activeMods.keys.last, equals('test.mod9')); expect(result.activeMods.keys.last, equals('test.mod9'));
}, },
); );
test('Linear search should end up with 10 mods every iteration', () { test('Should end up with 10 mods every iteration', () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
var result = troubleshooter.linearBackward(stepSize: 10); var result = troubleshooter.linearBackward(stepSize: 10);
@@ -281,10 +281,10 @@ void main() {
result = troubleshooter.linearBackward(stepSize: 10); result = troubleshooter.linearBackward(stepSize: 10);
expect(result.activeMods.length, equals(10)); expect(result.activeMods.length, equals(10));
expect(result.activeMods.keys.first, equals('test.mod20')); expect(result.activeMods.keys.first, equals('test.mod10'));
expect(result.activeMods.keys.last, equals('test.mod29')); expect(result.activeMods.keys.last, equals('test.mod19'));
}); });
test('Linear search should handle abuse gracefully', () { test('Should handle abuse gracefully', () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
var result = troubleshooter.linearBackward(stepSize: 10); var result = troubleshooter.linearBackward(stepSize: 10);
@@ -302,7 +302,25 @@ void main() {
result = troubleshooter.linearForward(stepSize: 10); result = troubleshooter.linearForward(stepSize: 10);
expect(result.activeMods.length, equals(10)); expect(result.activeMods.length, equals(10));
}); });
test('Linear search cannot return more items than there are', () { test('Should handle different step sizes', () {
final troubleshooter = ModListTroubleshooter(modList);
var result = troubleshooter.linearBackward(stepSize: 10);
expect(result.activeMods.length, equals(10));
result = troubleshooter.linearForward(stepSize: 10);
expect(result.activeMods.length, equals(10));
result = troubleshooter.linearBackward(stepSize: 10);
expect(result.activeMods.length, equals(10));
result = troubleshooter.linearForward(stepSize: 10);
expect(result.activeMods.length, equals(10));
result = troubleshooter.linearForward(stepSize: 10);
expect(result.activeMods.length, equals(10));
});
test('Cannot return more items than there are', () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
var result = troubleshooter.linearBackward(stepSize: 10000); var result = troubleshooter.linearBackward(stepSize: 10000);
@@ -312,6 +330,63 @@ void main() {
expect(result.activeMods.length, equals(30)); expect(result.activeMods.length, equals(30));
}); });
}); });
group('Navigation tests', () {
late ModList modList = ModList();
setUp(() {
modList = ModList();
// Add some base mods
for (int i = 0; i < 20; i++) {
final modId = 'test.mod$i';
final mod = makeDummy().copyWith(name: 'Test Mod $i', id: modId);
modList.mods[modId] = mod;
}
// Add some mods with dependencies
for (int i = 20; i < 30; i++) {
final modId = 'test.mod$i';
final mod = makeDummy().copyWith(
name: 'Test Mod $i',
id: modId,
dependencies: ['test.mod${i - 20}'], // Depend on earlier mods
);
modList.mods[modId] = mod;
}
modList.enableAll();
});
test(
'Mixed navigation should work',
() {
final troubleshooter = ModListTroubleshooter(modList);
var result = troubleshooter.linearForward(stepSize: 10);
expect(result.activeMods.length, equals(10));
expect(result.activeMods.keys.first, equals('test.mod0'));
expect(result.activeMods.keys.last, equals('test.mod9'));
result = troubleshooter.binaryForward();
expect(result.activeMods.length, equals(5));
expect(result.activeMods.keys.first, equals('test.mod5'));
expect(result.activeMods.keys.last, equals('test.mod9'));
result = troubleshooter.linearBackward(stepSize: 10);
expect(result.activeMods.length, equals(10));
expect(result.activeMods.keys.first, equals('test.mod0'));
expect(result.activeMods.keys.last, equals('test.mod9'));
result = troubleshooter.binaryForward();
expect(result.activeMods.length, equals(5));
expect(result.activeMods.keys.first, equals('test.mod5'));
expect(result.activeMods.keys.last, equals('test.mod9'));
result = troubleshooter.linearForward(stepSize: 10);
expect(result.activeMods.length, equals(10));
expect(result.activeMods.keys.first, equals('test.mod5'));
expect(result.activeMods.keys.last, equals('test.mod14'));
},
);
});
group('Loading dependencies', () { group('Loading dependencies', () {
late ModList modList = ModList(); late ModList modList = ModList();
setUp(() { setUp(() {
@@ -336,7 +411,7 @@ void main() {
modList.enableAll(); modList.enableAll();
}); });
// Not that it has any reason to since they're completely detached... // Not that it has any reason to since they're completely detached...
test('Loading dependencies should not fuck up troubleshooter', () { test('Should not fuck up troubleshooter', () {
final troubleshooter = ModListTroubleshooter(modList); final troubleshooter = ModListTroubleshooter(modList);
final expectedFirst = [ final expectedFirst = [
'test.mod1', 'test.mod1',