;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Keyboard entry of algebraic notation, using shortcut notation ;; ;; This scheme was adapted from the way SCID ;; (http://scid.sourceforge.net), by Shane Hudson, behaves. It's the ;; only way to move your pieces around! ;; (defvar chess-input-move-string "") (defvar chess-input-moves-pos nil) (defvar chess-input-moves nil) (defvar chess-input-position-function nil) (defvar chess-input-move-function nil) (make-variable-buffer-local 'chess-input-move-string) (make-variable-buffer-local 'chess-input-moves-pos) (make-variable-buffer-local 'chess-input-moves) (make-variable-buffer-local 'chess-input-position-function) (make-variable-buffer-local 'chess-input-move-function) (defun chess-input-test-move (move-ply) "Return the given MOVE if it matches the user's current input." (let* ((move (cdr move-ply)) (i 0) (x 0) (l (length move)) (xl (length chess-input-move-string)) (match t)) (unless (or (and (equal (downcase chess-input-move-string) "ok") (string-match "\\`O-O[+#]?\\'" move)) (and (equal (downcase chess-input-move-string) "oq") (string-match "\\`O-O-O[+#]?\\'" move))) (while (and (< i l) (< x xl)) (let ((move-char (aref move i)) (entry-char (aref chess-input-move-string x))) (if (and (= move-char ?x) (/= entry-char ?x)) (setq i (1+ i)) (if (/= entry-char (if (< entry-char ?a) move-char (downcase move-char))) (setq match nil i l) (setq i (1+ i) x (1+ x))))))) (if match move-ply))) (defsubst chess-input-display-moves (&optional move-list) (if (> (length chess-input-move-string) 0) (message "[%s] %s" chess-input-move-string (mapconcat 'cdr (or move-list (delq nil (mapcar 'chess-input-test-move (cdr chess-input-moves)))) " ")))) (defun chess-input-shortcut-delete () (interactive) (when (and chess-input-move-string (stringp chess-input-move-string) (> (length chess-input-move-string) 0)) (setq chess-input-move-string (substring chess-input-move-string 0 (1- (length chess-input-move-string)))) (chess-input-display-moves))) (defun chess-input-shortcut (&optional display-only) (interactive) (let* ((position (funcall chess-input-position-function)) (color (chess-pos-side-to-move position)) char) (unless (memq last-command '(chess-input-shortcut chess-input-shortcut-delete)) (setq chess-input-move-string nil)) (unless display-only (setq chess-input-move-string (concat chess-input-move-string (char-to-string last-command-char)))) (unless (and chess-input-moves (eq position chess-input-moves-pos) (or (> (length chess-input-move-string) 1) (eq (car chess-input-moves) last-command-char))) (setq char (if (eq (downcase last-command-char) ?o) ?k last-command-char) chess-input-moves-pos position chess-input-moves (cons char (sort (mapcar (function (lambda (ply) (cons ply (chess-ply-to-algebraic ply)))) (if (eq char ?b) (append (chess-legal-plies position :piece (if color ?P ?p) :file 1) (chess-legal-plies position :piece (if color ?B ?b))) (if (and (>= char ?a) (<= char ?h)) (chess-legal-plies position :piece (if color ?P ?p) :file (- char ?a)) (chess-legal-plies position :piece (if color (upcase char) (downcase char)))))) (function (lambda (left right) (string-lessp (cdr left) (cdr right))))))))) (let ((moves (delq nil (mapcar 'chess-input-test-move (cdr chess-input-moves))))) (cond ((or (= (length moves) 1) ;; if there is an exact match except for case, it must be an ;; abiguity between a bishop and a b-pawn move. In this ;; case, always take the b-pawn move; to select the bishop ;; move, use B to begin the keyboard shortcut (and (= (length moves) 2) (string= (downcase (cdr (car moves))) (downcase (cdr (cadr moves)))) (setq moves (cdr moves)))) (funcall chess-input-move-function nil (caar moves)) (setq chess-input-move-string nil chess-input-moves nil chess-input-moves-pos nil)) ((null moves) (chess-input-shortcut-delete)) (t (chess-input-display-moves moves))))) (provide 'chess-input) ;;; chess-input.el ends here