/**
*
*/
package cz.cuni.mff.abacs.burglar.logics;
import aStarLibrary.AStar;
import aStarLibrary.Node;
import cz.cuni.mff.abacs.burglar.logics.objects.BaseInterface;
import cz.cuni.mff.abacs.burglar.logics.objects.Room;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.*;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Inventory;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Item;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Key;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Uniform;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.*;
import cz.cuni.mff.abacs.burglar.logics.planning.instructions.Instruction;
import cz.cuni.mff.abacs.burglar.visual.VisualBurglar;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* A game map that executes instructions on it's agents.
*
* @author abacs
*
*/
public class ExecutingMap extends DataMap implements GameMap {
// properties:
/** List of agents that have no planned action sequence and need to create one. */
protected List<Agent> _agentsNeedReplan = new LinkedList<Agent>();
// -------------------------------------------------------------------------
// constructors:
/**
*
*
* @param nextFreeId
*/
public ExecutingMap(int nextFreeId, int requiredTrapRoomCount) {
super(nextFreeId, requiredTrapRoomCount);
}
// -------------------------------------------------------------------------
/**
* Returns the full list of A* nodes from the layout.
*
*/
public List<Node> getNodes() {
List<Node> nodes = new LinkedList<Node>();
for(Position pos : this.getPositions()){
nodes.add(((BasePosition)pos).getNode());
}
return nodes;
}
// -------------------------------------------------------------------------
/**
* Creates a guard agent and places him to protect the selected rooms.
*
* @param roomIds identifiers of the rooms to protect.
*/
public void addGuardPatrol(List<Integer> roomIds) {
Position position = this.getFloorPositions(roomIds.get(0)).get(0);
Guard guard = new Guard(this.getNextID(), position, roomIds, this);
BeliefBase knowledge = guard.getBeliefBase();
knowledge.seenFromNear(this.getOperablePositions());
// add the uniform:
Uniform clothes = new Uniform(this.getNextID(), this);
guard.addItem(clothes);
this.addItem(clothes);
this.addAgent(guard);
}
// -------------------------------------------------------------------------
/**
* Adds the instructions to the corresponding agents.
*
* The instructions are translated to simple instructions if needed.
*
* Unchecked function. Agent code must be correct.
*/
@Override
public void addInstructions(List<Instruction> instructions) {
// generate simple instructions if needed:
List<Instruction> simpleInstructions =
this.simplifyInstructions(instructions);
// clear the agent instructions that are in the instruction list:
List<Integer> agents = new LinkedList<Integer>();
for(Instruction instr : simpleInstructions){
if(agents.contains(instr._agentId) == false){
agents.add(instr._agentId);
}
}
for(int agentId : agents)
this.getAgent(agentId).clearInstructions();
// add instructions to their executing agent:
for(Instruction instr : simpleInstructions){
assert(this.getAgent(instr._agentId) != null) : "invalid Agent Id in Instruction";
this.getAgent(instr._agentId).addInstruction(instr);
}
}
@Override
public void clearInstructions() {
for(Agent agent : this.getAgents()){
agent.clearInstructions();
}
}
@Override
public List<Instruction> getAgentInstructions(int agentId) {
return this.getAgent(agentId).getInstructions();
}
@Override
public List<Instruction> getAgentInstructions(Agent agent) {
return agent.getInstructions();
}
@Override
public boolean instructionListsMatch(
List<Instruction> a,
List<Instruction> b
) {
if(a.size() != b.size()){
return false;
}
for(int i = 0; i < a.size(); i++){
Instruction instrA = a.get(i);
Instruction instrB = b.get(i);
if(instrA.matches(instrB) == false)
return false;
}
return true;
}
@Override
public List<Room> instructionsLeadToTraps(List<Instruction> list) {
List<Room> trapRooms = this.getTrapRooms();
int burglarId = this.getBurglar().getId();
List<Room> triggeredRooms = new ArrayList<Room>();
for(Instruction instr : list){
// if the agent is the burglar and the current room contains a trap
// the room is added to the triggered trap list
// all room can be only once in the triggered list.
if(instr._agentId != burglarId)
continue;
Position position = this.getPosition(instr._subjectId);
if(position.isTypeOf(BaseInterface.Type.DOOR)){
Door door = (Door)position;
Room rooms[] = door.getRooms();
if(trapRooms.contains(rooms[0]) &&
triggeredRooms.contains(rooms[0]) == false)
triggeredRooms.add(rooms[0]);
if(trapRooms.contains(rooms[1]) &&
triggeredRooms.contains(rooms[1]) == false)
triggeredRooms.add(rooms[1]);
}else{
Room room = position.getRoom();
if(trapRooms.contains(room) &&
triggeredRooms.contains(room) == false)
triggeredRooms.add(room);
}
}
return triggeredRooms;
}
// -------------------------------------------------------------------------
/**
* Executes a single step for each agent.
*/
@Override
public void executeStep() {
for(Agent agent : this.getAgents()){
// only active agents should perform actions:
if(agent.isInState(Agent.State.WELL) == false)
continue;
// execute the stepp:
boolean agentResult = this.executeStep(agent);
// if room contains active vender, go to it:
GoalBase agentGoals = agent.getGoals();
if(agentGoals.hasVenderToVisit() == false){
Position vender = this.getActiveVender(agent.getRoomId());
if(vender != null){
agentGoals.setVenderIdToVisit(vender.getId());
agentResult = false;
}
}
// if agent has no more instructions,
// or the instruction failed
// add the agent to the replanning list.
if(agentResult == false || agent.hasInstructions() == false){
agent.clearInstructions();
this._agentsNeedReplan.add(agent);
}
}
}
/**
* Executes the first instruction in the agent's instruction stack.
*
* Stunned agents remain immobile.
*
* @param agent the agent to act.
* @return success indicator.
*/
protected boolean executeStep(Agent agent) {
// if the agent is a stunned guard, do nothing:
if(agent.isInState(Agent.State.STUNNED)){
return true;
}
return this.execute(agent.popFirstInstruction());
}
/**
* Executes a single instruction.
*
* @param instruction instruction object to execute
* @return success indicator
*/
protected boolean execute(Instruction instruction) {
if(instruction == null)
return false;
boolean ret = false;
Agent agent = this.getAgent(instruction._agentId);
assert(agent != null);
switch(instruction._code){
case MOVE:
ret = this.move(instruction._agentId, instruction._subjectId);
break;
case OPEN:
ret = this.open(instruction._agentId, instruction._subjectId);
break;
case CLOSE:
ret = this.close(instruction._agentId, instruction._subjectId);
break;
case UNLOCK:
ret = this.unlock(
instruction._agentId,
instruction._subjectId,
instruction._itemId
);
break;
case LOCK:
ret = this.lock(
instruction._agentId,
instruction._subjectId,
instruction._itemId
);
break;
case PICK_UP:
ret = this.pickUp(
instruction._agentId,
instruction._subjectId,
instruction._itemId
);
break;
case TAKE_CLOTHES:
this.takeClothes(
instruction._agentId,
instruction._subjectId
);
break;
case USE:
this.use(instruction._agentId, instruction._subjectId);
break;
}
return ret;
}
/**
* Agents look around in their room.
*
* They may discover new positions or agents in the room.
* If the positions were already known, it does not change them.
*
* @return new information indicator
*/
@Override
public boolean lookAround() {
boolean result = false;
for(Agent agent : this.getAgents()) {
boolean agentResult = agent.lookAround();
result = agentResult || result;
if(VisualBurglar.FLAG_REPLAN_ON_NEW_KNOWLEDGE && agentResult){
agent.clearInstructions();
this._agentsNeedReplan.add(agent);
}
}
return result;
}
// -------------------------------------------------------------------------
/** Returns whether the map has agents that need replanning. */
@Override
public boolean needsReplanning() {
return ! this._agentsNeedReplan.isEmpty();
}
/**
* Returns the list of agents that require replanning.
*
* @return
*/
@Override
public List<Agent> getAgentsToReplan() {
return new ArrayList<Agent>(this._agentsNeedReplan);
}
@Override
public void clearAgentsToReplan() {
this._agentsNeedReplan.clear();
}
@Override
public void addAgentToReplan(Agent agent) {
this._agentsNeedReplan.add(agent);
}
@Override
public void removeAgentToReplan(Agent agent) {
this._agentsNeedReplan.remove(agent);
}
// -------------------------------------------------------------------------
@Override
public List<Position> nodesToPositions(List<Node> nodes) {
List<Position> positions = new ArrayList<Position>();
for(Node node : nodes)
positions.add(this.getPosition(node));
return positions;
}
// -------------------------------------------------------------------------
/**
* Replaces a complex instruction with simple ones.
*
* Used to break apart movement instructions.
*
* Destructive operation on the agent parameter.
* @param testAgent a test copy of the agent that will execute the instructions.
* @param instruction instruction to process.
* @return resulted instruction list
*/
protected List<Instruction> simplifyInstruction(
Instruction instruction,
Agent testAgent
) {
List<Instruction> result = new ArrayList<Instruction>();
Instruction simpleInstr;
switch(instruction._code){
case ENTER_DOOR:
simpleInstr =
new Instruction(
Instruction.code.MOVE,
instruction._agentId,
instruction._subjectId
);
result.add(simpleInstr);
testAgent.setPosition(instruction._subjectId);
break;
case COMPLEX_MOVE:
Position subjectPos = this.getPosition(instruction._subjectId);
if(subjectPos == null)
subjectPos = this.getAgent(instruction._subjectId).getPosition();
List<Position> path =
this.getSimplePath(
testAgent.getPosition(),
subjectPos
);
for(Position position : path){
simpleInstr =
new Instruction(
Instruction.code.MOVE,
instruction._agentId,
position.getId()
);
result.add(simpleInstr);
testAgent.setPosition(position);
}
break;
default:
result.add(instruction);
}
return result;
}
/**
* Replaces complex instructions with simple ones.
*
* Instructions have to contain a single agent.
*
* @param instructions list of instructions to process.
* @return resulted instruction list.
*/
protected List<Instruction> simplifyInstructions(
List<Instruction> instructions
) {
List<Instruction> result = new ArrayList<Instruction>();
if(instructions.isEmpty())
return result;
// creates a test copy that will be destroyed later;
Agent testAgent = this.getAgent(instructions.get(0)._agentId).copy(this);
for(Instruction instr : instructions){
result.addAll(this.simplifyInstruction(instr, testAgent));
}
return result;
}
/**
* Generates a path between two positions.
*
* The result does not include the start, neither
* the end position. It does not calculate with doors, but
* it does use only walkable positions.
*
* @param from starting position
* @param to movement aim
* @return list of positions from start to aim in correct order.
*/
protected List<Position> getSimplePath(Position from, Position to) {
List<Position> result = new ArrayList<Position>();
if(from.getId() == to.getId())
return result;
// plan the path:
AStar aStar = new AStar();
aStar.nodes.addAll(this.getNodes());
List<Node> nodes =
aStar.getPath(
((BasePosition)from).getNode(),
((BasePosition)to).getNode()
);
// the a* algorithm returns the nodes in oposite order
// we only need the last stepp if it's not the target position
for(int index = nodes.size() - 2; index > 0; index--){
result.add(this.getPosition(nodes.get(index)));
}
Position lastPosition = this.getPosition(nodes.get(0));
if(lastPosition.getId() != to.getId()){
result.add(lastPosition);
}
return result;
}
// -------------------------------------------------------------------------
@Override
public boolean isBurglarTrapped() {
Burglar burglar = this.getBurglar();
// burglar can hide from cameras
if(burglar.isDisguised() == false){
for(Position pos : this.getTrapPositions()){
if(pos.isTypeOf(BaseInterface.Type.CAMERA)){
Camera camera = (Camera)pos;
if(
camera.isActive() &&
burglar.isInRoom(camera.getRoomId())
)
return true;
}
}
}
for(Guard guard : this.getGuards()){
if(
guard.isInState(Guard.State.WELL) &&
guard.getRoomId() == burglar.getRoomId()
)
return true;
}
return false;
}
// -------------------------------------------------------------------------
// instructions:
/**
* Executes a directional move instruction.
*
* @param agentId acting agent
* @param direction movement direction
* @return success of the operation
*/
public boolean move(int agentId, Direction direction) {
return this.move(this.getAgent(agentId), direction);
}
/**
* Executes a directional move instruction.
*
* @param agent acting agent
* @param direction movement direction
* @return success of the operation
*/
boolean move(Agent agent, Direction direction) {
Position aim = this.getNeighbour(agent.getPosition(), direction);
if(aim == null)
return false;
return agent.moveTo(aim);
}
/**
* Executes a simple move instruction.
*
* @param agentId acting agent
* @param walkableId walkable position to step on
* @return success of the operation
*/
boolean move(int agentId, int walkableId) {
return this.move(this.getAgent(agentId), this.getPosition(walkableId));
}
/**
* Executes a simple move instruction.
*
* @param agent acting agent
* @param walkable walkable position to step on
* @return success of the operation
*/
private boolean move(Agent agent, Position walkable) {
return agent.moveTo(walkable);
}
/**
* Executes an open instruction.
*
* @param agentId acting agent
* @param lockableId lockable position to open
* @return success of the operation
*/
private boolean open(int agentId, int lockableId) {
return this.open(
this.getAgent(agentId),
(Lockable)this.getPosition(lockableId)
);
}
/**
* Executes an open instruction.
*
* @param agent acting agent
* @param lockable lockable position to open
* @return success of the operation
*/
private boolean open(Agent agent, Lockable lockable) {
return agent.open(lockable);
}
/**
* Executes a close instruction.
*
* @param agentId acting agent
* @param lockableId lockable position to close
* @return success of the operation
*/
private boolean close(int agentId, int lockableId) {
return this.close(
this.getAgent(agentId),
(Lockable) this.getPosition(lockableId)
);
}
/**
* Executes a close instruction.
*
* @param agent acting agent
* @param lockable lockable position to close
* @return success of the operation
*/
private boolean close(Agent agent, Lockable lockable) {
return agent.close(lockable);
}
/**
* Executes an unlock instruction.
*
* @param agentId acting agent
* @param lockableId lockable position to unlock
* @param keyId key to use
* @return success of the operation
*/
private boolean unlock(int agentId, int lockableId, int keyId) {
return this.unlock(
this.getAgent(agentId),
(Lockable) this.getPosition(lockableId),
(Key) this.getItem(keyId)
);
}
/**
* Executes an unlock instruction.
*
* @param agent acting agent
* @param lockable lockable position to unlock
* @param key key to use
* @return success of the operation
*/
private boolean unlock(Agent agent, Lockable lockable, Key key) {
return agent.unlock(lockable, key);
}
/**
* Executes a lock instruction.
*
* @param agentId acting agent
* @param lockableId lockable position to lock
* @param keyId key to use
* @return success of the operation
*/
private boolean lock(int agentId, int lockableId, int keyId) {
return this.lock(
this.getAgent(agentId),
(Lockable) this.getPosition(lockableId),
(Key) this.getItem(keyId)
);
}
/**
* Executes a lock instruction.
*
* @param agent acting agent
* @param lockable lockable position to lock
* @param key key to use
* @return success of the operation
*/
private boolean lock(Agent agent, Lockable lockable, Key key) {
return agent.lock(lockable, key);
}
/**
* Executes a pick up instruction.
*
* @param agentId acting agent
* @param inventoryId from where to pick up the object
* @param itemId item to pick up
* @return success of the operation
*/
private boolean pickUp(int agentId, int inventoryId, int itemId) {
return this.pickUp(
this.getAgent(agentId),
(Inventory) this.getPosition(inventoryId),
this.getItem(itemId)
);
}
/**
* Executes a pick up instruction.
*
* @param agent acting agent
* @param inventory from where to pick up the object
* @param item item to pick up
* @return success of the operation
*/
private boolean pickUp(Agent agent, Inventory inventory, Item item) {
return agent.pickUp(inventory, item);
}
/**
* Executes a take clothes instruction.
*
* @param agentId acting agent
* @param subjectAgentId from where to pick up the clothes
* @return success of the operation
*/
private boolean takeClothes(int agentId, int subjectAgentId) {
return this.takeClothes(
this.getAgent(agentId),
this.getAgent(subjectAgentId)
);
}
/**
* Executes a take clothes instruction.
*
* @param agent acting agent
* @param subjectAgent from where to pick up the clothes
* @return success of the operation
*/
private boolean takeClothes(Agent agent, Agent subjectAgent) {
int id = subjectAgent.getItemIdOfType(BaseInterface.Type.UNIFORM);
Item item;
if(id != -1)
item = subjectAgent.removeItem(id);
else{
item = new Uniform(this.getNextID(), this);
this.addItem(item);
}
agent.addItem(item);
System.out.println(
"- " + agent.getId() +
": Clothes taken from " + subjectAgent.getId()
);
return true;
}
/**
* Executes a use instruction.
*
* @param agentId acting agent
* @param venderId vender position to use
* @return success of the operation
*/
private boolean use(int agentId, int venderId) {
return this.use(
this.getAgent(agentId),
(Vender) this.getPosition(venderId)
);
}
/**
* Executes a use instruction.
*
* @param agent acting agent
* @param lockable vender position to use
* @return success of the operation
*/
private boolean use(Agent agent, Vender vender) {
return agent.use(vender);
}
}