///////////////////////////////////////////////////////////////////////
// STANFORD LOGIC GROUP //
// General Game Playing Project //
// //
// Sample Player Implementation //
// //
// (c) 2007. See LICENSE and CONTRIBUTORS. //
///////////////////////////////////////////////////////////////////////
/**
*
*/
package stanfordlogic.game;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import stanfordlogic.gdl.GdlAtom;
import stanfordlogic.gdl.GdlExpression;
import stanfordlogic.gdl.GdlList;
import stanfordlogic.gdl.Parser;
import stanfordlogic.knowledge.BasicKB;
import stanfordlogic.knowledge.GameInformation;
import stanfordlogic.knowledge.KnowledgeBase;
import stanfordlogic.knowledge.RelationNameProcessor;
import stanfordlogic.prover.AbstractReasoner;
import stanfordlogic.prover.GroundFact;
import stanfordlogic.prover.ProofContext;
import stanfordlogic.prover.Term;
import stanfordlogic.prover.TermObject;
import stanfordlogic.util.Stoppable;
import stanfordlogic.util.Triple;
/**
* Contains all information pertaining to a current run of a game.
*
* TODO: reorganize this as a running thread ("cognizing agent" model)
*/
public abstract class Gamer extends ReasoningEntity implements Stoppable
{
/** Play clock: how much time I have to make a move */
protected int playClock_;
/** Match ID */
protected String gameId_;
/** Ordered list of roles. */
protected List<TermObject> roles_;
/** The role assigned to "me" in the game. */
protected TermObject myRole_;
/** The role assigned to "me", in string form. */
protected String myRoleStr_;
/** My role number (the index into the roles list). */
protected int myRoleIndex_;
/** The current state of the game. That is, the collection of <i>true</i> facts. */
protected KnowledgeBase currentState_;
/** The proof context in which reasoning about this state is performed. */
protected ProofContext currentContext_;
/** The collected information about the game. */
protected GameInformation gameInformation_;
private static final Logger logger_ = Logger.getLogger("stanfordlogic.game");
private static final Logger stateLogger_ = Logger.getLogger("stanfordlogic.game.state");
protected final RelationNameProcessor doesProcessor_;
protected final RelationNameProcessor trueProcessor_;
protected Gamer(String gameId, Parser p)
{
super(p);
gameId_ = gameId;
doesProcessor_ = new RelationNameProcessor("does", symbolTable_);
trueProcessor_ = new RelationNameProcessor(parser_.TOK_TRUE);
}
public void initializeGame(TermObject role, int playClock,
GameInformation gameInformation,
AbstractReasoner reasoner)
{
myRole_ = role;
myRoleStr_ = role.toString();
playClock_ = playClock;
gameInformation_ = gameInformation;
roles_ = gameInformation_.getRoles();
reasoner_ = reasoner;
findRoleIndex();
setupInitialState();
}
private void findRoleIndex()
{
for (int i = 0; i < roles_.size(); i++)
{
TermObject role = roles_.get(i);
if (role.equals(myRole_)) {
myRoleIndex_ = i;
return;
}
}
logger_.severe("Could not find my role index");
myRoleIndex_ = 0;
}
protected void setupInitialState()
{
// First, create the game state
currentState_ = new BasicKB();
// Now, find all answers to the question: "init ?x"
Iterable<GroundFact> inits = getAllAnswersIterable(ProofContext.makeDummy(parser_), "init", "?x");
for (GroundFact init : inits) {
currentState_.setTrue(trueProcessor_.processFact(init));
}
currentContext_ = new ProofContext(currentState_, parser_);
}
/**
* @return The game ID used by this gamer.
*/
public String gameId()
{
return gameId_;
}
public abstract void stopIt();
/**
* Think about the next move and return it.
*
* @param prevMoves The moves just made, or nil if none (first move).
* @return A triple containing: the move, an explanation (or null) and a taunt (or null).
*/
public Triple<GdlExpression, String, String> play(GdlList prevMoves)
{
// Construct list of previous moves
GroundFact [] previousMoves = parsePreviousMoves(prevMoves);
if (previousMoves.length > 0 ) {
updateCurrentState(previousMoves);
}
Triple<Term, String, String> move = moveThink();
// Convert the Term to a GdlExpression
GdlExpression moveGdl;
if (move == null || move.first == null) {
logger_.severe(gameId_ + ": move returned by moveThink was null");
moveGdl = new GdlAtom(symbolTable_, parser_.TOK_NIL);
move = new Triple<Term, String, String>(null, "", "");
}
else {
// the top-level element returned is list of all elements in the parse;
// in this case, we have just one, the move.
moveGdl = parser_.parse(move.first.toString()).getElement(0);
}
Triple<GdlExpression, String, String> m =
new Triple<GdlExpression, String, String>(moveGdl, move.second,
move.third);
return m;
}
protected void updateCurrentState(GroundFact [] previousMoves)
{
for ( GroundFact prevMove : previousMoves )
currentState_.setTrue(prevMove);
if (stateLogger_.isLoggable(Level.FINE)) {
stateLogger_.fine(gameId_ + ": Updating game state.");
stateLogger_.fine(gameId_ + " - Previous: " + currentState_.stateToGdl());
}
Iterable<GroundFact> newFacts = reasoner_.getAllAnswersIterable(QUERY_NEXT, currentContext_);
// TODO: we should be creating the same kind of KB as currentState_ here
KnowledgeBase newKb = new BasicKB();
for(GroundFact newFact : newFacts)
newKb.setTrue(trueProcessor_.processFact(newFact));
currentState_ = newKb;
if (stateLogger_.isLoggable(Level.FINE)) {
stateLogger_.fine(gameId_ + " - New: " + currentState_.stateToGdl());
}
// Create a new proof context
currentContext_ = new ProofContext(currentState_, parser_);
}
protected abstract Triple<Term, String, String> moveThink();
protected GroundFact [] parsePreviousMoves(GdlList prevMoves)
{
if ( prevMoves == null )
return new GroundFact[0];
if (prevMoves.getSize() != roles_.size()) {
logger_.severe(gameId_ + ": Previous move list is not the same size as number of roles!");
}
GroundFact [] previousMoves = new GroundFact[prevMoves.getSize()];
for(int i=0; i<prevMoves.getSize(); i++)
{
if (i >= roles_.size())
break;
previousMoves[i] = new GroundFact(
parser_.TOK_DOES,
roles_.get(i),
Term.buildFromGdl(prevMoves.getElement(i))
);
}
return previousMoves;
}
/**
* Compute the payoffs of the game.
*
* @param prevMoves The list of the last moves.
* @return List of triplets with player's name, payoff, and a flag telling whether it's the played role.
*/
public List<Triple<String, Integer, Boolean>> getPayoffs(GdlList prevMoves)
{
// (role name, payoff, my role?) list
List<Triple<String, Integer, Boolean>> payoffs = new ArrayList<Triple<String, Integer, Boolean>>(roles_.size());
// Construct list of previous moves
GroundFact [] previousMoves = parsePreviousMoves(prevMoves);
updateCurrentState(previousMoves);
for(TermObject role : roles_)
{
int payoff = 0;
try
{
GroundFact goal = getAnAnswer(currentContext_, "goal", role.toString(), "?x");
TermObject score = (TermObject) goal.getTerm(1);
payoff = Integer.parseInt(score.toString());
}
catch(Exception e)
{
payoff = -1;
}
payoffs.add(new Triple<String, Integer, Boolean>(role.toString(), payoff, role.equals(myRole_)));
}
return payoffs;
}
}