|
1 | 1 | package ai.timefold.solver.core.impl.exhaustivesearch.decider; |
2 | 2 |
|
3 | | -import java.util.ArrayList; |
4 | | -import java.util.Collections; |
| 3 | +import java.util.Arrays; |
5 | 4 |
|
6 | 5 | import ai.timefold.solver.core.api.score.Score; |
7 | 6 | import ai.timefold.solver.core.impl.domain.variable.ListVariableStateSupply; |
@@ -102,35 +101,56 @@ public boolean isEntityReinitializable(Object entity) { |
102 | 101 | public void restoreWorkingSolution(ExhaustiveSearchStepScope<Solution_> stepScope, |
103 | 102 | boolean assertWorkingSolutionScoreFromScratch, boolean assertExpectedWorkingSolutionScore) { |
104 | 103 | var phaseScope = stepScope.getPhaseScope(); |
105 | | - //First, undo all previous changes |
106 | | - var undoNode = phaseScope.getLastCompletedStepScope().getExpandingNode(); |
107 | | - var unassignMoveList = new ArrayList<Move<Solution_>>(); |
108 | | - while (undoNode.getUndoMove() != null) { |
109 | | - unassignMoveList.add(undoNode.getUndoMove()); |
110 | | - undoNode = undoNode.getParent(); |
111 | | - } |
112 | | - // Next, rebuild the solution starting from the current search element |
113 | | - var assignNode = stepScope.getExpandingNode(); |
114 | | - var assignMoveList = new ArrayList<Move<Solution_>>(); |
115 | | - while (assignNode.getMove() != null) { |
116 | | - assignMoveList.add(assignNode.getMove()); |
117 | | - assignNode = assignNode.getParent(); |
118 | | - } |
119 | | - Collections.reverse(assignMoveList); |
120 | | - var allMoves = new ArrayList<Move<Solution_>>(unassignMoveList.size() + assignMoveList.size()); |
121 | | - allMoves.addAll(unassignMoveList); |
122 | | - allMoves.addAll(assignMoveList); |
123 | | - if (allMoves.isEmpty()) { |
124 | | - // No moves to restore, so the working solution is already correct |
| 104 | + //First, undo all previous changes. |
| 105 | + var unassignMoves = listAllUndoMoves(phaseScope.getLastCompletedStepScope().getExpandingNode()); |
| 106 | + // Next, rebuild the solution starting from the current search element. |
| 107 | + var assignMoves = listAllMovesInReverseOrder(stepScope.getExpandingNode()); |
| 108 | + var totalLength = unassignMoves.length + assignMoves.length; |
| 109 | + if (totalLength == 0) { |
| 110 | + // No moves to restore, so the working solution is already correct. |
125 | 111 | return; |
126 | 112 | } |
127 | | - var compositeMove = Moves.compose(allMoves); |
| 113 | + // Build a composite move of both arrays. |
| 114 | + var moves = Arrays.copyOf(unassignMoves, unassignMoves.length + assignMoves.length); |
| 115 | + System.arraycopy(assignMoves, 0, moves, unassignMoves.length, assignMoves.length); |
| 116 | + var compositeMove = Moves.compose(moves); |
| 117 | + // Execute the move. |
128 | 118 | phaseScope.getScoreDirector().executeMove(compositeMove); |
129 | 119 | var score = phaseScope.<Score_> calculateScore(); |
130 | 120 | stepScope.getExpandingNode().setScore(score); |
131 | 121 | phaseScope.getSolutionDescriptor().setScore(phaseScope.getWorkingSolution(), score.raw()); |
132 | 122 | } |
133 | 123 |
|
| 124 | + private static <Solution_> Move<Solution_>[] listAllUndoMoves(ExhaustiveSearchNode<Solution_> node) { |
| 125 | + return listAllUndoMoves(node, 0); |
| 126 | + } |
| 127 | + |
| 128 | + private static <Solution_> Move<Solution_>[] listAllUndoMoves(ExhaustiveSearchNode<Solution_> node, int depth) { |
| 129 | + var undoMove = node.getUndoMove(); |
| 130 | + if (undoMove != null) { |
| 131 | + var array = listAllUndoMoves(node.getParent(), depth + 1); |
| 132 | + array[depth] = undoMove; |
| 133 | + return array; |
| 134 | + } else { |
| 135 | + return new Move[depth]; |
| 136 | + } |
| 137 | + } |
| 138 | + |
| 139 | + private static <Solution_> Move<Solution_>[] listAllMovesInReverseOrder(ExhaustiveSearchNode<Solution_> node) { |
| 140 | + return listAllMovesInReverseOrder(node, 0); |
| 141 | + } |
| 142 | + |
| 143 | + private static <Solution_> Move<Solution_>[] listAllMovesInReverseOrder(ExhaustiveSearchNode<Solution_> node, int depth) { |
| 144 | + var move = node.getMove(); |
| 145 | + if (move != null) { |
| 146 | + var array = listAllMovesInReverseOrder(node.getParent(), depth + 1); |
| 147 | + array[array.length - depth - 1] = move; |
| 148 | + return array; |
| 149 | + } else { |
| 150 | + return new Move[depth]; |
| 151 | + } |
| 152 | + } |
| 153 | + |
134 | 154 | // ************************************************************************ |
135 | 155 | // Lifecycle methods |
136 | 156 | // ************************************************************************ |
|
0 commit comments