Package nl.zoidberg.calculon.icc

Source Code of nl.zoidberg.calculon.icc.ICCInterface$GameEndedHandler

/**
* Calculon - A Java chess-engine.
*
* Copyright (C) 2008-2009 Barry Smith
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package nl.zoidberg.calculon.icc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

import nl.zoidberg.calculon.engine.BitBoard;
import nl.zoidberg.calculon.engine.ChessEngine;
import nl.zoidberg.calculon.engine.MoveGenerator;
import nl.zoidberg.calculon.model.Piece;
import nl.zoidberg.calculon.notation.PGNUtils;
import nl.zoidberg.calculon.notation.Style12;
import nl.zoidberg.calculon.opening.OpeningBook;
import nl.zoidberg.calculon.util.LogFormatter;

import org.apache.commons.digester.Digester;
import org.apache.commons.lang.StringUtils;

public class ICCInterface {

  private static final Logger log = Logger.getLogger(ICCInterface.class.getName());

  private static final int DG_MY_GAME_STARTED     = 15;
  private static final int DG_MY_GAME_RESULT       = 16;

  private static boolean shutdown = false;
  private static String talkResponse = "I'm sorry Dave, I'm afraid I can't do that.";
  private static ICCSConfig iccConfig;
  private static enum GameType { BULLET, BLITZ, STANDARD };

  private Socket connection;
  private Thread moveThread = null;
  private List<ConnectionListener> listeners = new ArrayList<ConnectionListener>();
  private List<BlockHandler> blockHandlers = new ArrayList<BlockHandler>();
  private PrintStream out;
  private String opponent = null;
  private boolean rated = false;
  private int gameNumber = -1;
  private boolean playingWhite = true;
  private boolean accept = true;
  private boolean alive = true;
  private BitBoard currentBoard;
  private OpeningBook openingBook;
  private GameType gameType;
 
  public static void main(String[] args) throws Exception {
    log.setLevel(Level.FINE);
    Handler handler = new ConsoleHandler();
    handler.setLevel(Level.FINE);
    handler.setFormatter(new LogFormatter());
   
    for(Handler h: log.getHandlers()) {
      log.removeHandler(h);
    }
    log.addHandler(handler);
   
    if(System.getProperty("calculon.password") == null)
    {
      log.severe("password must be specified.");
      System.exit(-1);
    }
   
    while(!shutdown) {
      try {
        new ICCInterface().connect();
      } catch (Exception x) {
        log.log(Level.SEVERE, "Error", x);
        try { Thread.sleep(60000); } catch (InterruptedException ix) { }
      }
    }
  }

  private ICCInterface() {
    Digester digester = new Digester();
   
    digester.addObjectCreate("calculon/icc", ICCSConfig.class);
    digester.addBeanPropertySetter("calculon/icc/operator-name", "operatorName");
    digester.addBeanPropertySetter("calculon/icc/login-name", "loginName");
    digester.addBeanPropertySetter("calculon/icc/accept-min", "acceptMin");
    digester.addBeanPropertySetter("calculon/icc/accept-max", "acceptMax");
    digester.addBeanPropertySetter("calculon/icc/max-rematches", "maxRematches");
    digester.addBeanPropertySetter("calculon/icc/reseek", "reseek");
    digester.addObjectCreate("calculon/icc/default-seeks/seek", ICCSConfig.Seek.class);
    digester.addSetProperties("calculon/icc/default-seeks/seek", "time", "initialTime");
    digester.addSetProperties("calculon/icc/default-seeks/seek", "inc", "increment");
    digester.addSetNext("calculon/icc/default-seeks/seek", "addSeekAd");
   
    try {
      iccConfig = (ICCSConfig) digester.parse(ClassLoader.getSystemResourceAsStream("calculon.xml"));
    } catch (Exception e) {
      log.log(Level.WARNING, "Config reading failed", e);
      throw new RuntimeException(e);
    }
    log.finer(iccConfig.toString());

    openingBook = OpeningBook.getDefaultBook();

    listeners.add(new DebugListener());
    listeners.add(new ChallengeListener());
    listeners.add(new BoardListener());
    listeners.add(new AbortListener());
    listeners.add(new CommandListener());
    listeners.add(new ReseekListener());
    listeners.add(new ChatListener());
   
    listeners.add(new BlockListener());
    listeners.add(new BlockLv2Listener());
   
    blockHandlers.add(new GameEndedHandler());
  }

  public void connect() throws IOException {
    connection = new Socket("chessclub.com", 23);
    doLogin();
    BufferedReader reader = new BufferedReader(new InputStreamReader(
        connection.getInputStream()));
    out = new PrintStream(connection.getOutputStream());

    send("set level1 1");
    send("set style 12");
    receiveLevel2(DG_MY_GAME_STARTED);
    receiveLevel2(DG_MY_GAME_RESULT);
   
    setStatus();
    if (iccConfig.isReseek()) {
      reseek();
    }

    Runnable keepAlive = new Runnable() {
      public void run() {
        while(alive) {
          send("date");
          try { Thread.sleep(60000 * 15); } catch (InterruptedException x) { }
        }
      }
    };
    Thread keepAliveThread = new Thread(keepAlive);
    keepAliveThread.setDaemon(true);
    keepAliveThread.start();

    StringBuffer line = new StringBuffer();
    int c;
    try {
      while((c = reader.read()) != -1) {
        if(c == ('M'&0x1F) || c == ('G'&0x1F)) {
          continue;
        }
        line.append((char)c);
        if(c == '\n') {
          fireDataReceived(line.toString());
          line.setLength(0);
          continue;
        }
        if(line.length() >= 2 && line.charAt(line.length()-2) == ('Y'&0x1F) && line.charAt(line.length()-1) == ']') {
          fireDataReceived(line.toString());
          line.setLength(0);
        }
      }
    } finally {
      alive = false;
      try {
        reader.close();
        out.close();
      } catch (Exception x) { }
    }
  }
 
  private void receiveLevel2(int dgId) {
    send("set-2 " + String.valueOf(dgId) + " 1");
  }

  private void fireDataReceived(String s) {
    for (ConnectionListener listener : listeners) {
      try {
        listener.message(s);
      } catch (Exception e) {
        log.log(Level.WARNING, "Handler " + listener + " threw exception", e);
      }
    }
  }

  private void doLogin() throws IOException {
    int c;
    String sLogin = "login: ";
    int sptr = 0;
    while ((c = connection.getInputStream().read()) != -1) {
      if (c == sLogin.charAt(sptr)) {
        sptr++;
        if (sptr == sLogin.length()) {
          log.fine("Sending login name");
          connection.getOutputStream()
              .write((iccConfig.getLoginName() + "\n").getBytes());
          break;
        }
      } else {
        sptr = 0;
      }
    }

    sLogin = "password: ";
    sptr = 0;
    while ((c = connection.getInputStream().read()) != -1) {
      if (c == sLogin.charAt(sptr)) {
        sptr++;
        if (sptr == sLogin.length()) {
          log.finer("Sending password (" + System.getProperty("calculon.password") + ")");
          connection.getOutputStream().write((System.getProperty("calculon.password") + "\n").getBytes());
          break;
        }
      } else {
        sptr = 0;
      }
    }
  }
 
  private void reseek() {
    send("resume");
    Runnable seeker = new Runnable() {
      public void run() {
        for(int i = 0; i < 4; i++) {
          try { Thread.sleep(15000); } catch (InterruptedException x) { }
          if(gameNumber != -1) {
              return;
          }
          send("resume");
        }
        for(ICCSConfig.Seek seek: iccConfig.getSeekAds()) {
          send(seek.getCommand());
        }
      }
    };
    new Thread(seeker).start();
  }

  private synchronized void send(String s) {
    log.fine(">>> " + s);
    out.println(s);
  }

  private void tellOp(String s) {
    send("tell " + iccConfig.getOperatorName() + " " + s);
  }
 
  private void setStatus() {
    if(shutdown) {
      send("set 9 Current Status: Shutting down.");
    } else if (iccConfig.isReseek()) {
      send("set 9 Current Status: Auto (accept " + (accept?"on":"off") + ").");
    } else {
      send("set 9 Current Status: Manual (accept " + (accept?"on":"off") + ").");
    }
  }

  private interface ConnectionListener {
    public void message(String s);
  }
 
  private interface BlockHandler {
    @Deprecated
    public void processBlock(ResponseBlock responseBlock);
    public void processBlock(ResponseBlockLv2 responseBlock);
  }

  private class DebugListener implements ConnectionListener {
    public void message(String s) {
      log.finest("<<< " + s);
    }
  }

  private class ReseekListener implements ConnectionListener {
    public void message(String s) {
    }
  }

  private class ChallengeListener implements ConnectionListener {
    // e.g. Challenge: BarryNL (2029) CalculonX (2000) rated Blitz 5 0
   
    public void message(String s) {
      if (s.startsWith("Challenge: ") && !accept) {
        send("decline");
        return;
      }
     
      if(s.startsWith("Challenge: ") && s.contains(" (adjourned)")) {
        log.fine("Accepting adjourned game.");
        send("accept");
        return;
      }
     
      if (s.startsWith("Challenge: ") && accept) {
        String[] args = StringUtils.split(s);
        int gameLength = Integer.parseInt(args[args.length-2])*60 + Integer.parseInt(args[args.length-1])*40;
       
        if("rated".equals(args[args.length-4])
            && gameLength >= iccConfig.getAcceptMin() && gameLength <= iccConfig.getAcceptMax()) {
          log.fine("Accepting: '" + s + "' " + gameLength + "s");
          send("accept");
        } else {
          log.fine("Rejecting: '" + s + "' " + gameLength + "s");
          send("decline");
        }
        return;
      }
     
      if (s.startsWith("Creating: ")) {
        log.info("Starting game: '" + s + "'");
        List<String> fields = Arrays.asList(StringUtils.split(s));
        playingWhite = iccConfig.getLoginName().equals(fields.get(1));
        opponent = playingWhite ? fields.get(3) : fields.get(1);
        rated = "rated".equals(fields.get(5));
        send("finger " + opponent);
       
        if("Bullet".equals(fields.get(6))) {
          gameType = GameType.BULLET;
        } else if("Blitz".equals(fields.get(6))) {
          gameType = GameType.BLITZ;
        } else if("Standard".equals(fields.get(6))) {
          gameType = GameType.STANDARD;
        }
      }
    }
  }

  private class AbortListener implements ConnectionListener {
    public void message(String s) {
      if (opponent != null
          && s.startsWith(opponent + " would like to abort the game;")
          && !rated) {
        send("abort");
      }
    }
  }

  private class ChatListener implements ConnectionListener {
    public void message(String s) {
     
      if(s.startsWith(iccConfig.getOperatorName() + " ")) {
        return;
      }
     
      String[] fields = StringUtils.split(s);
      if(fields.length >= 3 && "tells".equals(fields[1]) && "you:".equals(fields[2])) {
        send("tell " + fields[0] + " " + talkResponse);
      }
      if(fields.length >= 3 && "says:".equals(fields[1])) {
        send("say " + talkResponse);
      }
    }
  }

  private class CommandListener implements ConnectionListener {
    public void message(String s) {
      if (!s.startsWith(iccConfig.getOperatorName() + " tells you: ")) {
        return;
      }
     
      List<String> words = Arrays.asList(StringUtils.split(s));
      if (words.size() < 4) {
        return;
      }

      if ("do".equals(words.get(3))) {
        StringBuffer buf = new StringBuffer();
        for (int i = 4; i < words.size(); i++) {
          buf.append(words.get(i)).append(" ");
        }
        send(buf.toString().trim());
        tellOp("sent '" + buf.toString().trim() + "'.");
      }

      if ("shutdown".equals(words.get(3))) {
        tellOp("Will shutdown after current game.");
        shutdown = true;
        iccConfig.setReseek(false);
        accept = false;
        setStatus();
      }
     
      if ("accept".equals(words.get(3))) {
        if(words.size() > 4 && "on".equals(words.get(4))) {
          accept = true;
          shutdown = false;
        } else {
          accept = false;
        }
        tellOp("accept " + (accept ? "on" : "off"));
        setStatus();
      }
     
      if (words.size() > 4 && "reseek".equals(words.get(3))) {
        if("on".equals(words.get(4))) {
          iccConfig.setReseek(true);
          shutdown = false;
        } else {
          iccConfig.setReseek(false);
        }
        tellOp("reseek " + (iccConfig.isReseek() ? "on" : "off"));
        setStatus();
      }
    }
  }
 
  private class GameEndedHandler implements BlockHandler {
    public void processBlock(ResponseBlock responseBlock) {
      // Deprecated
    }

    public void processBlock(ResponseBlockLv2 responseBlock) {
      if(responseBlock.getCode() != DG_MY_GAME_RESULT) {
        return;
      }

      log.info("Game ends: [" + responseBlock.getData() + "]");
      currentBoard = null;
      gameNumber = -1;
      opponent = null;

      while(moveThread != null && moveThread.isAlive()) {
        try { Thread.sleep(200); } catch (InterruptedException x) { }
      }
     
      if (shutdown) {
        send("quit");
      } else if (iccConfig.isReseek()) {
        reseek();
      }
    }
  }
 
  private class BoardListener implements ConnectionListener {
    public void message(String s) {
      if (!s.startsWith("<12> ")) {
        return;
      }
     
      final Style12 style12 = new Style12(s);
     
      if(style12.isMyGame()) {
        gameNumber = style12.getGameNumber();
        opponent = style12.getOpponentName();
        playingWhite = (style12.getMyColor() == Piece.WHITE);
        if(style12.isInitialPosition()) {
          currentBoard = new BitBoard().initialise();
        }
      }
     
      if ( ! (style12.getMyRelationToGame() == Style12.REL_ME_TO_MOVE)) {
        return;
      }
     
      if(style12.isFlagged()) {
        gameNumber = -1;
        currentBoard = null;
        return;
      }

      if(style12.getHalfMoveCount() >= 100) {
        log.info("Claiming draw by 50-move rule");
        send("draw");
        return;
      }
     
      if(currentBoard != null && !"none".equals(style12.getPreviousMovePGN())) {
        try {
          currentBoard.makeMove(currentBoard.getMove(PGNUtils.toPgnMoveMap(currentBoard).get(style12.getPreviousMovePGN())));
        } catch (Exception x) {
          log.log(Level.SEVERE, "Apply move failed", x);
        }
      }
     
      if(currentBoard == null || ! currentBoard.equalPosition(style12.getBoard())) {
        log.warning("Out of sync board detected - resetting!");
        currentBoard = style12.getBoard();
      }
     
      if(currentBoard.getRepeatedCount() >= 3) {
        log.info("Claiming draw by 3-fold repitition (opp move)");
        send("draw");
        return;
      }

      String bookMove = openingBook.getBookMove(currentBoard);
      if(bookMove != null) {
        PGNUtils.applyMove(currentBoard, bookMove);
        send(bookMove);
        log.fine("Using book move: " + bookMove);
        return;
      }
     
      if( ! new MoveGenerator(currentBoard).hasNext()) {
        log.fine("Looks like that game is over!");
        return;
      }
     
      Runnable moveMaker = new Runnable() {
        public void run() {
          BitBoard myBoard = currentBoard;
          ChessEngine engine = new ChessEngine();
          if(style12.getGameTime() >= 900) {
            engine.setQuiesce(true);
          }
          String bestMove = engine.getPreferredMove(myBoard);
          if(bestMove != null) {
            if(gameNumber != -1) {
              log.info("Moving: " + PGNUtils.translateMove(myBoard, bestMove));
              if(currentBoard != null) {
                currentBoard.makeMove(currentBoard.getMove(bestMove));
              }
              send(bestMove.toLowerCase());
              if(currentBoard.getRepeatedCount() >= 3) {
                log.info("Claiming draw by 3-fold repitition (my move)");
                send("draw");
              }
            } else {
              log.info("Game not active - move aborted");
            }
          }
          moveThread = null;
        }
      };
     
      moveThread = new Thread(moveMaker);
      moveThread.start();
    }
  }
 
  @Deprecated
  private class BlockListener implements ConnectionListener {
    private StringBuffer currentBlock = new StringBuffer();
    private int blockLevel = 0;
   
    public void message(String s) {
      for(int i = 0; i < s.length(); i++) {
        if(s.charAt(i) == ('Y'&0x1F) && s.charAt(i+1) == '[') {
          if(blockLevel == 0) {
            currentBlock.setLength(0);
          }
          blockLevel++;
        }
        if(blockLevel > 0) {
          currentBlock.append(s.charAt(i));
        }
        if(blockLevel > 0 && s.charAt(i) == ']' && s.charAt(i-1) == ('Y'&0x1F)) {
          blockLevel--;
          if(blockLevel == 0) {
            for(ResponseBlock block: parseBlockResponse(currentBlock.toString())) {
              for(BlockHandler handler: blockHandlers) {
                handler.processBlock(block);
              }
            }
          }
        }
      }
      if(blockLevel > 0) {
        currentBlock.append("\n");
      }
    }
  }
 
  private class BlockLv2Listener implements ConnectionListener {
    private StringBuffer currentBlock = new StringBuffer();
   
    public void message(String s) {
      for(int i = 0; i < s.length(); i++) {
        if(s.charAt(i) == ('Y'&0x1F) && s.charAt(i+1) == '(') {
          currentBlock.setLength(0);
        }
        currentBlock.append(s.charAt(i));
        if(s.charAt(i) == ')' && s.charAt(i-1) == ('Y'&0x1F)) {
          for(ResponseBlockLv2 block: parseBlockResponseLv2(currentBlock.toString())) {
            log.fine("LV2: " + block);
            for(BlockHandler handler: blockHandlers) {
              handler.processBlock(block);
            }
          }
        }
      }
      currentBlock.append("\n");
    }
  }
 
  @Deprecated
  private List<ResponseBlock> parseBlockResponse(String s) {
    List<ResponseBlock> rv = new ArrayList<ResponseBlock>();

    StringBuffer buf = new StringBuffer(s);
   
    Stack<StringBuffer> allBlocks = new Stack<StringBuffer>();
    for(int i = 0; i < buf.length(); i++) {
      if(buf.charAt(i) == ('Y'&0x1F) && buf.charAt(i+1) == '[') {
        allBlocks.push(new StringBuffer());
        i++;
        continue;
      }
      if(buf.charAt(i) == ('Y'&0x1F) && buf.charAt(i+1) == ']') {
        rv.add(new ResponseBlock(allBlocks.pop().toString()));
        i++;
        continue;
      }
      allBlocks.peek().append(buf.charAt(i));
    }
   
    log.fine(String.valueOf(rv));
    return rv;
  }

  private List<ResponseBlockLv2> parseBlockResponseLv2(String s) {
    List<ResponseBlockLv2> rv = new ArrayList<ResponseBlockLv2>();

    StringBuffer buf = new StringBuffer(s);
   
    Stack<StringBuffer> allBlocks = new Stack<StringBuffer>();
    for(int i = 0; i < buf.length(); i++) {
      if(buf.charAt(i) == ('Y'&0x1F) && buf.charAt(i+1) == '(') {
        allBlocks.push(new StringBuffer());
        i++;
        continue;
      }
      if(buf.charAt(i) == ('Y'&0x1F) && buf.charAt(i+1) == ')') {
        rv.add(new ResponseBlockLv2(allBlocks.pop().toString()));
        i++;
        continue;
      }
      allBlocks.peek().append(buf.charAt(i));
    }
   
    log.fine(String.valueOf(rv));
    return rv;
  }

  @Deprecated
  private class ResponseBlock {
    private String data;
    private int code;
    private String user;
    private List<String> lines = new ArrayList<String>();
   
    private ResponseBlock(String s) {
      this.data = s;
      StringTokenizer st = new StringTokenizer(s);
      code = Integer.parseInt(st.nextToken());
      user = st.nextToken();
     
      for(st = new StringTokenizer(s, "\n"); st.hasMoreTokens(); ) {
        lines.add(st.nextToken());
      }
      lines.remove(0); // First line is the code/user
    }
   
    public List<String> getLines() {
      return lines;
    }

    public int getCode() {
      return code;
    }

    public void setCode(int code) {
      this.code = code;
    }

    public String getUser() {
      return user;
    }

    public void setUser(String user) {
      this.user = user;
    }

    public String getData() {
      return data;
    }
    public void setData(String data) {
      this.data = data;
    }

    /**
     * Constructs a <code>String</code> with all attributes
     * in name = value format.
     *
     * @return a <code>String</code> representation
     * of this object.
     */
    public String toString()
    {
      StringBuffer buf = new StringBuffer("");
      buf.append("ResponseBlock(code=").append(code);
      buf.append(",user=").append(user).append(")\n=============\n");
      for(String s: lines) {
        buf.append(s).append("\n");
      }
      buf.append("=============");
      return buf.toString();
    }
  }
 
  private class ResponseBlockLv2 {
    private String data;
    private int code;
   
    private ResponseBlockLv2(String s) {
      this.data = s;
      StringTokenizer st = new StringTokenizer(s);
      code = Integer.parseInt(st.nextToken());
    }
   
    public int getCode() {
      return code;
    }

    public void setCode(int code) {
      this.code = code;
    }

    public String getData() {
      return data;
    }
    public void setData(String data) {
      this.data = data;
    }

    /**
     * Constructs a <code>String</code> with all attributes
     * in name = value format.
     *
     * @return a <code>String</code> representation
     * of this object.
     */
    public String toString()
    {
      StringBuffer buf = new StringBuffer("");
      buf.append("ResponseBlock(code=").append(code).append(" = ").append(data);
      return buf.toString();
    }
  }
}
TOP

Related Classes of nl.zoidberg.calculon.icc.ICCInterface$GameEndedHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
ript>