This is the configuration we use with optaplanner 6.2.0. To keep the customers locked in place filters had to be implemented for changeMoveSelector, swapMoveSelector and tailChainSwapMoveSelector in addition to CustomerEntitySelectionFilter.
<localSearch>
<unionMoveSelector>
<changeMoveSelector>
<entitySelector id="entitySelector1"/>
<filterClass>...THIS....CustomerFilterChangeMove</filterClass>
<valueSelector>
<nearbySelection>
<originEntitySelector mimicSelectorRef="entitySelector1"/>
<nearbyDistanceMeterClass>....CustomerNearbyDistanceMeter</nearbyDistanceMeterClass>
<parabolicDistributionSizeMaximum>80</parabolicDistributionSizeMaximum>
</nearbySelection>
</valueSelector>
</changeMoveSelector>
<swapMoveSelector>
<filterClass>...THIS....CustomerFilterSwapMove</filterClass>
</swapMoveSelector>
<tailChainSwapMoveSelector>
<entitySelector id="entitySelector3"/>
<filterClass>...THIS...CustomerFilterTailChainSwapMove</filterClass>
<valueSelector>
<nearbySelection>
<originEntitySelector mimicSelectorRef="entitySelector3"/>
<nearbyDistanceMeterClass>....CustomerNearbyDistanceMeter</nearbyDistanceMeterClass>
<parabolicDistributionSizeMaximum>80</parabolicDistributionSizeMaximum>
</nearbySelection>
</valueSelector>
<!--Disabled, doesn't work with tailChain -->
<!--<selectReversingMoveToo>false</selectReversingMoveToo>-->
</tailChainSwapMoveSelector>
</unionMoveSelector>
<acceptor>
<lateAcceptanceSize>200</lateAcceptanceSize>
</acceptor>
<forager>
<acceptedCountLimit>1</acceptedCountLimit>
</forager>
</localSearch>
The customer chain is checked for locked customers on EntitySelectionFilter and CustomerFilterTailChainSwapMove (both chains).
The classes are:
namespace ...THIS...;
public class CustomerFilterChangeMove implements SelectionFilter<ChangeMove> {
@Override
public boolean accept(ScoreDirector scoreDirector, ChangeMove changeMove) {
Customer customer = (Customer) changeMove.getEntity();
if(customer!=null && customer.isLocked())
return false;
//everything is fine
return true;
}
}
public class CustomerFilterSwapMove implements SelectionFilter<SwapMove>
{
@Override
public boolean accept(ScoreDirector scoreDirector, SwapMove move)
{
Customer leftCustomer = (Customer) move.getLeftEntity();
Customer rightCustomer = (Customer) move.getRightEntity();
if(leftCustomer!=null && leftCustomer.isLocked())
return false;
if(rightCustomer!=null && rightCustomer.isLocked())
return false;
return true;
}
}
public class CustomerFilterTailChainSwapMove implements SelectionFilter<TailChainSwapMove>
{
/**
* Chain starting at left entity will be moved to the right. If there's an entity on the right side, it's chain will be moved to the left
*/
@Override
public boolean accept(ScoreDirector scoreDirector, TailChainSwapMove move)
{
Customer shadow=null;
Customer leftCustomer = (Customer) move.getLeftEntity();
Customer rightCustomer = null;
Vehicle leftVehicle = leftCustomer.getVehicle();
Vehicle rightVehicle=null;
if(move.getRightValue() instanceof Customer)
{
rightCustomer = (Customer) move.getRightValue();
rightVehicle = rightCustomer.getVehicle();
}
else if(move.getRightValue() instanceof Vehicle)
{
rightVehicle = (Vehicle) move.getRightValue();
rightCustomer = rightVehicle.getNextCustomer();
}
shadow=rightCustomer;
while(shadow!=null)
{
if(shadow.isLocked())
return false;
shadow=shadow.getNextCustomer();
}
shadow=leftCustomer;
while(shadow!=null)
{
if(shadow.isLocked())
return false;
shadow=shadow.getNextCustomer();
}
return true;
}
}
public class CustomerEntitySelectionFilter implements SelectionFilter<Customer> {
@Override
public boolean accept(ScoreDirector scoreDirector, Customer customer) {
Customer shadow = customer;
while(shadow!=null)
{
if (shadow.isLocked())
return false;
shadow=shadow.getNextCustomer();
}
return true;
}
}