Skip to content

Commit 8514506

Browse files
committed
Fix the final test
1 parent 68ce48e commit 8514506

File tree

4 files changed

+34
-19
lines changed

4 files changed

+34
-19
lines changed

core/src/main/java/ai/timefold/solver/core/impl/move/streams/maybeapi/generic/provider/ListChangeMoveProvider.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package ai.timefold.solver.core.impl.move.streams.maybeapi.generic.provider;
22

3-
import java.util.Objects;
4-
53
import ai.timefold.solver.core.impl.move.streams.maybeapi.BiDataFilter;
64
import ai.timefold.solver.core.impl.move.streams.maybeapi.DataJoiners;
75
import ai.timefold.solver.core.impl.move.streams.maybeapi.generic.move.ListAssignMove;
@@ -14,9 +12,10 @@
1412
import ai.timefold.solver.core.preview.api.domain.metamodel.PlanningListVariableMetaModel;
1513
import ai.timefold.solver.core.preview.api.domain.metamodel.PositionInList;
1614
import ai.timefold.solver.core.preview.api.domain.metamodel.UnassignedElement;
17-
1815
import org.jspecify.annotations.NullMarked;
1916

17+
import java.util.Objects;
18+
2019
@NullMarked
2120
public class ListChangeMoveProvider<Solution_, Entity_, Value_>
2221
implements MoveProvider<Solution_> {
@@ -38,10 +37,11 @@ public ListChangeMoveProvider(PlanningListVariableMetaModel<Solution_, Entity_,
3837
};
3938
this.validChangeFilter = (solutionView, value, targetPosition) -> {
4039
var currentPosition = solutionView.getPositionOf(variableMetaModel, value);
41-
if (currentPosition.equals(targetPosition)) {
40+
if (currentPosition.equals(targetPosition)) { // No change needed.
4241
return false;
4342
}
4443
if (currentPosition instanceof UnassignedElement) {
44+
// Only assign the value if the target entity will accept it.
4545
var targetPositionInList = targetPosition.ensureAssigned();
4646
return solutionView.isValueInRange(variableMetaModel, targetPositionInList.entity(), value);
4747
} else {
@@ -53,11 +53,13 @@ public ListChangeMoveProvider(PlanningListVariableMetaModel<Solution_, Entity_,
5353
var valueCount = solutionView.countValues(variableMetaModel, currentPositionInList.entity());
5454
if (valueCount == 1) {
5555
return false; // The value is already in the list, and it is the only one.
56-
} else {
56+
} else if (targetPositionInList.index() == valueCount) {
57+
// The value is already in the list, and we are trying to move it past the end of the list.
58+
return false;
59+
} else { // Same list, same position; ignore.
5760
return currentPositionInList.index() != targetPositionInList.index();
5861
}
59-
} else {
60-
// We can move freely between entities.
62+
} else { // We can move freely between entities, assuming the target entity accepts the value.
6163
return solutionView.isValueInRange(variableMetaModel, targetPositionInList.entity(), value);
6264
}
6365
}
@@ -66,7 +68,7 @@ public ListChangeMoveProvider(PlanningListVariableMetaModel<Solution_, Entity_,
6668

6769
@Override
6870
public MoveProducer<Solution_> apply(MoveStreamFactory<Solution_> moveStreamFactory) {
69-
// For each unassigned value, we need to create a move to assign it to same position of some list variable.
71+
// For each unassigned value, we need to create a move to assign it to some position of some list variable.
7072
// For each assigned value that is not pinned, we need to create:
7173
// - A move to unassign it.
7274
// - A move to reassign it to another position if assigned.

core/src/test/java/ai/timefold/solver/core/impl/move/streams/maybeapi/provider/ChangeMoveProviderTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package ai.timefold.solver.core.impl.move.streams.maybeapi.provider;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.SoftAssertions.assertSoftly;
5+
6+
import java.util.Collections;
7+
import java.util.stream.StreamSupport;
8+
39
import ai.timefold.solver.core.api.score.buildin.simple.SimpleScore;
410
import ai.timefold.solver.core.api.score.stream.Constraint;
511
import ai.timefold.solver.core.api.score.stream.ConstraintFactory;
@@ -24,15 +30,10 @@
2430
import ai.timefold.solver.core.testdomain.valuerange.entityproviding.unassignedvar.TestdataAllowsUnassignedEntityProvidingSolution;
2531
import ai.timefold.solver.core.testdomain.valuerange.incomplete.TestdataIncompleteValueRangeEntity;
2632
import ai.timefold.solver.core.testdomain.valuerange.incomplete.TestdataIncompleteValueRangeSolution;
33+
2734
import org.jspecify.annotations.NullMarked;
2835
import org.junit.jupiter.api.Test;
2936

30-
import java.util.Collections;
31-
import java.util.stream.StreamSupport;
32-
33-
import static org.assertj.core.api.Assertions.assertThat;
34-
import static org.assertj.core.api.SoftAssertions.assertSoftly;
35-
3637
@NullMarked
3738
class ChangeMoveProviderTest {
3839

core/src/test/java/ai/timefold/solver/core/impl/solver/DefaultSolverTest.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ void solveWithMoveStreams() {
202202
}
203203

204204
@Test
205-
void solveListVarWithMoveStreams() {
205+
void solveWithMoveStreamsListVar() {
206206
var solverConfig = new SolverConfig()
207207
.withPreviewFeature(PreviewFeature.MOVE_STREAMS)
208208
.withSolutionClass(TestdataListSolution.class)
@@ -213,9 +213,16 @@ void solveListVarWithMoveStreams() {
213213
.withPhases(new LocalSearchPhaseConfig()
214214
.withMoveProvidersClass(TestingListMoveProviders.class));
215215

216-
var solution = TestdataListSolution.generateInitializedSolution(3, 2);
216+
// Both values are on the same entity; the goal of the solver is to move one of them to the other entity.
217+
var solution = TestdataListSolution.generateUninitializedSolution(2, 2);
218+
var v1 = solution.getValueList().get(0);
219+
var v2 = solution.getValueList().get(1);
220+
var e1 = solution.getEntityList().get(0);
221+
e1.addValue(v1);
222+
e1.addValue(v2);
223+
solution.getEntityList().forEach(TestdataListEntity::setUpShadowVariables);
217224

218-
solution = PlannerTestUtils.solve(solverConfig, solution, false);
225+
solution = PlannerTestUtils.solve(solverConfig, solution, true);
219226
assertThat(solution).isNotNull();
220227
}
221228

@@ -2389,12 +2396,14 @@ public void afterEntityRemoved(@NonNull Object entity) {
23892396

23902397
@NullMarked
23912398
public static final class TestingMoveProviders implements MoveProviders<TestdataSolution> {
2399+
23922400
@Override
23932401
public List<MoveProvider<TestdataSolution>> defineMoves(PlanningSolutionMetaModel<TestdataSolution> solutionMetaModel) {
23942402
var variableMetamodel = solutionMetaModel.entity(TestdataEntity.class)
2395-
.<TestdataValue> planningVariable("value");
2403+
.<TestdataValue> planningVariable();
23962404
return List.of(new ChangeMoveProvider<>(variableMetamodel));
23972405
}
2406+
23982407
}
23992408

24002409
/**
@@ -2419,13 +2428,15 @@ public static final class TestingEasyScoreCalculator implements EasyScoreCalcula
24192428

24202429
@NullMarked
24212430
public static final class TestingListMoveProviders implements MoveProviders<TestdataListSolution> {
2431+
24222432
@Override
24232433
public List<MoveProvider<TestdataListSolution>>
24242434
defineMoves(PlanningSolutionMetaModel<TestdataListSolution> solutionMetaModel) {
24252435
var variableMetamodel = solutionMetaModel.entity(TestdataListEntity.class)
2426-
.planningListVariable("valueList");
2436+
.planningListVariable();
24272437
return List.of(new ListChangeMoveProvider<>(variableMetamodel));
24282438
}
2439+
24292440
}
24302441

24312442
/**

core/src/test/java/ai/timefold/solver/core/testdomain/list/TestdataListEntity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ public void removeValue(TestdataListValue value) {
7373
.filter(v -> !Objects.equals(v, value))
7474
.toList();
7575
}
76+
7677
}

0 commit comments

Comments
 (0)