package iamrescue.execution;
import iamrescue.agent.ISimulationTimer;
import iamrescue.belief.IAMWorldModel;
import iamrescue.execution.command.ClearCommand;
import iamrescue.execution.command.DigOutCommand;
import iamrescue.execution.command.ExtinguishCommand;
import iamrescue.execution.command.IIAMAgentCommand;
import iamrescue.execution.command.LoadCommand;
import iamrescue.execution.command.MoveCommand;
import iamrescue.execution.command.RandomMoveCommand;
import iamrescue.execution.command.RandomStepCommand;
import iamrescue.execution.command.RestCommand;
import iamrescue.execution.command.UnloadCommand;
import iamrescue.routing.util.ISpeedInfo;
import iamrescue.util.HumanMovementUtility;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javolution.util.FastSet;
import org.apache.log4j.Logger;
import rescuecore2.config.Config;
import rescuecore2.connection.Connection;
import rescuecore2.connection.ConnectionException;
import rescuecore2.messages.AbstractCommand;
import rescuecore2.messages.Message;
import rescuecore2.standard.entities.Area;
import rescuecore2.standard.entities.Building;
import rescuecore2.standard.entities.Human;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.messages.AKClear;
import rescuecore2.standard.messages.AKExtinguish;
import rescuecore2.standard.messages.AKLoad;
import rescuecore2.standard.messages.AKMove;
import rescuecore2.standard.messages.AKRescue;
import rescuecore2.standard.messages.AKRest;
import rescuecore2.standard.messages.AKUnload;
import rescuecore2.worldmodel.EntityID;
public class RC2ExecutionService implements IExecutionService {
private static final Logger LOGGER = Logger
.getLogger(RC2ExecutionService.class);
private Connection connection;
private EntityID id;
private AbstractCommand enqueuedCommand;
private int maxPower;
private ISimulationTimer timer;
private IIAMAgentCommand lastEnqueuedCommand;
private IIAMAgentCommand lastSubmittedCommand;
private IAMWorldModel worldModel;
private int lastRandomMoveTime = -1;
private int consecutiveRandomSteps = 0;
private int sameMoveCounter = 0;
private ISpeedInfo speedInfo;
private boolean iamBuilding;
private static final String MAX_POWER_KEY = "fire.extinguish.max-sum";
// Proportion of average speed that is classed as a no-move (over all
// positions visited).
private static final double MAX_NO_MOVE_PROPORTION = 0.2;
// Proportion of average speed that is classed as a no-move (over absolute
// distance travelled).
private static final double MAX_NO_MOVE_ABSOLUTE_PROPORTION = 0.1;
// When stuck, how many random steps should be attempted first before
// starting to visit neighbours
private static final int STEP_TRIALS = 3;
// After how many steps of submitting the same move should I start to move
// randomly? If set to X, the agent will move randomly on the Xth time the
// same move is sent.
private static final int SAME_MOVE_THRESHOLD = 8;
// How many samples to take for finding a random side-step
private static final int MAX_SAMPLES = 30;
// Probability of taking a random move once the necessary conditions hold
private static final double RANDOM_MOVE_PROBABILITY = 1;
public RC2ExecutionService(EntityID id, Connection connection,
Config config, ISimulationTimer timer, IAMWorldModel worldModel,
ISpeedInfo speedInfo) {
this.connection = connection;
this.worldModel = worldModel;
this.id = id;
maxPower = config.getIntValue(MAX_POWER_KEY);
this.timer = timer;
setSpeedInfo(speedInfo);
iamBuilding = worldModel.getEntity(id) instanceof Building;
}
public void setSpeedInfo(ISpeedInfo speedInfo) {
this.speedInfo = speedInfo;
}
private void sendMessage(Message message) {
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending command: " + message);
}
connection.sendMessage(message);
} catch (ConnectionException e) {
e.printStackTrace();
}
}
@Override
public void execute(DigOutCommand rescueCommand) {
AKRescue rescue = new AKRescue(id, timer.getTime(), rescueCommand
.getObjectToDigOut().getID());
this.lastEnqueuedCommand = rescueCommand;
enqueueCommand(rescue);
}
@Override
public void execute(UnloadCommand unloadCommand) {
AKUnload unload = new AKUnload(id, timer.getTime());
this.lastEnqueuedCommand = unloadCommand;
enqueueCommand(unload);
}
@Override
public void execute(LoadCommand loadCommand) {
AKLoad load = new AKLoad(id, timer.getTime(), loadCommand
.getCivilianToLoad().getID());
this.lastEnqueuedCommand = loadCommand;
enqueueCommand(load);
}
@Override
public void execute(ClearCommand clearCommand) {
AKClear clear = new AKClear(id, timer.getTime(), clearCommand
.getBlockadeToClear().getID());
this.lastEnqueuedCommand = clearCommand;
enqueueCommand(clear);
}
@Override
public void execute(ExtinguishCommand extinguishCommand) {
Building building = extinguishCommand.getBuildingToExtinguish();
double percentage = extinguishCommand.getPercentageOfFullPower();
double power = maxPower * percentage;
int time = timer.getTime();
EntityID bId = building.getID();
AKExtinguish ex = new AKExtinguish(id, time, bId, (int) power);
this.lastEnqueuedCommand = extinguishCommand;
enqueueCommand(ex);
}
@Override
public void execute(MoveCommand moveCommand) {
AKMove move = new AKMove(id, timer.getTime(), moveCommand.getPath()
.getLocations(), moveCommand.getPath().getEndPosition().getX(),
moveCommand.getPath().getEndPosition().getY());
this.lastEnqueuedCommand = moveCommand;
enqueueCommand(move);
}
private void enqueueMove(MoveCommand moveCommand) {
AKMove move = new AKMove(id, timer.getTime(), moveCommand.getPath()
.getLocations(), moveCommand.getPath().getEndPosition().getX(),
moveCommand.getPath().getEndPosition().getY());
this.lastEnqueuedCommand = moveCommand;
this.enqueuedCommand = move;
}
private void enqueueCommand(AbstractCommand command) {
assert enqueuedCommand == null : "Warning : multiple commands submitted in the same timestep";
this.enqueuedCommand = command;
}
@Override
public void flushCommands() {
// enqueuedCommand = generateRandomStep(MAX_SAMPLES);
if (enqueuedCommand == null) {
if (!iamBuilding) {
LOGGER.error("Warning : agent has not submitted "
+ "any commands in this timestep");
}
} else {
boolean sentMove = false;
if (timer.getTime() > 3 && needRandomMove()) {
// Do not step randomly twice in a row.
if (lastRandomMoveTime != timer.getTime() - 1) {
sentMove = sendRandomMove();
}
}
if (!sentMove) {
sendMessage(enqueuedCommand);
this.lastSubmittedCommand = lastEnqueuedCommand;
}
enqueuedCommand = null;
}
}
private AKMove generateRandomStep(int maxTries) {
Human me = (Human) worldModel.getEntity(id);
EntityID position = me.getPosition();
// Area I am on
Area area = (Area) worldModel.getEntity(position);
Shape shape = area.getShape();
Rectangle bounds = shape.getBounds();
boolean done = false;
int counter = 1;
while (!done && counter <= maxTries) {
int x = (int) (bounds.getMinX() + Math.random()
* (bounds.getMaxX() - bounds.getMinX()));
int y = (int) (bounds.getMinY() + Math.random()
* (bounds.getMaxY() - bounds.getMinY()));
if (shape.contains(new Point2D.Float(x, y))) {
done = true;
List<EntityID> pathLocations = new ArrayList<EntityID>();
pathLocations.add(position);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Generated random move to " + x + ", " + y);
}
return new AKMove(id, timer.getTime(), pathLocations, x, y);
/*
* this.lastEnqueuedCommand = null; this.enqueuedCommand = move;
*/
//
}
counter++;
}
return null;
}
private AKMove generateRandomNeighbourStep() {
Human me = (Human) worldModel.getEntity(id);
EntityID position = me.getPosition();
// Area I am on
Area area = (Area) worldModel.getEntity(position);
List<EntityID> neighbours = area.getNeighbours();
if (neighbours.size() == 0) {
LOGGER.error("Could not create random move on "
+ area.getFullDescription());
return null;
} else {
Set<EntityID> tried = new FastSet<EntityID>();
EntityID target = null;
boolean ignore = false;
List<EntityID> pathLocations;
do {
ignore = false;
target = neighbours.get((int) (Math.random() * neighbours
.size()));
pathLocations = new ArrayList<EntityID>();
pathLocations.add(position);
pathLocations.add(target);
tried.add(target);
StandardEntity entity = worldModel.getEntity(target);
if (entity instanceof Building) {
Building b = (Building) entity;
if (b.isFierynessDefined() && b.getFieryness() >= 1
&& b.getFieryness() <= 3) {
ignore = true;
}
}
} while (ignore && tried.size() < neighbours.size());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Generated random move to neighbour " + target);
}
return new AKMove(id, timer.getTime(), pathLocations);
}
}
/**
*
*/
private boolean sendRandomMove() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Submitting random move, "
+ "as enqueued command has been submitted repeatedly: "
+ enqueuedCommand);
}
boolean randomlySteppedLastTime = (lastRandomMoveTime >= timer
.getTime() - 2);
boolean skipStep = randomlySteppedLastTime
&& (consecutiveRandomSteps >= STEP_TRIALS);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Last random step: " + lastRandomMoveTime);
LOGGER.info("Consecutive steps: " + consecutiveRandomSteps);
}
AKMove move = null;
if (!skipStep) {
move = generateRandomStep(MAX_SAMPLES);
if (move != null) {
sendMessage(move);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Sent random step: " + move);
}
}
}
if (move == null) {
// Go to random neighbour
move = generateRandomNeighbourStep();
sendMessage(move);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Sent random neighbour step: " + move);
}
}
if (move != null) {
if (randomlySteppedLastTime) {
consecutiveRandomSteps++;
} else {
consecutiveRandomSteps = 1;
}
lastRandomMoveTime = timer.getTime();
return true;
} else {
return false;
}
}
/**
* @return
*/
private boolean needRandomMove() {
if (timer.getTime() > 4) {
if (lastEnqueuedCommand != null && lastSubmittedCommand != null) {
if (lastEnqueuedCommand instanceof MoveCommand
&& lastSubmittedCommand instanceof MoveCommand) {
MoveCommand lastEnqMove = (MoveCommand) lastEnqueuedCommand;
MoveCommand lastSubMove = (MoveCommand) lastSubmittedCommand;
List<EntityID> enqLocations = lastEnqMove.getPath()
.getLocations();
List<EntityID> subLocations = lastSubMove.getPath()
.getLocations();
if (enqLocations.equals(subLocations)) {
sameMoveCounter++;
return needRandomMoveGivenSameCommand();
} else {
// Did not submit same move
sameMoveCounter = 0;
}
}
}
}
return false;
}
/**
* @return
*/
private boolean needRandomMoveGivenSameCommand() {
boolean conditionsMet = false;
if (sameMoveCounter >= SAME_MOVE_THRESHOLD) {
conditionsMet = true;
} else {
StandardEntity me = worldModel.getEntity(id);
if (me instanceof Human) {
int distanceJustTravelled = HumanMovementUtility
.getDistanceJustTravelled((Human) me, worldModel);
if (distanceJustTravelled == -1) {
LOGGER.error("Could not determine"
+ " distance last travelled");
} else {
if (distanceJustTravelled < MAX_NO_MOVE_PROPORTION
* speedInfo.getDistancePerTimeStep()) {
conditionsMet = true;
}
}
if (!conditionsMet) {
int absoluteDistanceTravelled = HumanMovementUtility
.getAbsoluteDistanceJustTravelled((Human) me,
worldModel, timer);
if (absoluteDistanceTravelled == -1) {
LOGGER.error("Could not determine"
+ " absolute distance " + "last travelled");
} else {
if (absoluteDistanceTravelled < MAX_NO_MOVE_ABSOLUTE_PROPORTION
* speedInfo.getDistancePerTimeStep()) {
conditionsMet = true;
}
}
}
}
}
if (conditionsMet) {
return Math.random() <= RANDOM_MOVE_PROBABILITY;
} else {
return false;
}
}
public IIAMAgentCommand getLastSubmittedCommand() {
return lastSubmittedCommand;
}
@Override
public void performCommand(IIAMAgentCommand command)
throws UnknownCommandException, IllegalStateException {
command.checkValidity();
command.execute(this);
}
@Override
public void execute(RestCommand command) {
this.lastEnqueuedCommand = command;
enqueueCommand(new AKRest(id, timer.getTime()));
}
@Override
public int getLastRandomMoveTime() {
return lastRandomMoveTime;
}
/*
* (non-Javadoc)
*
* @see
* iamrescue.execution.IExecutionService#execute(iamrescue.execution.command
* .RandomMoveCommand)
*/
@Override
public void execute(RandomMoveCommand randomMoveCommand) {
AKMove move = generateRandomNeighbourStep();
this.lastEnqueuedCommand = randomMoveCommand;
if (move == null) {
move = generateRandomStep(MAX_SAMPLES);
if (move == null) {
LOGGER.warn("Could not generate random move. "
+ "Sending rest instead.");
execute(new RestCommand());
return;
}
}
enqueueCommand(move);
}
/*
* (non-Javadoc)
*
* @see
* iamrescue.execution.IExecutionService#execute(iamrescue.execution.command
* .RandomStepCommand)
*/
@Override
public void execute(RandomStepCommand randomStepCommand) {
AKMove move = generateRandomStep(MAX_SAMPLES);
this.lastEnqueuedCommand = randomStepCommand;
if (move == null) {
move = generateRandomNeighbourStep();
if (move == null) {
LOGGER.warn("Could not generate random step. "
+ "Sending rest instead.");
execute(new RestCommand());
return;
}
}
enqueueCommand(move);
}
@Override
public boolean NotSubmittedCommand() {
return enqueuedCommand == null;
}
@Override
public AbstractCommand getEnqueuedCommand() {
return enqueuedCommand;
}
/**
* @return the consecutiveRandomSteps
*/
public int getConsecutiveRandomSteps() {
return consecutiveRandomSteps;
}
}