1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;; Convert a ply to/from standard chess algebraic notation
5 ;; A thing to deal with in chess is algebraic move notation, such as
6 ;; Nxf3+. (I leave description of this notation to better manuals
7 ;; than this). This notation is a shorthand way of representing where
8 ;; a piece is moving from and to, by specifying the piece is involved,
9 ;; where it's going, and whether or not a capture or check is
12 ;; You can convert from algebraic notation to a ply (one pair in most
13 ;; cases, but two for a castle) using the following function (NOTE:
14 ;; POSITION determines which side is on move (by calling
15 ;; `chess-pos-side-to-move')):
17 ;; (chess-algebraic-to-ply POSITION STRING)
19 ;; The function also checks if a move is legal, and will raise an
22 ;; To convert from a ply to algebraic notation, use:
24 ;; (chess-ply-to-algebraic PLY)
26 ;; Castling is determined by the movement of both a king and a rook.
28 ;; Lastly, there is a regexp for quickly checking if a string is in
29 ;; algebraic notation or not, or searching out algebraic strings in a
32 ;; chess-algebraic-regexp
38 (defconst chess-algebraic-pieces-regexp "[RNBKQ]")
40 (defconst chess-algebraic-regexp
50 chess-algebraic-pieces-regexp
51 chess-algebraic-pieces-regexp)
52 "A regular expression that matches all possible algebraic moves.
53 This regexp handles both long and short form.")
55 (defconst chess-algebraic-regexp-entire
56 (concat chess-algebraic-regexp "$"))
58 (chess-message-catalog 'english
59 '((clarify-piece . "Clarify piece to move by rank or file")
60 (could-not-clarify . "Could not determine which piece to use")
61 (could-not-diff . "Could not differentiate piece")
62 (no-candidates . "There are no candidate moves for '%s'")))
64 (defun chess-algebraic-to-ply (position move &optional trust)
65 "Convert the algebraic notation MOVE for POSITION to a ply."
66 (when (string-match chess-algebraic-regexp-entire move)
67 (let ((color (chess-pos-side-to-move position))
68 (mate (match-string 9 move))
72 (let ((long (= (length (match-string 1 move)) 5)))
73 (if (chess-pos-can-castle position (if long (if color ?Q ?q)
75 (setq changes (chess-ply-create-castle position long))))
76 (let ((promotion (match-string 8 move)))
78 (let ((source (match-string 4 move))
79 (target (chess-coord-to-index (match-string 6 move))))
80 (if (and source (= (length source) 2))
81 (list (chess-coord-to-index source) target)
82 (if (= (length source) 0)
84 (setq source (aref source 0)))
85 (let (candidates which)
87 (setq source piece piece ?P))
88 ;; we must use our knowledge of how pieces can
89 ;; move, to determine which piece is meant by the
92 (chess-search-position position target
95 (if (= (length candidates) 1)
96 (list (car candidates) target)
98 (chess-error 'clarify-piece)
99 (nconc changes (list :which source))
101 (if (if (>= source ?a)
102 (eq (chess-index-file (car candidates))
104 (eq (chess-index-rank (car candidates))
105 (- 7 (- source ?1))))
106 (setq which (car candidates)
108 (setq candidates (cdr candidates))))
110 (chess-error 'could-not-clarify)
111 (list which target))))
112 (chess-error 'no-candidates move))))))
114 (nconc changes (list :promote (aref promotion 0))))))
118 (nconc changes (list (if (equal mate "#") :checkmate :check))))
119 (nconc changes (list :valid)))
121 (or ply (apply 'chess-ply-create position changes)))))
123 (defun chess-ply-to-algebraic (ply &optional long)
124 "Convert the given PLY to algebraic notation.
125 If LONG is non-nil, render the move into long notation."
126 (if (let ((source (chess-ply-source ply)))
127 (or (null source) (symbolp source)))
129 (or (and (chess-ply-keyword ply :castle) "O-O")
130 (and (chess-ply-keyword ply :long-castle) "O-O-O")
131 (let* ((pos (chess-ply-pos ply))
132 (from (chess-ply-source ply))
133 (to (chess-ply-target ply))
134 (from-piece (chess-pos-piece pos from))
135 (color (chess-pos-side-to-move pos))
137 (from-rank (/ from 8))
138 (from-file (mod from 8))
139 (differentiator (cdr (memq :which (chess-ply-changes ply)))))
140 (unless differentiator
141 (let ((candidates (chess-search-position pos to from-piece)))
142 (when (> (length candidates) 1)
143 (dolist (candidate candidates)
144 (if (= (/ candidate 8) from-rank)
145 (setq rank (1+ rank)))
146 (if (= (mod candidate 8) from-file)
147 (setq file (1+ file))))
150 (setq differentiator (+ from-file ?a)))
152 (setq differentiator (+ (- 7 from-rank) ?1)))
153 (t (chess-error 'could-not-diff))))))
155 (unless (= (upcase from-piece) ?P)
156 (char-to-string (upcase from-piece)))
158 (chess-index-to-coord from)
160 (char-to-string differentiator)
161 (if (and (not long) (= (upcase from-piece) ?P)
162 (/= (chess-index-file from)
163 (chess-index-file to)))
164 (char-to-string (+ (chess-index-file from) ?a)))))
165 (if (or (/= ? (chess-pos-piece pos to))
166 (chess-ply-keyword ply :en-passant))
168 (chess-index-to-coord to)
169 (let ((promote (chess-ply-keyword ply :promote)))
171 (concat "=" (char-to-string
172 (upcase (cadr promote))))))
173 (if (chess-ply-keyword ply :check) "+"
174 (if (chess-ply-keyword ply :checkmate) "#")))))))
176 (provide 'chess-algebraic)
178 ;;; chess-algebraic.el ends here