Java tutorial
/** * The chessclub.com connection library. * More information is available at http://www.jinchess.com/. * Copyright (C) 2002 Alexander Maryanovsky. * All rights reserved. * * The chessclub.com connection library is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * The chessclub.com connection library 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 Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the chessclub.com connection library; if not, write to the Free * Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package free.chessclub.bot; import chess.engine.model.Board; import chess.engine.model.Move; import chess.engine.model.Piece; import chess.engine.search.*; import chess.engine.utils.MoveGeneration; import free.jin.Game; import free.jin.JinConnection; import free.jin.JinFrame; import free.jin.event.*; import free.jin.freechess.FreechessJinListenerManager; import free.jin.freechess.JinFreechessConnection; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import twitter4j.TwitterException; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.*; public class Bot extends JinFreechessConnection implements GameListener { public static int TWEET_MAX = 1; public static int TWEET_SOFT = 3; public static final String SET_TWEET_MAX = "tweetMax"; public static final String SET_TWEET_SOFT = "tweetSoft"; public boolean autoseek = false; public boolean tellOwner = true; public String owner = "tarabas"; private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd hh:mm:ss aaa"); private MoveGeneration moveGeneration = new MoveGeneration(); private BoardEvaluator eval = new SimpleEvaluator(moveGeneration); private ABSearch search = new ABSearch(moveGeneration, eval); private Move[] availableMoves = Move.createMoves(100); private IterativeSearch iterativeSearch = new IterativeSearch(search, moveGeneration, eval); private Thread searchThread; private int lastScore = 0; private Board gameBoard; /** * Jin's main frame. */ private static JinFrame mainFrame; /** * Our listener manager. */ private FreechessJinListenerManager listenerManager = new FreechessJinListenerManager(this); /** * A hashtable mapping command names to CommandHandlers. */ private Hashtable commandHandlers = new Hashtable(); /** * Jin's properties. */ private static Properties jinProps = new Properties(); private int thinkCommentIndex; private int lastGameCount; private String twitterUser; private String twitterPassword; private boolean foundMate; private boolean talkedMate; class TwitterStatus { public Game currentGame = null; public Game lastGame = null; public int gameCount; public int wins; public int losses; public int draws; public int incomplete; public boolean isPlaying() { return currentGame != null; } public Game getGame() { return isPlaying() ? currentGame : lastGame; } } private TwitterStatus twitterStatus = new TwitterStatus(); /** * The user's properties. The 'user' here isn't the chess server account/user, * but the operating system user. */ private static Properties userProps = new Properties(); private TwitterManager twitter; /** * The main method, duh :-). Creates a new Bot and makes it connect to * the server. */ public static void main(String[] args) { String hostname = args[0]; int port = Integer.parseInt(args[1]); String username = args[2]; String password = args[3]; String owner = args[4]; String twitterUser = args.length > 5 ? args[5] : null; String twitterPassword = args.length > 6 ? args[6] : null; try { Bot bot = new Bot(hostname, port, username, password, owner, twitterUser, twitterPassword); bot.connectAndLogin(); } catch (IOException e) { e.printStackTrace(); System.exit(0); } } /** * Creates a new ServerBot which will connect to the given hostname on the * given port and will use the given account and password. */ public Bot(String hostname, int port, String username, String password, String owner, String twitterUser, String twitterPassword) { super(hostname, port, username, password); this.owner = owner; this.twitterUser = twitterUser; this.twitterPassword = twitterPassword; //setDGState(Datagram.DG_PERSONAL_TELL, true); // This lets people talk to us. getJinListenerManager().addGameListener(this); } public void onLogin() { setStyle(12); super.onLogin(); if (autoseek) { seek(); } twitter = twitterUser != null && twitterUser.length() > 0 ? new TwitterManager() : null; if (twitter != null) { twitter.connect(); } } /** * Returns the SeekJinListenerManager via which you can register and * unregister SeekListeners. */ /** * Writes out usage information into the standard error stream. */ private static void showUsage() { System.out.println("Usage: "); System.out.println(" java " + Bot.class.getName() + " hostname port username password"); } private class SearchThread implements Runnable { private JinConnection connection; private Searcher searcher; private Board searchBoard; public SearchThread(JinConnection connection, Searcher searcher, Board searchBoard) { this.connection = connection; this.searcher = searcher; this.searchBoard = searchBoard; } public void run() { int score = searcher.search(searchBoard, 100); SearchStats stats = search.getStats(); if (stats.nodes < 100 && score == 0) { connection.sendCommand("draw"); connection.sendCommand("tell " + owner + " Detected Draw"); } if (tellOwner) connection.sendCommand("tell " + owner + " s: " + formatScore(score)); if (tellOwner) connection.sendCommand("tell " + owner + " Moves: " + Move.toString(searcher.getPV())); if (tellOwner) { connection.sendCommand("tell " + owner + " Stats: " + stats); } connection = null; } protected void finalize() throws Throwable { System.err.println("finalizing searcher thread"); } } private class SearchTimerThread implements Runnable { private JinConnection connection; private Searcher searcher; private long maxTime; private String fen; public SearchTimerThread(JinConnection connection, IterativeSearch searcher, long maxTime, String fen) { this.connection = connection; this.searcher = searcher; this.maxTime = maxTime; this.fen = fen; } public void run() { long start = System.currentTimeMillis(); long end = System.currentTimeMillis(); boolean extended = false; boolean spoken = false; while ((end - start <= maxTime)) { if (maxTime > 2000) { try { Thread.sleep(maxTime / 4); } catch (InterruptedException e) { } } else { try { Thread.sleep(maxTime + 1); } catch (InterruptedException e) { } } Move pvMove = searcher.getPV()[0]; int score = pvMove.score; if (maxTime > 700) { if (score < lastScore - 45 && score > -Searcher.MATE + 100) { lastScore = searcher.getPV()[0].score; maxTime += maxTime / 2; String thinkComment = getThinkComment(); if (tellOwner) { connection.sendCommand("tell " + owner + " " + thinkComment); } boolean playingWhite = twitterStatus.getGame().getWhiteName().equals(getUsername()); String oppName = !playingWhite ? twitterStatus.getGame().getWhiteName() : twitterStatus.getGame().getBlackName(); connection.sendCommand("kib " + oppName + " - " + thinkComment); extended = true; } } if (!spoken) { if (score > lastScore + 10000) { connection.sendCommand( "kib Hee-Haw (" + formatScore(score) + ") - " + Move.toString(searcher.getPV())); spoken = true; } else if (score > lastScore + 900) { connection.sendCommand( "kib Zoiks! (" + formatScore(score) + ") - " + Move.toString(searcher.getPV())); spoken = true; } else if (score > lastScore + 300) { connection.sendCommand( "kib Ruh roh... (" + formatScore(score) + ") - " + Move.toString(searcher.getPV())); spoken = true; } } if (Math.abs(score) < Searcher.MATE && Math.abs(score) > Searcher.MATE - 300) { System.err.println("Found Mate"); if (score > Searcher.MATE - 300) { foundMate = true; } break; } System.err.println("Checking on search (" + lastScore + " -> " + score + ")"); end = System.currentTimeMillis(); } System.err.println("Stopping Search"); lastScore = searcher.getPV()[0].score; connection.sendCommand(searcher.getPV()[0].toFICSString()); if (foundMate) { maybeTweetMatePuzzle(); } searcher.stop(); } private void maybeTweetMatePuzzle() { if (!talkedMate) { talkedMate = true; new Thread(new Runnable() { @Override public void run() { Move[] pv = searcher.getPV(); int movesToMate = 1; int color = pv[0].moved.color; int movedType = pv[0].moved.type; if (pv[1].moved == null) { return; } int movedType2 = pv[1].moved.type; int complexity = -Math.abs(eval.scorePosition(gameBoard, 0, 0)) / 600; System.err.println("MatePV: " + Move.toString(pv)); int material = 0; for (int i = 0; i < 100 && pv[i].moved != null; i++) { if (pv[i].taken != null) { material += (pv[i].taken.color == color ? -1 : 1) * Math.abs(pv[i].taken.getMaterialValue()); } if (pv[i].moved.color == color) { if (pv[i].promoteTo == -1) { if (pv[i].moved.type != Piece.QUEEN && pv[i].moved.type != Piece.ROOK) { if (movedType != pv[i].moved.type) { complexity += 1; movedType = pv[i].moved.type; } } else if (pv[i].taken == null) { complexity += 1; } else if (!pv[i].check) { complexity += 1; } } else if (pv[i].promoteTo == Piece.QUEEN) { complexity -= 1; } } else { if (pv[i].taken != null) { complexity += 1; } else if (movedType2 != pv[i].moved.type) { complexity += 1; movedType2 = pv[i].moved.type; } } movesToMate++; } complexity -= material / 3; movesToMate /= 2; complexity += (movesToMate / 2) - 1; String boardURL = "http://www.eddins.net/steve/chess/ChessImager/ChessImager.php?fen=" + fen.substring(0, fen.indexOf(" ")); String tinyUrl = getTinyUrl(boardURL); if (tinyUrl != null && !tinyUrl.equals("Error") && complexity + TWEET_SOFT > TWEET_MAX && movesToMate > 1) { // New DonkeyFactory Chess position // Tweet final String tweet = (complexity < TWEET_MAX ? "@heavypennies " : "") + (color == 1 ? "White" : "Black") + " mates in " + movesToMate + " (DF-" + complexity + ") -> " + tinyUrl + " - solution in next tweet! #donkeyfactory #chess"; System.err.println("Tiny URL: " + tinyUrl); System.err.println("Full URL: " + boardURL); System.err.println(tweet); tweet(tweet); final int colorX = color; final int movesToMateX = movesToMate; final int complexityX = complexity; final String tinyUrlX = tinyUrl; final String mateLine = Move.toString(pv); try { Thread.sleep(1000 * 60 * 5); } catch (InterruptedException e) { e.printStackTrace(); } final String solutionTweet = (complexityX < TWEET_MAX ? "@heavypennies " : "") + "M" + movesToMateX + ": " + tinyUrlX + "\n Solution:" + mateLine; tweet(solutionTweet); sendTell(owner, "Tweeted solution: " + solutionTweet); System.err.println("Tweeted Solution: " + solutionTweet); connection.sendCommand( "say Thank You! We created a new DonkeyFactory chess puzzle for this Mate in " + movesToMate + ", Estimated Complexity (DonkeyScale): " + complexity + ""); connection.sendCommand("say http://twitter.com/donkeyfactory"); } else { System.err.println("(Difficulty " + complexity + ")-[" + dateFormat.format(new Date()) + "] I found Mate in " + movesToMate + " -> " + boardURL + " #donkeyfactory #chess"); } } }).start(); } } private String getThinkComment() { String thinkComment; switch (thinkCommentIndex) { case 0: { thinkComment = "Let me look at this position for " + (maxTime / 2) + " more milliseconds (" + "you may wish i'd tell u what i'm thinking..." + ")"; thinkCommentIndex++; break; } case 1: { thinkComment = "Gimme " + (maxTime / 2) + " milliseconds...this line looks interesting: " + "you may wish i'd tell u what i'm thinking..."; thinkCommentIndex++; break; } case 2: { thinkComment = "Please allow me to concentrate for just " + (maxTime / 2) + " more milliseconds (" + "you may wish i'd tell u what i'm thinking..." + ")"; thinkCommentIndex++; break; } case 3: { thinkComment = "Hmmm... I'm gonna take an extra " + (maxTime / 2) + " milliseconds to think - this line looks interesting: " + "you may wish i'd tell u what i'm thinking..."; thinkCommentIndex++; break; } case 4: { thinkComment = "Interesting...I'll need " + (maxTime / 2) + " milliseconds more time to ponder this (" + "you may wish i'd tell u what i'm thinking..." + ")"; thinkCommentIndex++; break; } case 5: { thinkComment = "I'll need to think on this for " + (maxTime / 2) + " milliseconds longer. (" + "you may wish i'd tell u what i'm thinking..." + ")"; thinkCommentIndex = 0; break; } default: { thinkComment = "You may have something there, perhaps continuing with " + "you may wish i'd tell u what i'm thinking..."; thinkCommentIndex = 0; } } return thinkComment; } protected void finalize() throws Throwable { System.err.println("finalizing timer thread"); } } public void gameStarted(GameStartEvent evt) { System.err.println("game started"); if (tellOwner) evt.getConnection().sendCommand("tell " + owner + " Game Started: " + evt.getGame().getID()); evt.getConnection().sendCommand("say Heee Haw!!"); Integer gameNumber = findMyGame(); JinFreechessConnection.InternalGameData gameData = (JinFreechessConnection.InternalGameData) ongoingGamesData .get(gameNumber); search = new ABSearch(moveGeneration, eval); iterativeSearch = new IterativeSearch(search, moveGeneration, eval); gameBoard = new Board(); //((SimpleEvaluator)eval).pawnHash.clear(); gameBoard.setEPDPosition(evt.getGame().getInitialPosition().getFEN()); System.err.println("Board: " + evt.getGame().getInitialPosition().getFEN()); if (gameData != null && gameData.boardData != null) { gameBoard.stats.whiteKingsideRookMoves = gameData.boardData.canWhiteCastleKingside() ? 0 : 1; gameBoard.stats.whiteQueensideRookMoves = gameData.boardData.canWhiteCastleQueenside() ? 0 : 1; gameBoard.stats.blackKingsideRookMoves = gameData.boardData.canBlackCastleKingside() ? 0 : 1; gameBoard.stats.blackQueensideRookMoves = gameData.boardData.canBlackCastleQueenside() ? 0 : 1; } twitterStatus.currentGame = evt.getGame(); talkedMate = false; foundMate = false; } public void gameEnded(GameEndEvent evt) { twitterStatus.gameCount++; if (autoseek) { sendCommand("rem"); seek(); } twitterStatus.currentGame = null; Game lastGame = evt.getGame(); twitterStatus.lastGame = lastGame; boolean playingWhite = lastGame.getWhiteName().equals(getUsername()); int myRating = playingWhite ? lastGame.getWhiteRating() : lastGame.getBlackRating(); int oppRating = !playingWhite ? lastGame.getWhiteRating() : lastGame.getBlackRating(); String oppName = !playingWhite ? lastGame.getWhiteName() : lastGame.getBlackName(); boolean iWin = ((lastGame.getResult() == Game.WHITE_WINS && playingWhite) || (lastGame.getResult() == Game.BLACK_WINS && !playingWhite)); boolean iLost = ((lastGame.getResult() == Game.WHITE_WINS && !playingWhite) || (lastGame.getResult() == Game.BLACK_WINS && playingWhite)); if (iWin) { twitterStatus.wins++; if (oppRating - myRating > 50) { tweetGreatVictory(oppName, oppRating, myRating); } } else if (iLost) { twitterStatus.losses++; if (oppRating < myRating - 200) { tweetStunningDefeat(oppName, oppRating, myRating); } } else if (lastGame.getResult() == Game.DRAW) { twitterStatus.draws++; } else { twitterStatus.incomplete++; } if (twitterStatus.gameCount > lastGameCount + 9) { lastGameCount = twitterStatus.gameCount; tweetCurrentStatus(); } System.err.println("Stopping search on game end"); iterativeSearch.stop(); if (searchThread != null) { try { searchThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.gc(); } public void moveMade(MoveMadeEvent evt) { System.err.println("move made"); } String currentSearchEPD = ""; public void positionChanged(PositionChangedEvent evt) { int lastScore; System.err.println("position changed"); Integer gameNumber = findMyGame(); JinFreechessConnection.InternalGameData gameData = (JinFreechessConnection.InternalGameData) ongoingGamesData .get(gameNumber); Move move = makeGameMovesOnBoard(gameData, gameBoard); if (gameBoard.isDraw()) { if (tellOwner) sendCommand("tell " + owner + " Claiming draw"); evt.getConnection().sendCommand("draw"); } // if(move != null) evt.getConnection().sendCommand("tell " + owner + " board move: " + move.toString()); if (evt.getGame().isUserAllowedToMovePieces(evt.getPosition().getCurrentPlayer()) && (!currentSearchEPD.equals(evt.getGame().getInitialPosition().getFEN()) || evt.getGame().getPliesSinceStart() == 0)) { if (!iterativeSearch.isDone()) { System.err.println("Waiting for search..."); try { searchThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("Search complete."); } Board searchBoard = new Board(); // searchBoard.setEPDPosition(gameData.boardData.getBoardFEN()); currentSearchEPD = evt.getGame().getInitialPosition().getFEN(); searchBoard.setEPDPosition(evt.getGame().getInitialPosition().getFEN()); searchBoard.turn = evt.getGame().getUserPlayer().isWhite() ? 1 : 0; searchBoard.stats = gameBoard.stats; searchBoard.stats.whiteKingsideRookMoves = gameData.boardData.canWhiteCastleKingside() ? 0 : 1; searchBoard.stats.whiteQueensideRookMoves = gameData.boardData.canWhiteCastleQueenside() ? 0 : 1; searchBoard.stats.blackKingsideRookMoves = gameData.boardData.canBlackCastleKingside() ? 0 : 1; searchBoard.stats.blackQueensideRookMoves = gameData.boardData.canBlackCastleQueenside() ? 0 : 1; searchBoard.repetitionTable = gameBoard.repetitionTable; searchBoard.fiftyMoveTable = gameBoard.fiftyMoveTable; searchBoard.moveIndex = gameBoard.moveIndex; for (int t = 0; t < 128; t++) { if (gameBoard.boardSquares[t] == null) { continue; } System.arraycopy(gameBoard.boardSquares[t].enPassentInfo, 0, searchBoard.boardSquares[t].enPassentInfo, 0, searchBoard.boardSquares[t].enPassentInfo.length); } // searchBoard.moveHistory = gameBoard.moveHistory; /* searchBoard.hash1 = gameBoard.hash1; searchBoard.pawnHash = gameBoard.pawnHash; */ iterativeSearch.reset(); searchThread = new Thread(new SearchThread(evt.getConnection(), iterativeSearch, searchBoard)); searchThread.setPriority(6); searchThread.start(); String fen = gameData.boardData.getBoardFEN(); System.err.println("MoveIndex: " + searchBoard.moveIndex); System.err.println("GB Approaching Draw: " + gameBoard.isApproachingDraw()); System.err.println("SB Approaching Draw: " + searchBoard.isApproachingDraw()); System.err.println("Board: " + fen); System.err.println("Stats: " + searchBoard.stats); int score = eval.scorePosition(searchBoard, -Searcher.INFINITY, Searcher.INFINITY); System.err.println("Searching (" + formatScore(score) + ") ..."); System.err.println(searchBoard.toString()); long maxTime = getTimeForMove(evt, gameData); Thread searchTimerThread = new Thread( new SearchTimerThread(evt.getConnection(), iterativeSearch, maxTime, fen)); searchTimerThread.setPriority(7); searchTimerThread.start(); } else { System.err.println("Waiting for opponent..."); // System.gc(); } } private Move makeGameMovesOnBoard(InternalGameData gameData, Board board) { if (gameData.moveList.size() == 0) { return null; } free.chess.Move move = (free.chess.Move) gameData.moveList.lastElement(); int toSquareIndex = (move.getEndingSquare().getRank() * 8) + move.getEndingSquare().getFile(); int fromSquareIndex = (move.getStartingSquare().getRank() * 8) + move.getStartingSquare().getFile(); try { moveGeneration.generateFullMoves(availableMoves, board); } catch (Exception e) { return null; } Move actualMove = null; for (Move possibleMove : availableMoves) { if (possibleMove.fromSquare == null) { break; } if (possibleMove.fromSquare.index64 == fromSquareIndex && possibleMove.toSquare.index64 == toSquareIndex) { if (move.getMoveString().indexOf("=") > -1) { char promoteTo = move.getMoveString().toLowerCase() .substring(move.getMoveString().indexOf("=") + 1).charAt(0); switch (promoteTo) { case 'n': { if (possibleMove.promoteTo == Piece.KNIGHT) { actualMove = possibleMove; } break; } case 'b': { if (possibleMove.promoteTo == Piece.BISHOP) { actualMove = possibleMove; } break; } case 'r': { if (possibleMove.promoteTo == Piece.ROOK) { actualMove = possibleMove; } break; } case 'q': { if (possibleMove.promoteTo == Piece.QUEEN) { actualMove = possibleMove; } break; } } } else { actualMove = possibleMove; break; } } } if (actualMove != null && actualMove.moved != null) { board.make(actualMove); /* if(actualMove.moved.type == Piece.PAWN || actualMove.taken != null) { board.repetitionTable[board.moveIndex] = board.moveIndex; } */ } return actualMove; } private long getTimeForMove(PositionChangedEvent evt, InternalGameData gameData) { int timeLeft = (evt.getGame().getUserPlayer().isWhite() ? gameData.boardData.getWhiteTime() : gameData.boardData.getBlackTime()); long maxTime; if (gameData.boardData.getNextMoveNumber() < 2) { maxTime = timeLeft / 36; } else if (gameData.boardData.getNextMoveNumber() < 12) { maxTime = timeLeft / 32; } else if (gameData.boardData.getNextMoveNumber() > 30 && timeLeft < 5000) { maxTime = timeLeft / 34; } /* else if(timeLeft < 15000 && timeLeft > 10000) { maxTime = 1000; } */ else { maxTime = timeLeft / 29; } System.err.println("Time Left: " + timeLeft); System.err.println("MaxTime: " + maxTime); return maxTime; } public void takebackOccurred(TakebackEvent evt) { evt.getConnection().sendCommand("refresh"); } public void illegalMoveAttempted(IllegalMoveEvent evt) { evt.getConnection().sendCommand("refresh"); } public void clockAdjusted(ClockAdjustmentEvent evt) { } public void boardFlipped(BoardFlipEvent evt) { } public void tweet(String tweet) { if (twitter == null) return; try { twitter.tweet(tweet); sendCommand("tell " + owner + " tweet complete."); } catch (TwitterException e) { sendCommand("tell " + owner + " tweet failed."); e.printStackTrace(); } } public void tweetStunningDefeat(String winnersName, int winnersRating, int myRating) { tweet("Defeat - I (" + myRating + ") was beaten by " + winnersName + " (" + winnersRating + ") :("); } public void tweetGreatVictory(String winnersName, int winnersRating, int myRating) { tweet("Victory - I (" + myRating + ") beat " + winnersName + " (" + winnersRating + ")!"); } public void tweetCurrentStatus() { String status = getCurrentStatus(); if (status != null) { tweet(status); } } public String getCurrentStatus() { Game game = twitterStatus.getGame(); if (game != null) { boolean playingWhite = game.getWhiteName().equals(getUsername()); int myRating = playingWhite ? game.getWhiteRating() : game.getBlackRating(); return myRating + " - My W/D/L is " + twitterStatus.wins + "-" + twitterStatus.draws + "-" + twitterStatus.losses + " for the past " + twitterStatus.gameCount + " games (" + twitterStatus.incomplete + " games were incomplete)."; } return null; } public void tweetCurrentGameStatus() { String currentGameStatus = getCurrentGameStatus(); if (currentGameStatus != null) { tweet(currentGameStatus); } } public String getCurrentGameStatus() { Game game = twitterStatus.getGame(); if (game != null) { boolean playingWhite = game.getWhiteName().equals(getUsername()); int myRating = playingWhite ? game.getWhiteRating() : game.getBlackRating(); int oppRating = !playingWhite ? game.getWhiteRating() : game.getBlackRating(); String oppName = !playingWhite ? game.getWhiteName() : game.getBlackName(); String play = "played"; String scoreStatement = ""; if (twitterStatus.isPlaying()) { String score = formatScore(lastScore); play = "am playing"; if (lastScore == 0) { scoreStatement = " and I think its an even game"; } if (Math.abs(lastScore) > Searcher.MATE - 300 && Math.abs(lastScore) < Searcher.MATE) { scoreStatement = " and I see " + score; } else { scoreStatement = " and I think I am " + (lastScore > 0 ? "ahead" : "behind") + " by " + score + " pawns "; } String fen = game.getInitialPosition().getFEN(); String boardURL = "http://www.eddins.net/steve/chess/ChessImager/ChessImager.php?fen=" + fen.substring(0, fen.indexOf(" ")); String tinyUrl = getTinyUrl(boardURL); return "DonkeyFactory: (" + myRating + ") I " + play + " " + oppName + " (" + oppRating + ") " + scoreStatement + " " + tinyUrl; } } return null; } private String formatScore(int score) { if (score > Searcher.MATE - 300) { int mateDistance = (Searcher.MATE - score + 1) / 2; return "+MATE in " + mateDistance; } else if (score < -Searcher.MATE + 300) { int mateDistance = (Searcher.MATE + score) / 2; return "-MATE in " + mateDistance; } return (score > 0 ? "+" : "") + (score / 100D); } protected void processLine(String line) { if (line.indexOf("(adjourned)") > -1) { sendCommand("accept"); } super.processLine(line); } /** * Registers a CommandHandler for the given command. All commands registered * this way are case insensitive. There can be only one command handler per * command, so this method will throw an IllegalArgumentException if you try to * register a CommandHandler for a command for which a CommandHandler was * already registered. */ public void registerCommandHandler(String command, CommandHandler commandHandler) { command = command.toLowerCase(); CommandHandler oldCommandHandler = (CommandHandler) commandHandlers.put(command, commandHandler); if (oldCommandHandler != null) { commandHandlers.put(command, oldCommandHandler); throw new IllegalArgumentException("Unable to register another CommandHandler for command: " + command); } } /** * Sends the given message to the given player as a personal tell. */ public void sendTell(String player, String message) { sendCommand("tell " + player + " " + message); } /** * Processes personal tells. If the tell type is TELL, this method parses the * message and delegates to processCommand. Otherwise, the tell is ignored. */ protected void processPersonalTell(String playerName, String titles, String message, int tellType) { /* if (tellType!=TELL) return; */ StringTokenizer tokenizer = new StringTokenizer(message, " \"\'", true); String[] tokens = new String[tokenizer.countTokens()]; int numTokens = 0; try { while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (token.equals("\"") || token.equals("\'")) { tokens[numTokens++] = tokenizer.nextToken(token); tokenizer.nextToken(" \"\'"); // Get rid of the string terminating quote. } else if (!token.equals(" ")) tokens[numTokens++] = token; } } catch (NoSuchElementException e) { sendTell(playerName, "Unterminated string literal"); return; } if (numTokens == 0) { sendTell(playerName, "You must specify a command"); return; } String issuedCommand = tokens[0]; String[] args = new String[numTokens - 1]; System.arraycopy(tokens, 1, args, 0, args.length); CommandEvent command = new CommandEvent(this, issuedCommand, args, message, playerName, titles); processCommand(command); } /** * Asks the appropriate CommandHandler to handle the command. */ protected void processCommand(CommandEvent command) { String issuedCommand = command.getCommand(); CommandHandler commandHandler = (CommandHandler) commandHandlers.get(issuedCommand.toLowerCase()); if (commandHandler == null) sendTell(command.getPlayerName(), "Unknown command: " + issuedCommand); else commandHandler.handleCommand(command); } private void seek() { sendCommand("seek 1 0 r f"); sendCommand("seek 3 0 r f"); sendCommand("seek 5 0 r f"); } protected boolean processPersonalTell(String username, String titles, String message) { super.processPersonalTell(username, titles, message); if (message.indexOf("status") > -1) { String currentStatus = getCurrentStatus(); if (currentStatus != null) { sendCommand("tell " + username + " " + currentStatus); } String currentGameStatus = getCurrentGameStatus(); if (currentGameStatus != null) { sendCommand("tell " + username + " " + currentGameStatus); } } else if (username.equals(owner)) { if (message.indexOf("tellme") > -1) { tellOwner = !tellOwner; sendCommand("tell " + owner + " tellme: " + tellOwner); } else if (message.indexOf("tdf") == 0) { sendCommand("tell " + owner + " challenging testdonkeyfactor"); sendCommand("ma testdonkeyfactor 1 0 u"); } else if (message.indexOf("tweetGame") > -1) { tweetCurrentGameStatus(); } else if (message.indexOf("dset ") == 0) { if (message.indexOf(" tweetMax ") > -1) { try { TWEET_MAX = Integer.parseInt( message.substring(message.indexOf(SET_TWEET_MAX) + SET_TWEET_MAX.length() + 1)); } catch (NumberFormatException e) { sendCommand("tell " + owner + " no... e.g. set " + SET_TWEET_MAX + " 8"); } } if (message.indexOf(" " + SET_TWEET_SOFT + " ") > -1) { try { TWEET_SOFT = Integer.parseInt( message.substring(message.indexOf(SET_TWEET_SOFT) + SET_TWEET_SOFT.length() + 1)); } catch (NumberFormatException e) { sendCommand("tell " + owner + " no... e.g. set " + SET_TWEET_SOFT + " 8"); } } } else if (message.startsWith("dset")) { sendCommand("tell " + owner + " " + SET_TWEET_MAX + ": " + TWEET_MAX); sendCommand("tell " + owner + " " + SET_TWEET_SOFT + ": " + TWEET_SOFT); } else if (message.indexOf("tweet ") == 0) { sendCommand("tell " + owner + " tweeting '" + message.substring(6) + "'"); try { twitter.tweet(message.substring(6)); } catch (TwitterException e) { e.printStackTrace(); } } else if (message.indexOf("seek") > -1) { autoseek = !autoseek; sendCommand("tell " + owner + " autoseek: " + autoseek); if (autoseek) { seek(); } } else { sendCommand(message); } } return true; } public static String getTinyUrl(String fullUrl) { HttpClient httpclient = new DefaultHttpClient(); HttpGet get = new HttpGet("http://tinyurl.com/api-create.php?url=" + fullUrl); try { HttpResponse response = httpclient.execute(get); return EntityUtils.toString(response.getEntity()); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }