1 ;;; chess-plain.el --- Plain ASCII chess display
3 ;; Copyright (C) 2002-2005, 2014 Free Software Foundation, Inc.
5 ;; Author: John Wiegley <johnw@gnu.org>
6 ;; Maintainer: Mario Lang <mlang@delysid.org>
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
24 ;; This chess display module presents a very compact plain character-based
25 ;; view of the chessboard. Contrary to the classic chess-ics1, it does not draw
26 ;; a border around squares.
28 ;; The characters used to display individual pieces can be customized,
29 ;; see `chess-plain-piece-chars' for a number of suggestions. Other aspects of
30 ;; the chessboard are also customizable, see customization group `chess-plain'.
34 (require 'chess-display)
36 (defgroup chess-plain nil
37 "A minimal, customizable ASCII display."
39 :link '(custom-manual "(chess)Plain ASCII diagram displays"))
41 (defcustom chess-plain-border-style [?+ ?- ?+ ?| ?| ?+ ?- ?+]
42 "If non-nil, a vector describing the border characters."
44 :type '(choice (const :tag "No border" nil)
45 (vector :tag "Plain ASCII"
46 (const :value ?+ :tag "Upper left corner: +")
47 (const :value ?- :tag "Upper border: -")
48 (const :value ?+ :tag "Upper right corner: +")
49 (const :value ?| :tag "Left border: |")
50 (const :value ?| :tag "Right border: |")
51 (const :value ?+ :tag "Lower left corrner: +")
52 (const :value ?- :tag "Lower border: -")
53 (const :value ?+ :tag "Lower right corner: +"))
54 (vector :tag "Unicode box drawing characters"
55 (const :value ?┌ :tag "Upper left corner: ┌")
56 (const :value ?╶ :tag "Upper border: ╶")
57 (const :value ?┐ :tag "Upper right corner: ┐")
58 (const :value ?╷ :tag "Left border: ╷")
59 (const :value ?╷ :tag "Right border: ╷")
60 (const :value ?└ :tag "Lower left corrner: └")
61 (const :value ?╶ :tag "Lower border: ╶")
62 (const :value ?┘ :tag "Lower right corner: ┘"))
64 (character :tag "Upper left corner")
65 (character :tag "Upper border")
66 (character :tag "Upper right corner")
67 (character :tag "Left border")
68 (character :tag "Right border")
69 (character :tag "Lower left corner")
70 (character :tag "Lower border")
71 (character :tag "Lower right corner"))))
73 (defcustom chess-plain-black-square-char ?.
74 "Character used to indicate empty black squares."
78 (defcustom chess-plain-white-square-char ?.
79 "Character used to indicate empty white squares."
83 (defcustom chess-plain-piece-chars '((?K . ?K)
95 "Alist of pieces and their corresponding characters.
96 Characters defined here should make sense in respect to the current setting
97 of `chess-plain-upcase-indicates'."
99 :type '(choice (list :tag "White has uppercase english letters and black has lowercase english letters"
100 (const :tag "White King: K" (?K . ?K))
101 (const :tag "White Queen: Q" (?Q . ?Q))
102 (const :tag "White Rook: R" (?R . ?R))
103 (const :tag "White Bishop: B" (?B . ?B))
104 (const :tag "White Knight: N" (?N . ?N))
105 (const :tag "White Pawn: P" (?P . ?P))
106 (const :tag "Black King: k" (?k . ?k))
107 (const :tag "Black Queen: q" (?q . ?q))
108 (const :tag "Black Rook: r" (?r . ?r))
109 (const :tag "Black Bishop: b" (?b . ?b))
110 (const :tag "Black Knight: n" (?n . ?n))
111 (const :tag "Black Pawn: p" (?p . ?p)))
112 (list :tag "White has uppercase german letters and black has lowercase german letters"
113 (const :tag "White King: K" (?K . ?K))
114 (const :tag "White Queen: D" (?Q . ?D))
115 (const :tag "White Rook: T" (?R . ?T))
116 (const :tag "White Bishop: L" (?B . ?L))
117 (const :tag "White Knight: S" (?N . ?S))
118 (const :tag "White Pawn: B" (?P . ?B))
119 (const :tag "Black King: k" (?k . ?k))
120 (const :tag "Black Queen: d" (?q . ?d))
121 (const :tag "Black Rook: t" (?r . ?t))
122 (const :tag "Black Bishop: l" (?b . ?l))
123 (const :tag "Black Knight: s" (?n . ?s))
124 (const :tag "Black Pawn: b" (?p . ?b)))
125 (list :tag "White has english letters and black has german letters"
126 (const :tag "White King: K" (?K . ?K))
127 (const :tag "White Queen: Q" (?Q . ?Q))
128 (const :tag "White Rook: R" (?R . ?R))
129 (const :tag "White Bishop: B" (?B . ?B))
130 (const :tag "White Knight: N" (?N . ?N))
131 (const :tag "White Pawn: P" (?P . ?P))
132 (const :tag "Black King: J" (?k . ?J))
133 (const :tag "Black Queen: D" (?q . ?D))
134 (const :tag "Black Rook: T" (?r . ?T))
135 (const :tag "Black Bishop: L" (?b . ?L))
136 (const :tag "Black Knight: S" (?n . ?S))
137 (const :tag "Black Pawn: X" (?p . ?X)))
138 (list :tag "White has german letters and black has english letters"
139 (const :tag "White King: J" (?K . ?J))
140 (const :tag "White Queen: D" (?Q . ?D))
141 (const :tag "White Rook: T" (?R . ?T))
142 (const :tag "White Bishop: L" (?B . ?L))
143 (const :tag "White Knight: S" (?N . ?S))
144 (const :tag "White Pawn: X" (?P . ?X))
145 (const :tag "Black King: K" (?k . ?K))
146 (const :tag "Black Queen: Q" (?q . ?Q))
147 (const :tag "Black Rook: R" (?r . ?R))
148 (const :tag "Black Bishop: B" (?b . ?B))
149 (const :tag "Black Knight: N" (?n . ?N))
150 (const :tag "Black Pawn: P" (?p . ?P)))
151 (list :tag "Unicode figure pieces"
152 (const :tag "White King: ♔" (?K . ?♔))
153 (const :tag "White Queen: ♕" (?Q . ?♕))
154 (const :tag "White Rook: ♖" (?R . ?♖))
155 (const :tag "White Bishop: ♗" (?B . ?♗))
156 (const :tag "White Knight: ♘" (?N . ?♘))
157 (const :tag "White Pawn: ♙" (?P . ?♙))
158 (const :tag "Black King: ♚" (?k . ?♚))
159 (const :tag "Black Queen: ♛" (?q . ?♛))
160 (const :tag "Black Rook: ♜" (?r . ?♜))
161 (const :tag "Black Bishop: ♝" (?b . ?♝))
162 (const :tag "Black Knight: ♞" (?n . ?♞))
163 (const :tag "Black Pawn: ♟" (?p . ?♟)))
164 (list :tag "User defined"
166 (const :format "" ?K) (character :tag "White King"))
168 (const :format "" ?Q) (character :tag "White Queen"))
170 (const :format "" ?R) (character :tag "White Rook"))
172 (const :format "" ?B) (character :tag "White Bishop"))
174 (const :format "" ?N) (character :tag "White Knight"))
176 (const :format "" ?P) (character :tag "White Pawn"))
178 (const :format "" ?k) (character :tag "Black King"))
180 (const :format "" ?q) (character :tag "Black Queen"))
182 (const :format "" ?r) (character :tag "Black Rook"))
184 (const :format "" ?b) (character :tag "Black Bishop"))
186 (const :format "" ?n) (character :tag "Black Knight"))
188 (const :format "" ?p) (character :tag "Black Pawn")))
189 (function :tag "Function")))
191 (defcustom chess-plain-upcase-indicates 'color
192 "Defines what a upcase char should indicate.
193 The default is 'color, meaning a upcase char is a white piece, a
194 lowercase char a black piece. Possible values: 'color (default),
195 'square-color. If set to 'square-color, a uppercase character
196 indicates a piece on a black square. (Note that you also need to
197 modify `chess-plain-piece-chars' to avoid real confusion.)"
199 :type '(choice (const :tag "Upcase indicates white piece" color)
200 (const :tag "Upcase indicates black square" square-color)))
202 (defcustom chess-plain-spacing 1
203 "Number of spaces between files."
207 (defface chess-plain-black-face
208 '((((class color) (background light)) (:foreground "Black"))
209 (((class color) (background dark)) (:foreground "Green"))
211 "The face used for black pieces on the ASCII display."
214 (defface chess-plain-white-face
215 '((((class color) (background light)) (:foreground "Blue"))
216 (((class color) (background dark)) (:foreground "Yellow"))
218 "The face used for white pieces on the ASCII display."
221 (defface chess-plain-highlight-face
222 '((((class color) (background light)) (:background "#add8e6"))
223 (((class color) (background dark)) (:background "#add8e6")))
224 "Face to use for highlighting pieces that have been selected."
227 (defcustom chess-plain-popup-function 'chess-plain-popup
228 "The function used to popup a chess-plain display."
232 (defcustom chess-plain-separate-frame nil
233 "If non-nil, display the chessboard in its own frame."
239 (defun chess-plain-customize ()
240 "Show possible customisations for the plain chessboard display."
242 (customize-group 'chess-plain))
244 (defun chess-plain-handler (event &rest args)
246 ((eq event 'initialize) t)
247 ((eq event 'popup) (funcall chess-plain-popup-function))
248 (t (let ((handler (intern-soft (concat "chess-plain-" (symbol-name event)))))
249 (when handler (apply handler args))))))
251 (defun chess-plain-popup ()
252 (if chess-plain-separate-frame
253 (chess-display-popup-in-frame 9 (* (1+ chess-plain-spacing) 8)
255 (chess-display-popup-in-window)))
257 (defun chess-plain-piece-text (piece rank file)
258 (let ((white-square (zerop (% (+ file rank) 2))))
261 chess-plain-white-square-char
262 chess-plain-black-square-char)
263 (let* ((pchar (cdr (assq piece chess-plain-piece-chars)))
265 (if (eq chess-plain-upcase-indicates 'square-color)
266 (if white-square (downcase pchar) (upcase pchar))
268 (add-text-properties 0 1 (list 'face (if (> piece ?a)
269 'chess-plain-black-face
270 'chess-plain-white-face)) p)
273 (defun chess-plain-draw-square (pos piece index)
274 "Draw a piece at POS on an already drawn display."
278 (insert (chess-plain-piece-text piece (chess-index-rank index)
279 (chess-index-file index)))
280 (add-text-properties pos (point) (list 'chess-coord index))))
282 (defun chess-plain-draw (position perspective)
283 "Draw the given POSITION from PERSPECTIVE's point of view.
284 PERSPECTIVE is t for white or nil for black."
285 (let ((inhibit-redisplay t)
288 (let* ((inverted (not perspective))
289 (rank (if inverted 7 0))
290 (file (if inverted 7 0)))
291 (when chess-plain-border-style
292 (insert ? (aref chess-plain-border-style 0)
293 (make-string (+ 8 (* 7 chess-plain-spacing))
294 (aref chess-plain-border-style 1))
295 (aref chess-plain-border-style 2) ?\n))
296 (while (if inverted (>= rank 0) (< rank 8))
297 (when chess-plain-border-style
298 (insert (number-to-string (- 8 rank))
299 (aref chess-plain-border-style 3)))
300 (while (if inverted (>= file 0) (< file 8))
301 (let ((piece (chess-pos-piece position
302 (chess-rf-to-index rank file)))
304 (insert (chess-plain-piece-text piece rank file))
305 (add-text-properties begin (point)
307 (chess-rf-to-index rank file)))
308 (when (if inverted (>= file 1) (< file 7))
309 (insert (make-string chess-plain-spacing ? ))))
310 (setq file (if inverted (1- file) (1+ file))))
311 (when chess-plain-border-style
312 (insert (aref chess-plain-border-style 4)))
314 (setq file (if inverted 7 0)
315 rank (if inverted (1- rank) (1+ rank))))
316 (if chess-plain-border-style
317 (insert ? (aref chess-plain-border-style 5)
318 (make-string (+ 8 (* 7 chess-plain-spacing))
319 (aref chess-plain-border-style 6))
320 (aref chess-plain-border-style 7) ?\n
322 (let ((string (if (not inverted) "abcdefgh" "hgfedcba")))
323 (mapconcat 'string (string-to-list string)
324 (make-string chess-plain-spacing ? )))))
325 (set-buffer-modified-p nil)
328 (defun chess-plain-highlight (index &optional mode)
329 (let ((pos (chess-display-index-pos nil index)))
330 (put-text-property pos (1+ pos) 'face
333 'chess-plain-highlight-face)
335 (chess-display-get-face mode))))))
337 (provide 'chess-plain)
339 ;;; chess-plain.el ends here