]> code.delx.au - gnu-emacs-elpa/blob - chess-fen.el
use zerop
[gnu-emacs-elpa] / chess-fen.el
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;
3 ;; Convert a chess position to/from FEN notation
4 ;;
5 ;; FEN notation encodes a chess position using a simple string. The
6 ;; format is:
7 ;;
8 ;; POSITION SIDE FLAGS
9 ;;
10 ;; The POSITION gives all eight ranks, by specifying a letter for each
11 ;; piece on the position, and a number for any intervening spaces.
12 ;; Trailing spaces need not be counted. Uppercase letters signify
13 ;; white, and lowercase black. For example, if your position only had
14 ;; a black king on d8, your POSITION string would be:
15 ;;
16 ;; 3k////////
17 ;;
18 ;; For the three spaces (a, b and c file), the black king, and then
19 ;; all the remaining ranks (which are all empty, so their spaces can
20 ;; be ignored).
21 ;;
22 ;; The SIDE is w or b, to indicate whose move it is.
23 ;;
24 ;; The FLAGS can contain K, Q, k or q, to signify whether the white or
25 ;; black king can still castle on the king or queen side. You can
26 ;; also have coordinates, such as e4, a5, to specify which pawns may
27 ;; be captured by en passant.
28 ;;
29 ;; The starting chess position always looks like this:
30 ;;
31 ;; rnbqkbnr/pppppppp/////PPPPPPPP/RNBQKBNR/ w KQkq -
32 ;;
33 ;; And in "full" mode (where all spaces are accounted for):
34 ;;
35 ;; rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -
36 ;;
37 ;; It may also have the current game sequence appended, but this
38 ;; relate to the game, not the position.
39 ;;
40
41 (defun chess-fen-to-pos (fen)
42 "Convert a FEN-like notation string to a chess position."
43 (assert (stringp fen))
44 (let ((i 0) (l (length fen))
45 (rank 0) (file 0) (c ?0)
46 (position (chess-pos-create t))
47 error (space-count 0))
48 (setq c (aref fen i))
49 (while (and (null error)
50 (/= c ? ) (< i l))
51 (cond
52 ((= c ?/)
53 (setq file 0 rank (1+ rank)))
54 ((and (>= c ?1) (<= c ?9))
55 (setq file (+ file (- c ?0))))
56 ((memq (upcase c) '(?K ?Q ?B ?N ?R ?P))
57 (chess-pos-set-piece position (chess-rf-to-index rank file) c)
58 (setq file (1+ file)))
59 (t
60 (setq error t)))
61 (setq i (1+ i) c (aref fen i)))
62 (if (= (aref fen i) ? )
63 (setq i (1+ i)))
64 (if (memq (aref fen i) '(?b ?w))
65 (progn
66 (chess-pos-set-side-to-move position (= (aref fen i) ?w))
67 (setq i (+ i 2)))
68 (setq error t))
69 (setq c (aref fen i))
70 (while (and (null error)
71 (< space-count 2) (< i l))
72 (cond
73 ((= c ?-))
74 ((= c ? ) (setq space-count (1+ space-count)))
75 ((= c ?K) (chess-pos-set-can-castle position ?K t))
76 ((= c ?Q) (chess-pos-set-can-castle position ?Q t))
77 ((= c ?k) (chess-pos-set-can-castle position ?k t))
78 ((= c ?q) (chess-pos-set-can-castle position ?q t))
79 ((and (>= c ?a) (<= c ?h))
80 (chess-pos-set-en-passant position (chess-coord-to-index
81 (substring fen i (+ i 2))))
82 (setq i (1+ i)))
83 (t
84 (setq error t)))
85 (setq i (1+ i) c (and (< i l) (aref fen i))))
86 (unless error
87 position)))
88
89 (defun chess-pos-to-fen (position &optional full)
90 "Convert a chess POSITION to FEN-like notation.
91 If FULL is non-nil, represent trailing spaces as well."
92 (assert (vectorp position))
93 (let ((blank 0) (str "") output)
94 (dotimes (rank 8)
95 (dotimes (file 8)
96 (let ((p (chess-pos-piece position (chess-rf-to-index rank file))))
97 (if (= p ? )
98 (setq blank (1+ blank))
99 (if (> blank 0)
100 (setq str (concat str (int-to-string blank)) blank 0))
101 (setq str (concat str (char-to-string p))))))
102 (if (and full (> blank 0))
103 (setq str (concat str (int-to-string blank))))
104 (setq blank 0 str (concat str "/")))
105 (setq str (if (chess-pos-side-to-move position)
106 (concat str " w ")
107 (concat str " b ")))
108 (if (chess-pos-can-castle position ?K)
109 (setq str (concat str "K") output t))
110 (if (chess-pos-can-castle position ?Q)
111 (setq str (concat str "Q") output t))
112 (if (chess-pos-can-castle position ?k)
113 (setq str (concat str "k") output t))
114 (if (chess-pos-can-castle position ?q)
115 (setq str (concat str "q") output t))
116 (if output
117 (setq str (concat str " "))
118 (setq str (concat str "- ")))
119 (let ((index (chess-pos-en-passant position)))
120 (if index
121 (concat str (chess-index-to-coord index))
122 (concat str "-")))))
123
124 (provide 'chess-fen)
125
126 ;;; chess-fen.el ends here