/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package cz.cuni.mff.abacs.burglar.logics.planning;
import cz.cuni.amis.planning4j.*;
import cz.cuni.amis.planning4j.external.ExternalPlanner;
import cz.cuni.amis.planning4j.external.impl.itsimple.ItSimplePlannerExecutor;
import cz.cuni.amis.planning4j.external.impl.itsimple.ItSimplePlannerInformation;
import cz.cuni.amis.planning4j.external.impl.itsimple.PlannerListManager;
import cz.cuni.amis.planning4j.external.plannerspack.PlannersPackUtils;
import cz.cuni.amis.planning4j.impl.StringDomainProvider;
import cz.cuni.amis.planning4j.impl.StringProblemProvider;
import cz.cuni.amis.planning4j.pddl.PDDLRequirement;
import cz.cuni.mff.abacs.burglar.logics.GameMap;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Guard;
import cz.cuni.mff.abacs.burglar.logics.planning.instructions.Instruction;
import cz.cuni.mff.abacs.burglar.visual.VisualBurglar;
import java.io.*;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Planninf4J planner library integration handler.
*
* @author abacs
*/
public class Planning4JHandler extends PlannerHandlerBase implements PlannerHandler {
/** Selected planner element. */
private ItSimplePlannerInformation _plannerElement = null;
// -------------------------------------------------------------------------
@Override
public List<Instruction> solveBurglarProblem(
GameMap map,
List<Integer> roomsToEnter,
List<Integer> roomsToAvoid
) {
String problemString = PDDLGenerator.generateBurglarProblem(map, roomsToEnter, roomsToAvoid);
return solveProblem(map, problemString);
}
@Override
public List<Instruction> solveGuardProblem(Guard guard, GameMap map, List<Integer> roomsToEnter, List<Integer> roomsToAvoid) {
String problemString = PDDLGenerator.generateGuardProblem(map, guard, roomsToEnter, roomsToAvoid);
return solveProblem(map, problemString);
}
// -------------------------------------------------------------------------
/**
* Solves the problem handed over as a string parameter.
*
* @param map world in witch the results can be interpreted.
* @param problemString problem string
* @return list of resulted instructions.
*/
private List<Instruction> solveProblem(
GameMap map,
String problemString
) {
getPlanner();
List<Instruction> instructions = new LinkedList<Instruction>();
String domainString = readInFile(VisualBurglar.PATH_PLANNING + SUBPATH_DOMAINS + "agent.pddl");
IDomainProvider domainProvider = new StringDomainProvider(domainString);
IProblemProvider problemProvider = new StringProblemProvider(problemString);
//This will be true with default maven project. If you have extracted the planners elsewhere, change it
File plannersDirectory = new File("target"); // TODO ?????
try{
IPlanner planner =
new ExternalPlanner(
new ItSimplePlannerExecutor(this._plannerElement, plannersDirectory)
);
IPlanningResult result = planner.plan(domainProvider, problemProvider);
if(result.isSuccess() == false){
System.out.println("Planning4JHandler: No solution found.");
return instructions;
}else{
translateInstructions(result.getPlan(), map, instructions);
for(ActionDescription action : result.getPlan()){
System.out.println(action.getName());
}
}
}catch(PlanningException ex){
System.out.println("Planning4JHandler: Exception during planning.");
ex.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
return instructions;
}
/** Selects a planner to be used based on the requirements. */
private boolean getPlanner() {
PlannerListManager plannerManager = PlannersPackUtils.getPlannerListManager();
// Let the engine suggest as a planner that supports strips and runs on current platform
Set<PDDLRequirement> requirements = EnumSet.of(PDDLRequirement.STRIPS, PDDLRequirement.TYPING);
List<ItSimplePlannerInformation> suggestedPlanners = plannerManager.suggestPlanners(requirements);
if(suggestedPlanners.isEmpty()){
System.out.println("Planning4JHandler: No planner found for current platform.");
return false;
}
//let's use the first suggested planner
this._plannerElement = suggestedPlanners.get(0);
return true;
}
/**
* Reads in a file.
*
* Intended to be used to read in the PDDL domain file.
*
* @param filename
* @return
*/
private static String readInFile(String filename) {
StringBuilder builder = new StringBuilder();
try{
// Open the file that is the first
// command line parameter
FileInputStream fstream = new FileInputStream(filename);
// Get the object of DataInputStream
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
// Read File Line By Line
while ((strLine = br.readLine()) != null){
// Print the content on the console
builder.append(strLine);
builder.append("\n");
}
// Close the input stream
in.close();
}catch (Exception e){ // Catch exception if any
System.err.println("Planning4JHandler: Error: " + e.getMessage());
}
return builder.toString();
}
/**
* Goes though the input and translates
* the instructions from planner specific format.
*
* @param actions
* @param map
* @param instructions
* @throws IOException
*/
private static void translateInstructions(
List<ActionDescription> actions,
GameMap map,
List<Instruction> instructions
) throws IOException {
for(ActionDescription action : actions){
translateInstruction(action, map, instructions);
}
}
/**
* Translates a single instruction.
*
* @param action action to translate
* @param map
* @param instructions
* @return true if agent just entered a new room.
*/
private static boolean translateInstruction(
ActionDescription action,
GameMap map,
List<Instruction> instructions
) {
// translate the instruction code:
Instruction.code code = getInstructionCode(action.getName());
List<String> params = action.getParameters();
// get the agent:
int agentId = -1;
try{
agentId = Integer.parseInt(params.get(0));
}catch(NumberFormatException e){
System.err.println(e.toString());
e.printStackTrace();
}
int positionId;
switch(code){
case COMPLEX_MOVE:
int moveToId = Integer.parseInt(params.get(2));
instructions.add(new Instruction(code, agentId, moveToId));
return false;
case ENTER_DOOR:
int doorId = Integer.parseInt(params.get(1));
instructions.add(new Instruction(code, agentId, doorId));
return true;
case OPEN:
case CLOSE:
case USE:
positionId = Integer.parseInt(params.get(1));
instructions.add(new Instruction(code, agentId, positionId));
return false;
case UNLOCK:
case LOCK:
case PICK_UP:
positionId = Integer.parseInt(params.get(1));
int itemId = Integer.parseInt(params.get(2));
instructions.add(
new Instruction(code, agentId, positionId, itemId)
);
return false;
default:
}
return false;
}
}