package de.tu_dresden.inf.ggp06_2.connection; import java.util.Timer; import java.util.TimerTask; import org.apache.log4j.Logger; import de.tu_dresden.inf.ggp06_2.gamedb.logic.GameManager; import de.tu_dresden.inf.ggp06_2.parser.Parser; import de.tu_dresden.inf.ggp06_2.simulator.Game; import de.tu_dresden.inf.ggp06_2.simulator.Match; import de.tu_dresden.inf.ggp06_2.simulator.flags.TimerFlag; import de.tu_dresden.inf.ggp06_2.strategies.AbstractStrategy; import de.tu_dresden.inf.ggp06_2.strategies.SimultaneousFuzzySearch; import de.tu_dresden.inf.ggp06_2.strategies.SinglePlayerFuzzySearch; import de.tu_dresden.inf.ggp06_2.strategies.SinglePlayerSearch; import de.tu_dresden.inf.ggp06_2.strategies.TurnTakingFuzzySearch; import de.tu_dresden.inf.ggp06_2.strategies.mixins.LegalInBodyMixin; import de.tu_dresden.inf.ggp06_2.strategies.mixins.StrategyMixin; import de.tu_dresden.inf.ggp06_2.strategies.mixins.StubMixin; public class Player { /** * The MatchTimer class stops the search for legal moves after the specified * time. */ public static class MatchTimerTask extends TimerTask { Match match; public MatchTimerTask( Match matchToTime ) { match = matchToTime; match.getTimerFlag().reset(); logger.info( "Create a new RunnableMatch Timer." ); } @Override public void run() { match.getTimerFlag().interrupt(); logger.info( "RunnableMatch gets interrupted." ); } } public static class FlagTask extends TimerTask { TimerFlag flag; public FlagTask(TimerFlag flatToSet){ this.flag = flatToSet; this.flag.reset(); } @Override public void run(){ flag.interrupt(); } } /* Stores the logger for this class */ public final static Logger logger = Logger.getLogger(Player.class); /* GameSimulator of the player */ Match realMatch; MatchTimerTask matchTimerTask; int playClock; int startClock; Timer timer; GameManager gameManager = new GameManager(); final static String STR_ERROR1 = "Play message for the wrong match "; /** * This method is called when a new match begins.<br> * <br> * msg="(START <MATCH ID> <ROLE> <GAME DESCRIPTION> <STARTCLOCK> * <PLAYCLOCK>)"<br> * e.g. msg="(START tictactoe1 white ((role white) (role black) ...) 1800 * 120)" means: * <ul> * <li>the current match is called "tictactoe1"</li> * <li>your role is "white",</li> * <li> * after at most 1800 seconds, you have to return from the * commandStart method * </li> * <li>for each move you have 120 seconds</li> * </ul> * * TODO: * - use the time to "contemplate" about the game description * and return on time (before STARTCLOCK is over!) */ public void commandStart(Message msg){ // create the clock playClock = (msg.getPlayClock() - 5) * 1000; startClock = (msg.getStartClock() - 5) * 1000; logger.info( "Start Clock " + (startClock / 1000) ); logger.info( "Play Clock " + (playClock / 1000) ); Timer startClockTimer = new Timer(); TimerFlag timerFlag = new TimerFlag(); startClockTimer.schedule( new FlagTask(timerFlag), startClock ); // get the game from the database Game runningGame = gameManager.getGameByGDL( msg.getGameDescription().toString() ); logger.info("Bender created the game."); AbstractStrategy strategy; /** * brutal and annoying strategy switch */ // single player strategy if ( runningGame.isSinglePlayer() ) { TimerFlag flag = new TimerFlag(); new Timer().schedule( new FlagTask(flag), startClock/2 ); SinglePlayerSearch crispStrategy = new SinglePlayerSearch( runningGame, msg.getRole(), flag ); crispStrategy.pickMove( runningGame.getInitialNode() ); if (crispStrategy.isSomeGoalReached()){ strategy = crispStrategy; } else { StrategyMixin mixin = new StubMixin(); if (Parser.isLegalInsideBody()){ mixin = new LegalInBodyMixin(); } strategy = new SinglePlayerFuzzySearch(runningGame, msg.role, mixin); } // multiplayer strategy } else // turn taking game if ( runningGame.isTurnTaking() ) { // create the strategy StrategyMixin mixin = new StubMixin(); if (Parser.isLegalInsideBody()){ mixin = new LegalInBodyMixin(); } strategy = new TurnTakingFuzzySearch( runningGame, msg.getRole(), mixin ); // simultanous game } else { StrategyMixin mixin = new StubMixin(); if (Parser.isLegalInsideBody()){ mixin = new LegalInBodyMixin(); } strategy = new SimultaneousFuzzySearch( runningGame, msg.getRole(), mixin ); } logger.info( "Bender created the strategy " + strategy.getClass().getSimpleName() + "." ); // create a match realMatch = new Match ( msg.getMatchId(), strategy, msg.getRole() ); realMatch.setTimerFlag( timerFlag ); logger.info( "Bender created the match." ); // new Timer().schedule( new MatchTimerTask(realMatch), startClock ); logger.info( "Bender starts contemplate while doing yoga." ); // here search should be started realMatch.selectMove(); logger.info( "Bender is prepared to start the game." ); } /** * This method is called once for each move<br> * <br> * msg = "(PLAY <MATCH ID> <JOINT MOVE>)<br> * <JOINT MOVE> will be NIL for the first PLAY message and the list of the * moves of all the players in the previous state<br> * e.g. msg="(PLAY tictactoe1 NIL)" for the first PLAY message * or msg="(PLAY tictactoe1 ((MARK 1 2) NOOP))" if white marked cell (1,2) * and black did a "noop".<br> * <br> * TODO:<br> * <ul> * <li> * calculate the new state from the old one and the <JOINT MOVE> * </li> * <li> * use the time to find the best of your possible moves in the current * state * </li> * <li> * return your move (instead of "NIL") on time (before PLAYCLOCK is * over!) * </li> * </ul> * @return the move of this player */ public String commandPlay(Message msg){ // calls interrupted after playClock time is over (-1 to be on the save // side new Timer().schedule( new MatchTimerTask(realMatch), playClock ); logger.info( "Bender started its playClock timer." ); checkMatchId(msg); // only make a turn if the move list is not empty // is it empty it means we hit the first play message, setting the // initial state is done while constructing the match object if ( msg.getMoves() != null ){ logger.info( "Moves from GameMaster: " + msg.getMoves() ); realMatch.makeTurn( msg.getMoves() ); } // real work is done here String move = realMatch.getMoveString(); // logging the resulting move logger.info( "Our move: " + move ); return move; } /** * This method is called if the match is over * * msg="(STOP <MATCH ID> <JOINT MOVE>) * * TODO: * <ul> * <li>clean up the GamePlayer for the next match</li> * <li> * be happy if you have won, think about what went wrong if you have * lost ;-) * </li> * </ul> */ public void commandStop(Message msg){ checkMatchId(msg); // adds the moves to the match if ( msg.getMoves() != null ) realMatch.makeTurn( msg.getMoves() ); // check if we agree to be in a final state and log the result if ( !realMatch.isFinished() ) logger.fatal( "Game stopped but not finished" ); else logger.info( "Game finished" ); // match information is stored realMatch.saveInformationToDB(); } /** * This method is for game player spectators. * @return The current running match. */ public Match getMatch() { return this.realMatch; } /** * This method is for error checking nothing more. * @param msg */ public void checkMatchId( Message msg ) { if ( !msg.getMatchId().equals( realMatch.getMatchId() ) ) logger.error( STR_ERROR1 + msg.getMatchId() ); } }