/*
This file is part of Fantom.
Fantom is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fantom is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fantom. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.matfyz.aai.fantom.client;
import cz.matfyz.aai.fantom.game.Graph;
import cz.matfyz.aai.fantom.game.GraphReadOnly;
import cz.matfyz.aai.fantom.message.ClientType;
import cz.matfyz.aai.fantom.message.Message;
import cz.matfyz.aai.fantom.message.MessageMove;
import cz.matfyz.aai.fantom.message.MessageQuit;
import cz.matfyz.aai.fantom.message.MessageReady;
import cz.matfyz.aai.fantom.message.MessageStart;
import cz.matfyz.aai.fantom.message.MessageUpdate;
import cz.matfyz.aai.fantom.message.MessagingClientBase;
import cz.matfyz.aai.fantom.message.MessageMoveBase.ActorMove;
import cz.matfyz.aai.fantom.server.ProtocolException;
import cz.matfyz.aai.fantom.server.ServerException;
public class ClientRunner extends MessagingClientBase {
/**
* The user supplied agent that performs all the moves.
*/
private Agent agent;
/**
* The game graph used by the client.
*/
private Graph graph;
/**
* A read-only version of the graph, that is given to the clients.
*/
private GraphReadOnly graphReadOnly;
/**
* Keeps track of whether the client already sent placement of
* actors for the current game.
*/
private boolean sentPlacement;
/**
* Processes the message 'update'. Updates the internal graph, and informs
* the agent.
*
* @param msg the message to be processed.
*/
protected void receiveUpdate(MessageUpdate msg) {
graph.updateActors(msg);
ActorMove[] moves = msg.getMoves().clone();
for(int i = 0; i < moves.length; i++) {
moves[i] = new ActorMove(graphReadOnly.getActor(moves[i].getActor().getId()),
moves[i].getTargetNode(), moves[i].getTransportType());
}
agent.update(moves);
}
/**
* Gets the movement for the current round from the agent, updates the internal
* representation of the game, and sends a 'move' message to the server.
*
* @throws ServerException if the message to the server was not sent.
*/
protected void sendMoves() throws ServerException {
ActorMove[] moves;
if(sentPlacement)
moves = agent.move();
else {
moves = agent.place();
sentPlacement = true;
}
MessageMove msg = new MessageMove(moves);
sendMessage(msg);
// Re-load the message for this graph
msg = new MessageMove(graph, msg.serialize());
graph.updateActors(msg);
}
/**
* Gets placement of the actors from the agent, updates the internal representation
* of the game, and sends a 'move' message to the server.
*
* @throws ServerException if the message to the server was not sent.
*/
protected void sendPlacement() throws ServerException {
ActorMove[] moves = agent.move();
MessageMove msg = new MessageMove(moves);
sendMessage(msg);
msg = new MessageMove(graph, msg.serialize());
graph.placeActors(msg);
}
/**
* Runs the client using {@link #agent} for decision making.
*/
public void run() throws ServerException {
try {
Message msg = null;
setStreams(System.in, System.out, null);
msg = new MessageReady(agent.getName(), agent.getClientType());
sendMessage(msg);
tournament_loop:
while(true) {
msg = receiveMessage(null);
if(msg instanceof MessageStart) {
this.graph = ((MessageStart)msg).getGraph();
this.graphReadOnly = new GraphReadOnly(this.graph);
agent.start(graphReadOnly);
sentPlacement = false;
if(agent.getClientType() == ClientType.DETECTIVE)
sendMoves();
while(true) {
msg = receiveMessage(graph);
if(!(msg instanceof MessageUpdate))
throw new ProtocolException(String.format("Message 'update' was expected, got '%s'", msg.getMessageType()), null);
MessageUpdate msgUpdate = (MessageUpdate)msg;
// Update the agent, if nobody did win the game in the last round, or if there
// is information about movements of the other agent (e.g. about the movement
// of the detectives, that captured the phantom, or the movement of the phantom
// that commited suicide).
if(msgUpdate.getWinner() == null || msgUpdate.getMoves().length > 0)
receiveUpdate(msgUpdate);
// If there is a winner, end the round
if(msgUpdate.getWinner() != null) {
agent.end(msgUpdate.getWinner());
continue tournament_loop;
}
sendMoves();
}
}
else if(msg instanceof MessageQuit) {
agent.quit();
return;
}
else {
throw new ProtocolException("Unexpected message type: " + msg.getMessageType(), null);
}
}
}
catch(Throwable e) {
e.printStackTrace();
}
}
/**
* Creates a new client runner.
*
* @param agent the agent object used to make all the decisions.
*/
public ClientRunner(Agent agent) {
if(agent == null)
throw new IllegalArgumentException("no agent was specified");
this.agent = agent;
}
}