package puzzledice;
import java.awt.Component;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
// Abstract class representing a single PuzzleBlock
// Subclasses implement specifics for different types of puzzle blocks
public abstract class PuzzleBlock {
protected String _name;
// Normally I would make any kind of type variable an enum, but the primary purpose of this field is to give
// a textual representation to the user of what kind of puzzle this is, so I'm making it a string.
protected String _type;
protected Component _editUI;
protected Object _graphCell;
protected String _outputTempName = "";
// Accessors and Mutators
public String getName()
{
return _name;
}
public void setName(String newName)
{
_name = newName;
}
//Get this type of Puzzle Blocks specific UI component that's used to edit this puzzle block.
public Component getUI()
{
return _editUI;
}
// The type of this puzzle, represented as a string
public String getPuzzleType()
{
return _type;
}
// Called when the puzzle block is selected. Used to update the puzzle blocks ui
public abstract void update();
// Overridden by subclasses
// When a puzzle block is deleted, need to allow all puzzle blocks to remove references to it
// By default, these functions should silently do nothing
public void maybeRemoveRef(PuzzleBlock blockToRemove) {}
// Similarly when an area is removed
public void maybeRemoveRef(AreaBlock areaToRemove) {}
// Called when a block is deleted to give it a chance to clean up its references (particularly useful for puzzle blocks that connect to areas)
public void onDelete() {}
// Used to construct the text view
public String getTextualDescription() {return "";}
public String getOutputTempName() {return _outputTempName;}
public void setGraphCell(Object graphCell)
{
_graphCell = graphCell;
}
public Object getGraphCell()
{
return _graphCell;
}
public String getCellStyle()
{
return "fillcolor=#BAD0EF";
}
@Override
public String toString()
{
return _name;
}
// Function for finishing up the loading process.
public void attachBlocksToName(Map<String, AreaBlock> areas, Map<String, PuzzleBlock> puzzles)
{
}
// Functions used to detect cycles
public PuzzleBlock[] getPuzzleInputs()
{
return new PuzzleBlock[0];
}
public AreaBlock[] getAreaInputs()
{
return new AreaBlock[0];
}
public boolean canReachBlockBackwards(Object target)
{
Set<Object> closedBlocks = new HashSet<Object>();
Queue<Object> agenda = new LinkedList<Object>();
agenda.add(this);
while (agenda.size() > 0) {
Object block = agenda.remove();
if (block == target)
return true;
closedBlocks.add(block);
if (block instanceof PuzzleBlock) {
PuzzleBlock pBlock = (PuzzleBlock)block;
for (PuzzleBlock neighbor : pBlock.getPuzzleInputs()) {
if (!closedBlocks.contains(neighbor))
agenda.add(neighbor);
}
for (AreaBlock neighbor : pBlock.getAreaInputs()) {
if (!closedBlocks.contains(neighbor))
agenda.add(neighbor);
}
}
else if (block instanceof AreaBlock) {
AreaBlock aBlock = (AreaBlock)block;
for (DoorUnlockBlock neighbor : aBlock.getLockedDoorList()) {
if (!closedBlocks.contains(neighbor))
agenda.add(neighbor);
}
for (AreaBlock neighbor : aBlock.getInputAreas()) {
if (!closedBlocks.contains(neighbor))
agenda.add(neighbor);
}
}
}
return false;
}
public String toXML()
{
return "<PuzzleBlock name=\"" + _name + "\" />";
}
}