summaryrefslogtreecommitdiff
path: root/chess-algebraic.el
blob: 6e6ba83d04a6e0c2bf3dce12978b01dc774a6a16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Convert a ply to/from standard chess algebraic notation
;;
;; A thing to deal with in chess is algebraic move notation, such as
;; Nxf3+.  (I leave description of this notation to better manuals
;; than this).  This notation is a shorthand way of representing where
;; a piece is moving from and to, by specifying the piece is involved,
;; where it's going, and whether or not a capture or check is
;; involved.
;;
;; You can convert from algebraic notation to a ply (one pair in most
;; cases, but two for a castle) using the following function (NOTE:
;; POSITION determines which side is on move (by calling
;; `chess-pos-side-to-move')):
;;
;;    (chess-algebraic-to-ply POSITION STRING)
;;
;; The function also checks if a move is legal, and will raise an
;; error if not.
;;
;; To convert from a ply to algebraic notation, use:
;;
;;    (chess-ply-to-algebraic PLY)
;;
;; Castling is determined by the movement of both a king and a rook.
;;
;; Lastly, there is a regexp for quickly checking if a string is in
;; algebraic notation or not, or searching out algebraic strings in a
;; buffer:
;;
;;    chess-algebraic-regexp

;; $Revision$

(require 'chess-pos)

(defconst chess-algebraic-pieces-regexp "[RNBKQ]")

(defconst chess-algebraic-regexp
  (format (concat "\\("
		  "O-O\\(-O\\)?\\|"
		    "\\(%s\\(\\([a-h]\\|[1-8]\\)?\\|[a-h][1-8]\\)\\)?"
		  "\\([x-]\\)?"
		  "\\([a-h][1-8]\\)"
		  "\\(=\\(%s\\)\\)?"
		  "\\)"
		  "\\([#+]\\)?")
	  chess-algebraic-pieces-regexp
	  chess-algebraic-pieces-regexp)
  "A regular expression that matches all possible algebraic moves.
This regexp handles both long and short form.")

(defun chess-algebraic-to-ply (position move)
  "Convert the algebraic notation MOVE for POSITION to a ply."
  (when (string-match chess-algebraic-regexp move)
    (let* ((piece (aref move 0))
	   (mate (match-string 10 move))
	   (changes
	    (if (eq piece ?O)
		(let ((rank (if color 7 0))
		      (long (= (length (match-string 1 move)) 5)))
		  (list (chess-rf-to-index rank 4)
			(chess-rf-to-index rank (if long 2 6))
			(chess-rf-to-index rank (if long 0 7))
			(chess-rf-to-index rank (if long 3 5))))
	      (let ((source (match-string 4 move))
		    (target (chess-coord-to-index (match-string 7 move))))
		(if (and source (= (length source) 2))
		    (list (chess-coord-to-index source) target)
		  (let ((color (chess-pos-side-to-move position))
			candidates which)
		    (unless (< piece ?a)
		      (setq piece ?P))
		    ;; we must use our knowledge of how pieces can
		    ;; move, to determine which piece is meant by the
		    ;; piece indicator
		    (when (setq candidates
				(funcall (car chess-modules) nil nil
					 'search position target
					 (if color piece
					   (downcase piece))))
		      (if (= (length candidates) 1)
			  (list (car candidates) target)
			(if (null source)
			    (error "Clarify piece to move by rank or file")
			  (while candidates
			    (if (if (>= source ?a)
				    (eq (cdar candidates) (- source ?a))
				  (eq (caar candidates) (- 7 (- source ?1))))
				(setq which (car candidates) candidates nil)
			      (setq candidates (cdr candidates))))
			  (if (null which)
			      (error "Could not determine which piece to use")
			    (list which target)))))))))))
      (if mate
	  (nconc changes
		 (list (if (equal mate "#")
			   ':checkmate
			 ':check))))
      (apply 'chess-ply-create position changes))))

(defun chess-ply-to-algebraic (ply &optional long)
  "Convert the given PLY to algebraic notation.
If LONG is non-nil, render the move into long notation."
  (if (null (car (chess-ply-changes ply)))
      ""
    (let* ((pos (chess-ply-pos ply))
	   (changes (chess-ply-changes ply))
	   (from (car changes))
	   (to (cadr changes))
	   (from-piece (chess-pos-piece pos from))
	   (color (chess-pos-side-to-move pos)) str
	   (notation
	    (if (setq str
		      (and (= (upcase from-piece) ?K)
			   (= from (chess-rf-to-index (if color 7 0) 4))
			   (if (= to (chess-rf-to-index (if color 7 0) 6))
			       "O-O"
			     (if (= to (chess-rf-to-index (if color 7 0) 2))
				 "O-O-O"))))
		str
	      (let ((candidates
		     (funcall (car chess-modules)
			      nil nil 'search pos to from-piece))
		    (rank 0) (file 0)
		    (from-rank (/ from 8))
		    (from-file (mod from 8))
		    differentiator notation)
		(when (> (length candidates) 1)
		  (dolist (candidate candidates)
		    (if (= (/ candidate 8) from-rank)
			(setq rank (1+ rank)))
		    (if (= (mod candidate 8) from-file)
			(setq file (1+ file))))
		  (cond
		   ((= file 1)
		    (setq differentiator (+ from-file ?a)))
		   ((= rank 1)
		    (setq differentiator (+ (- 7 from-rank) ?1)))
		   (t (error "Could not differentiate piece"))))
		(concat
		 (unless (= (upcase from-piece) ?P)
		   (char-to-string (upcase from-piece)))
		 (if long
		     (chess-index-to-coord from)
		   (if differentiator
		       (char-to-string differentiator)
		     (if (and (not long) (= (upcase from-piece) ?P)
			      (/= (chess-index-file from)
				  (chess-index-file to)))
			 (char-to-string (+ (chess-index-file from) ?a)))))
		 (if (/= ?  (chess-pos-piece pos to))
		     "x" (if long "-"))
		 (chess-index-to-coord to)
		 (let ((promote (memq ':promote changes)))
		   (if promote
		       (concat "=" (char-to-string (cadr promote))))))))))
      (concat notation
	      (if (memq ':check changes) "+"
		(if (memq ':checkmate changes) "#"))))))

(provide 'chess-algebraic)

;;; chess-algebraic.el ends here