org.drools.planner.examples.travelingtournament.solver.smart.move.factory.SmartTravelingTournamentMoveFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of drools-planner-examples Show documentation
Show all versions of drools-planner-examples Show documentation
Drools Planner optimizes automated planning by combining metaheuristic search algorithms with rule
engine powered score calculation. This is the drools-planner-examples module which contains examples on how to use
Drools Planner.
/*
* Copyright 2010 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.planner.examples.travelingtournament.solver.smart.move.factory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.drools.planner.core.localsearch.LocalSearchSolverPhaseScope;
import org.drools.planner.core.move.Move;
import org.drools.planner.core.move.factory.AbstractMoveFactory;
import org.drools.planner.core.solution.Solution;
import org.drools.planner.examples.travelingtournament.domain.Day;
import org.drools.planner.examples.travelingtournament.domain.Match;
import org.drools.planner.examples.travelingtournament.domain.Team;
import org.drools.planner.examples.travelingtournament.domain.TravelingTournament;
import org.drools.planner.examples.travelingtournament.solver.smart.move.MatchSwapMove;
import org.drools.planner.examples.travelingtournament.solver.smart.move.MultipleMatchListRotateMove;
public class SmartTravelingTournamentMoveFactory extends AbstractMoveFactory {
private List cachedMoveList;
@Override
public void phaseStarted(LocalSearchSolverPhaseScope localSearchSolverPhaseScope) {
TravelingTournament travelingTournament = (TravelingTournament) localSearchSolverPhaseScope.getWorkingSolution();
cachedMoveList = new ArrayList(travelingTournament.getMatchList().size() / 2);
addCachedHomeAwaySwapMoves(travelingTournament);
}
private void addCachedHomeAwaySwapMoves(TravelingTournament travelingTournament) {
List matchList = travelingTournament.getMatchList();
for (Match firstMatch : matchList) {
for (Match secondMatch : matchList) {
if (firstMatch.getHomeTeam().equals(secondMatch.getAwayTeam())
&& firstMatch.getAwayTeam().equals(secondMatch.getHomeTeam())
&& (firstMatch.getId().compareTo(secondMatch.getId()) < 0)) {
MatchSwapMove matchSwapMove = new MatchSwapMove(firstMatch, secondMatch);
cachedMoveList.add(matchSwapMove);
break;
}
}
}
}
public List createMoveList(Solution solution) {
TravelingTournament travelingTournament = (TravelingTournament) solution;
List moveList = new ArrayList();
moveList.addAll(cachedMoveList);
RotationMovesFactory rotationMovesFactory = new RotationMovesFactory(travelingTournament);
logger.debug("Reused {} moves for N1 neighborhood.", moveList.size());
int oldSize = moveList.size();
rotationMovesFactory.addDayRotation(moveList);
logger.debug("Created {} moves for N3 U N5 neighborhood.", (moveList.size() - oldSize));
oldSize = moveList.size();
rotationMovesFactory.addTeamRotation(moveList);
logger.debug("Created {} moves for N2 U N4 neighborhood.", (moveList.size() - oldSize));
rotationMovesFactory = null;
return moveList;
}
private static class RotationMovesFactory {
private List dayList;
private List teamList;
private List matchList;
private Map> dayTeamMap;
private Map> teamDayMap;
private Map> homeTeamAwayTeamMap;
public RotationMovesFactory(TravelingTournament travelingTournament) {
dayList = travelingTournament.getDayList();
teamList = travelingTournament.getTeamList();
matchList = travelingTournament.getMatchList();
createMaps();
}
private void createMaps() {
dayTeamMap = new HashMap>(dayList.size());
for (Day day : dayList) {
// This map should be ordered so the order of the matchRotationList is the same (when it's used as tabu)
dayTeamMap.put(day, new LinkedHashMap(teamList.size()));
}
teamDayMap = new HashMap>(teamList.size());
homeTeamAwayTeamMap = new HashMap>(teamList.size());
for (Team team : teamList) {
// This map should be ordered so the order of the matchRotationList is the same (when it's used as tabu)
teamDayMap.put(team, new LinkedHashMap(dayList.size()));
homeTeamAwayTeamMap.put(team, new LinkedHashMap(teamList.size() - 1));
}
for (Match match : matchList) {
Map subTeamMap = dayTeamMap.get(match.getDay());
subTeamMap.put(match.getHomeTeam(), match);
subTeamMap.put(match.getAwayTeam(), match);
teamDayMap.get(match.getHomeTeam()).put(match.getDay(), match);
teamDayMap.get(match.getAwayTeam()).put(match.getDay(), match);
homeTeamAwayTeamMap.get(match.getHomeTeam()).put(match.getAwayTeam(), match);
}
}
private Team getOtherTeam(Match match, Team team) {
return match.getHomeTeam().equals(team) ? match.getAwayTeam() : match.getHomeTeam();
}
/**
* @TODO clean up this code
*/
private void addDayRotation(List moveList) {
for (ListIterator firstDayIt = dayList.listIterator(); firstDayIt.hasNext();) {
Day firstDay = firstDayIt.next();
Map firstDayTeamMap = dayTeamMap.get(firstDay);
for (ListIterator secondDayIt = dayList.listIterator(firstDayIt.nextIndex()); secondDayIt.hasNext();) {
Day secondDay = secondDayIt.next();
List clonedFirstDayMatchList = new ArrayList(firstDayTeamMap.values());
while (!clonedFirstDayMatchList.isEmpty()) {
List rotateList = new ArrayList(4);
Match startMatch = clonedFirstDayMatchList.remove(0);
boolean otherInFirst = false;
rotateList.add(startMatch);
Team startHomeTeam = startMatch.getHomeTeam();
Team nextTeamToFind = startMatch.getAwayTeam();
while (!startHomeTeam.equals(nextTeamToFind)) {
Map subTeamMap = dayTeamMap.get(otherInFirst ? firstDay : secondDay);
Match repairMatch = subTeamMap.get(nextTeamToFind);
if (otherInFirst) {
clonedFirstDayMatchList.remove(repairMatch);
}
rotateList.add(repairMatch);
nextTeamToFind = getOtherTeam(repairMatch, nextTeamToFind);
otherInFirst = !otherInFirst;
}
// assert(rotateList.size() % 2 == 0);
// if size is 2 then addCachedHomeAwaySwapMoves will have done it
if (rotateList.size() > 2) {
List emptyList = Collections.emptyList();
Move rotateMove = new MultipleMatchListRotateMove(rotateList, emptyList);
moveList.add(rotateMove);
}
}
}
}
}
/**
* @TODO clean up this code
*/
private void addTeamRotation(List moveList) {
for (ListIterator firstTeamIt = teamList.listIterator(); firstTeamIt.hasNext();) {
Team firstTeam = firstTeamIt.next();
Map firstTeamDayMap = teamDayMap.get(firstTeam);
for (ListIterator secondTeamIt = teamList.listIterator(firstTeamIt.nextIndex()); secondTeamIt.hasNext();) {
Team secondTeam = secondTeamIt.next();
List clonedFirstTeamMatchList = new ArrayList(firstTeamDayMap.values());
while (!clonedFirstTeamMatchList.isEmpty()) {
List firstRotateList = new ArrayList();
List secondRotateList = new ArrayList();
Match firstStartMatch = clonedFirstTeamMatchList.remove(0);
Team firstStartTeam = getOtherTeam(firstStartMatch, firstTeam);
Day startDay = firstStartMatch.getDay();
boolean firstTeamIsHomeTeam = firstStartMatch.getHomeTeam().equals(firstTeam);
Match secondStartMatch = teamDayMap.get(secondTeam).get(startDay);
if (firstStartMatch.equals(secondStartMatch)) {
break;
}
firstRotateList.add(0, firstStartMatch);
secondRotateList.add(secondStartMatch);
Map visitedTeamMap = new HashMap();
Team teamToFind = getOtherTeam(secondStartMatch, secondTeam);
while (!teamToFind.equals(firstStartTeam)) {
// boolean shortcut = visitedTeamMap.containsKey(teamToFind);
// if (shortcut) {
Match firstRepairMatch = homeTeamAwayTeamMap
.get(firstTeamIsHomeTeam ? firstTeam : teamToFind)
.get(firstTeamIsHomeTeam ? teamToFind : firstTeam);
if (!clonedFirstTeamMatchList.contains(firstRepairMatch)) {
if (visitedTeamMap.containsKey(teamToFind)) {
// shortcut splitoff is possible
Match shortcutMatch = visitedTeamMap.get(teamToFind);
int shortcutSize = firstRotateList.indexOf(shortcutMatch) + 1;
int reverseShortcutSize = firstRotateList.size() - shortcutSize;
List firstShortcutRotateList = new ArrayList(
firstRotateList.subList(0, shortcutSize));
for (Match match : firstShortcutRotateList) {
visitedTeamMap.remove(getOtherTeam(match, firstTeam));
}
List secondShortcutRotateList = new ArrayList(
secondRotateList.subList(reverseShortcutSize, secondRotateList.size()));
firstRotateList = new ArrayList(
firstRotateList.subList(shortcutSize, firstRotateList.size()));
secondRotateList = new ArrayList(
secondRotateList.subList(0, reverseShortcutSize));
addTeamRotateMove(moveList, firstShortcutRotateList, secondShortcutRotateList);
}
firstTeamIsHomeTeam = !firstTeamIsHomeTeam;
// Team firstRepairHomeTeam = (firstTeamIsHomeTeam ^ shortcut) ? firstTeam : teamToFind;
// Team firstRepairAwayTeam = (firstTeamIsHomeTeam ^ shortcut) ? teamToFind : firstTeam;
// Match firstRepairMatch = homeTeamAwayTeamMap
// .get(firstRepairHomeTeam).get(firstRepairAwayTeam);
firstRepairMatch = homeTeamAwayTeamMap
.get(firstTeamIsHomeTeam ? firstTeam : teamToFind)
.get(firstTeamIsHomeTeam ? teamToFind : firstTeam);
}
Day repairDay = firstRepairMatch.getDay();
Match secondRepairMatch = teamDayMap.get(secondTeam).get(repairDay);
clonedFirstTeamMatchList.remove(firstRepairMatch);
visitedTeamMap.put(teamToFind, firstRepairMatch);
firstRotateList.add(0, firstRepairMatch);
secondRotateList.add(secondRepairMatch);
teamToFind = getOtherTeam(secondRepairMatch, secondTeam);
}
addTeamRotateMove(moveList, firstRotateList, secondRotateList);
}
}
}
}
private void addTeamRotateMove(List moveList, List firstRotateList, List secondRotateList) {
assert (firstRotateList.size() == secondRotateList.size());
// if size is 1 then addCachedHomeAwaySwapMoves will have done it
// if size is 2 then addDayRotation will have done it by 1 list of size 4
if (firstRotateList.size() > 2) {
Move rotateMove = new MultipleMatchListRotateMove(firstRotateList, secondRotateList);
moveList.add(rotateMove);
}
}
}
}