2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
6
* This program is distributed in the hope that it will be useful,
7
* but WITHOUT ANY WARRANTY; without even the implied warranty of
8
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
* GNU General Public License for more details.
10
* You should have received a copy of the GNU General Public License
11
* along with this program; if not, write to the Free Software
12
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
package games.strategy.engine.data;
17
import games.strategy.triplea.delegate.Matches;
18
import games.strategy.util.CompositeMatchOr;
19
import games.strategy.util.Match;
21
import java.util.ArrayList;
22
import java.util.Collection;
23
import java.util.Collections;
24
import java.util.HashMap;
25
import java.util.HashSet;
26
import java.util.List;
27
import java.util.logging.Logger;
29
public class CompositeRouteFinder
31
private final static Logger s_logger = Logger.getLogger(CompositeRouteFinder.class.getName());
32
public static Logger GetStaticLogger()
36
private final GameMap m_map;
37
private final HashMap<Match<Territory>, Integer> m_matches;
40
* This class can find composite routes between two territories.
42
* Example set of matches: [Friendly Land, score: 1] [Enemy Land, score: 2] [Neutral Land, score = 4]
44
* With this example set, an 8 length friendly route is considered equal in score to a 4 length enemy route and a 2 length neutral route.
45
* This is because the friendly route score is 1/2 of the enemy route score and 1/4 of the neutral route score.
47
* Note that you can choose whatever scores you want, and that the matches can mix and match with each other in any way.
49
* @param map - Game map found through <gamedata>.getMap()
50
* @param matches - Set of matches and scores. The lower a match is scored, the more favorable it is.
52
public CompositeRouteFinder(GameMap map, HashMap<Match<Territory>, Integer> matches)
56
s_logger.finer("Initializing CompositeRouteFinderClass...");
58
private HashSet<Territory> ToHashSet(Collection<Territory> ters)
60
HashSet<Territory> result = new HashSet<Territory>();
61
for(Territory ter : ters)
65
public Route findRoute(Territory start, Territory end)
67
HashSet<Territory> allMatchingTers = ToHashSet(Match.getMatches(m_map.getTerritories(), new CompositeMatchOr<Territory>(m_matches.keySet())));
69
HashMap<Territory, Integer> terScoreMap = CreateScoreMap(allMatchingTers, start);
70
HashMap<Territory, Integer> routeScoreMap = new HashMap<Territory, Integer>();
71
int bestRouteToEndScore = Integer.MAX_VALUE;
73
HashMap<Territory, Territory> previous = new HashMap<Territory, Territory>();
74
List<Territory> routeLeadersToProcess = new ArrayList<Territory>();
75
for (Territory ter : m_map.getNeighbors(start, Matches.territoryIsInList(allMatchingTers)))
77
int routeScore = terScoreMap.get(start) + terScoreMap.get(ter);
78
routeScoreMap.put(ter, routeScore);
79
routeLeadersToProcess.add(ter);
80
previous.put(ter, start);
82
while(routeLeadersToProcess.size() > 0)
84
List<Territory> newLeaders = new ArrayList<Territory>();
85
for(Territory oldLeader : routeLeadersToProcess)
87
for (Territory ter : m_map.getNeighbors(oldLeader, Matches.territoryIsInList(allMatchingTers)))
89
int routeScore = routeScoreMap.get(oldLeader) + terScoreMap.get(ter);
90
if(routeLeadersToProcess.contains(ter) || ter.equals(start)) //If we're bumping into one of the current route leaders or the start
92
if(previous.containsKey(ter)) //If we're bumping into an existing route
94
if(routeScore >= routeScoreMap.get(ter)) //If the already existing route route is better or the same
97
if (bestRouteToEndScore <= routeScore)
98
continue; //Ignore this route leader, as we know we already have a better route
99
routeScoreMap.put(ter, routeScore);
101
previous.put(ter, oldLeader);
104
if(routeScore < bestRouteToEndScore)
105
bestRouteToEndScore = routeScore;
109
routeLeadersToProcess = newLeaders;
111
if(bestRouteToEndScore == Integer.MAX_VALUE)
113
return AssembleRoute(start, end, previous);
115
private Route AssembleRoute(Territory start, Territory end, HashMap<Territory, Territory> previous)
117
List<Territory> routeTers = new ArrayList<Territory>();
118
Territory curTer = end;
119
while(previous.containsKey(curTer))
121
routeTers.add(curTer);
122
curTer = previous.get(curTer);
124
routeTers.add(start);
125
Collections.reverse(routeTers);
126
return new Route(routeTers);
128
private HashMap<Territory, Integer> CreateScoreMap(Collection<Territory> ters, Territory startTer)
130
HashMap<Territory, Integer> result = new HashMap<Territory, Integer>();
131
for (Territory ter : m_map.getTerritories())
133
result.put(ter, GetTerScore(ter));
138
* Returns the score of the best match that matches this territory
140
private Integer GetTerScore(Territory ter)
142
int bestMatchingScore = Integer.MAX_VALUE;
143
for(Match<Territory> match : m_matches.keySet())
145
int score = m_matches.get(match);
146
if(score < bestMatchingScore) //If this is a 'better' match
150
bestMatchingScore = score;
154
return bestMatchingScore;