;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
+;;; Commentary:
+
+;; URL: http://en.wikipedia.org/wiki/Universal_Chess_Interface
+
;;; Code:
(require 'chess-common)
+(require 'chess-polyglot)
+
+(defgroup chess-uci nil
+ "Customisations for Chess engines based on the UCI protocol"
+ :group 'chess)
+
+(defcustom chess-uci-polyglot-book-file nil
+ "The path to a polyglot binary opening book file."
+ :group 'chess-uci
+ :type '(choice (const :tag "Not specified" nil) file))
+
+(defvar chess-uci-book nil
+ "A (polyglot) opening book object.
+See `chess-uci-polyglot-book-file' for details on how to enable this.")
-(defvar chess-uci-move-regexp "[a-h][1-8][a-h][1-8][nbrq]?"
- "A regular expression matching a UCI move.")
+(defvar chess-uci-long-algebraic-regexp "\\([a-h][1-8]\\)\\([a-h][1-8]\\)\\([nbrq]\\)?"
+ "A regular expression matching a UCI log algebraic move.")
+
+(defun chess-uci-long-algebraic-to-ply (position move)
+ "Convert the long algebraic notation MOVE for POSITION to a ply."
+ (assert (vectorp position))
+ (assert (stringp move))
+ (let ((case-fold-search nil))
+ (when (string-match chess-uci-long-algebraic-regexp move)
+ (let ((color (chess-pos-side-to-move position))
+ (from (chess-coord-to-index (match-string 1 move)))
+ (to (chess-coord-to-index (match-string 2 move)))
+ (promotion (match-string 3 move)))
+ (apply #'chess-ply-create position nil
+ (if (and (= from (chess-pos-king-index position color))
+ (= (chess-index-rank from) (chess-index-rank to))
+ (> (abs (- (chess-index-file from)
+ (chess-index-file to))) 1))
+ (chess-ply-castling-changes
+ position
+ (< (- (chess-index-file to) (chess-index-file from)) 0))
+ (nconc (list from to)
+ (when promotion
+ (list :promote (upcase (aref promotion 0)))))))))))
+
+(defsubst chess-uci-convert-long-algebraic (move)
+ "Convert long algebraic MOVE to a ply in reference to the engine position.
+If conversion fails, this function fired an 'illegal event."
+ (or (chess-uci-long-algebraic-to-ply (chess-engine-position nil) move)
+ (chess-engine-command nil 'illegal)))
+
+(defvar chess-uci-regexp-alist
+ (list
+ (cons "^id\\s-+name\\s-+\\(.+\\)$"
+ (function
+ (lambda ()
+ (setq-local chess-engine-opponent-name (match-string 1))
+ 'once)))
+ (cons (concat "^bestmove\\s-+\\(" chess-uci-long-algebraic-regexp "\\)")
+ (function
+ (lambda ()
+ (funcall chess-engine-response-handler 'move
+ (chess-uci-convert-long-algebraic (match-string 1)))))))
+ "Patterns matching responses of a standard UCI chess engine.")
(defun chess-uci-position (game)
+ "Convert the current GAME position to a UCI position command string."
(concat "position fen " (chess-pos-to-fen (chess-game-pos game 0) t)
" moves " (mapconcat (lambda (ply)
(let ((source (chess-ply-source ply))
"\n"))
(defun chess-uci-handler (game event &rest args)
+ "Default handler for UCI based engines."
(unless chess-engine-handling-event
(cond
+ ((eq event 'initialize)
+ (when chess-uci-polyglot-book-file
+ (unless chess-uci-book
+ (setq chess-uci-book (chess-polyglot-book-open
+ chess-uci-polyglot-book-file))))
+ (apply #'chess-common-handler game event args))
+
((eq event 'move)
(when (= 1 (chess-game-index game))
(chess-game-set-tag game "White" chess-full-name)
(chess-game-set-tag game "Black" chess-engine-opponent-name))
- (chess-engine-send nil (concat (chess-uci-position game) "go\n"))
+ (let ((book-plies (and chess-uci-book (bufferp chess-uci-book)
+ (buffer-live-p chess-uci-book)
+ (chess-polyglot-book-plies chess-uci-book
+ (chess-game-pos game)))))
+ (if book-plies
+ (let ((chess-display-handling-event nil))
+ (funcall chess-engine-response-handler 'move (car book-plies)))
+ (chess-engine-send nil (concat (chess-uci-position game) "go\n"))))
+
(if (chess-game-over-p game)
(chess-game-set-data game 'active nil)))