package nl.tudelft.bw4t.environmentstore.editor.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import nl.tudelft.bw4t.map.BlockColor;
import nl.tudelft.bw4t.map.NewMap;
import nl.tudelft.bw4t.map.Zone;
/**
* A utility class containing a BFS algorithm to verify if
* a created map is solvable or not.
*/
public final class SolvabilityAlgorithm {
/**
* Store the frequency of the sequence colors here.
*/
private static HashMap<BlockColor, Integer> sequenceFreq;
/**
* Flag to check if the start zone has been reached.
*/
private static boolean startZoneReachable = false;
/**
* A private constructor.
*/
private SolvabilityAlgorithm() {
}
/**
* First call to the BFS solvability algorithm, starts at the dropzone.
* @param map The map to check sovability for.
* @return Whether or not the map is solvable.
*/
public static String mapIsSolvable(NewMap map) {
sequenceFreq = new HashMap<BlockColor, Integer>();
startZoneReachable = false;
List<BlockColor> seq = map.getSequence();
for (BlockColor bc : seq) {
addToMap(sequenceFreq, bc);
}
HashMap<Zone, Boolean> visited = new HashMap<Zone, Boolean>();
HashMap<BlockColor, Integer> frequencies = new HashMap<BlockColor, Integer>();
Zone dropZone = map.getZone(Zone.DROP_ZONE_NAME);
if (dropZone == null) {
return "There is no drop zone.";
}
List<Zone> neighbours = dropZone.getNeighbours();
visited.put(dropZone, true);
return mapIsSolvable(visited, frequencies, neighbours);
}
/**
* The recursive call, checks a new level.
* @param visited Keeps track of the nodes that have been visited.
* @param freq Keeps track of the frequency of the found colors.
* @param level The level to search in.
* @return Whether or not the map is solvable.
*/
private static String mapIsSolvable(HashMap<Zone, Boolean> visited,
HashMap<BlockColor, Integer> freq, List<Zone> level) {
if (level.size() == 0) {
return checkAccesibleBlocks(freq);
}
List<Zone> newLevel = new ArrayList<Zone>();
for (Zone z : level) {
if (z.getName().matches("StartZone.*")) {
startZoneReachable = true;
}
visited.put(z, true);
for (BlockColor bc : z.getBlocks()) {
addToMap(freq, bc);
}
for (Zone neigh : z.getNeighbours()) {
if (visited.get(neigh) == null) {
visited.put(neigh, true);
newLevel.add(neigh);
}
}
}
return mapIsSolvable(visited, freq, newLevel);
}
/**
* Compare the frequencies of the accessible blocks with those of
* the block sequence.
* @param map The hashmap with found frequencies.
* @return true iff the given map indicates that there are at least
* as many blocks in the map, as well as accessible, as in the frequency map
* of the color sequence.
*/
private static String checkAccesibleBlocks(HashMap<BlockColor, Integer> map) {
for (BlockColor bc : sequenceFreq.keySet()) {
if ((map.get(bc) == null && sequenceFreq.get(bc) != null)
|| sequenceFreq.get(bc) > map.get(bc)) {
return "Not all necessary blocks can be reached from the drop zone.";
}
}
if (!startZoneReachable) {
return "The start zone isn't reachable";
}
return null;
}
/**
* Adds a new value to the map.
* @param map The hashmap to add the value to.
* @param bc The color.
*/
private static void addToMap(HashMap<BlockColor, Integer> map, BlockColor bc) {
if (map.get(bc) == null) {
map.put(bc, 1);
} else {
map.put(bc, map.get(bc) + 1);
}
}
}