summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO23
-rw-r--r--chess-announce.el8
-rw-r--r--chess-display.el105
-rw-r--r--chess-none.el28
-rw-r--r--chess-sound.el101
-rw-r--r--chess.el17
6 files changed, 213 insertions, 69 deletions
diff --git a/TODO b/TODO
index c82bf82..14340f9 100644
--- a/TODO
+++ b/TODO
@@ -25,23 +25,17 @@
(chess-search-position ... "f1" ?R) should return the h1 rook, if it
could reach that square legally by castling.
-- In M-x chess, if chess-images is being used, ensure that
- chess-images-directory is valid, otherwise fall back on chess-ics1.
-
-- Only use "chess-engine-game" in chess-engine.el. All other modules
- should call "(chess-engine-game nil)".
+- There is an ambiguity in keyboard shortcutting between Bxc6 and bxc6
- In chess-fen-to-pos, syntax check incoming FEN strings
-- Add the capacity to abort/resign when there is no engine
-
- Detect draw/resign/abort/retract, etc., from ICS and common engines
-- Make use of the my-color data in chess-game.el to ensure that I only
- do what I should be doing
+- Make use of the my-color and active data in chess-game.el to ensure
+ that I only do what I should be doing
- Add `chess-display-read-only', to indicate that no changes can be
- made to the displayed chess board. This would be good for cloned
+ made to a displayed chess board. This would be good for cloned
displays, and when observing a bot (or two engines head-to-head).
- Remove `chess-illegal', and just use plain error.
@@ -53,14 +47,11 @@
- Have chess-display-popup-in-frame autosize based on the content (if
possible)
-- There is an ambiguity in keyboard shortcutting between Bxc6 and bxc6
-
-- Remove chess-display-boring-events, and instead use (not
- chess-display-interesting-events). Otherwise, chess-display.el has
- to care about an ever growing set of non-display events.
-
----------------------------------------------------------------------
+- Read-only mode needs to be a bit more vigorous. There's nothing
+ preventing the user from using M-x commands.
+
- Add an engine function for obtaining an evaluation of the current
position. Then, allow M-x chess to startup a non-game oriented
engine, solely for the purpose of submitting position evaluations,
diff --git a/chess-announce.el b/chess-announce.el
index ff1c7f1..27554a8 100644
--- a/chess-announce.el
+++ b/chess-announce.el
@@ -18,7 +18,8 @@
(autoload 'festival-kill-process "festival")
(defvar chess-announce-functions
- (if (executable-find "festival")
+ (if (and (executable-find "festival")
+ (not (featurep 'emacspeak)))
(if (fboundp 'festival-say-string)
'(festival-start-process festival-say-string festival-kill-process)
'(ignore chess-announce-festival ignore))
@@ -45,9 +46,8 @@ See `chess-display-type' for the different kinds of displays."
(pos (chess-ply-pos ply)))
(unless (eq (chess-game-data game 'my-color)
(chess-pos-side-to-move pos))
- (let* ((changes (chess-ply-changes ply))
- (source (car changes))
- (target (cadr changes))
+ (let* ((source (chess-ply-source ply))
+ (target (chess-ply-target ply))
(s-piece (chess-pos-piece pos source))
(t-piece (chess-pos-piece pos target))
text)
diff --git a/chess-display.el b/chess-display.el
index a6b10f0..edde9d6 100644
--- a/chess-display.el
+++ b/chess-display.el
@@ -80,14 +80,14 @@
,@body)
,@body)))
-(defun chess-display-create (style perspective)
+(defun chess-display-create (style perspective &optional read-only)
"Create a chess display, for displaying chess objects."
(let* ((name (symbol-name style))
(handler (intern-soft (concat name "-handler"))))
(unless handler
(error "There is no such chessboard display style '%s'" name))
(with-current-buffer (generate-new-buffer "*Chessboard*")
- (chess-display-mode)
+ (chess-display-mode read-only)
(funcall handler 'initialize)
(setq chess-display-style style
chess-display-perspective perspective
@@ -368,55 +368,55 @@ that is supported by most displays, and is the default mode."
;; Event handler
;;
-(defcustom chess-display-momentous-events
- '(orient setup-game pass move game-over resign)
- "Events that will cause the 'main' display to popup."
+(defcustom chess-display-interesting-events nil
+ "Events which will cause a display refresh."
:type '(repeat symbol)
:group 'chess-display)
-(defcustom chess-display-boring-events
- '(set-data set-tags set-tag draw abort undo)
- "Events which will not even cause a refresh of the display."
+(defcustom chess-display-momentous-events
+ '(orient setup-game pass move game-over resign)
+ "Events that will refresh, and cause 'main' displays to popup.
+These are displays for which `chess-display-set-main' has been
+called."
:type '(repeat symbol)
:group 'chess-display)
(defun chess-display-event-handler (game display event &rest args)
"This display module presents a standard chessboard.
See `chess-display-type' for the different kinds of displays."
- (unless (memq event chess-display-boring-events)
- (with-current-buffer display
- (cond
- ((eq event 'shutdown)
- (chess-display-destroy nil))
+ (with-current-buffer display
+ (cond
+ ((eq event 'shutdown)
+ (chess-display-destroy nil))
- ((eq event 'destroy)
- (chess-display-detach-game nil))
+ ((eq event 'destroy)
+ (chess-display-detach-game nil))
- ((eq event 'pass)
- (let ((my-color (chess-game-data game 'my-color)))
- (chess-game-set-data game 'my-color (not my-color))
- (chess-display-set-perspective* nil (not my-color))))
+ ((eq event 'pass)
+ (let ((my-color (chess-game-data game 'my-color)))
+ (chess-game-set-data game 'my-color (not my-color))
+ (chess-display-set-perspective* nil (not my-color))))
- ((eq event 'orient)
- ;; Set the display's perspective to whichever color I'm
- ;; playing; also set the index just to be sure
- (chess-display-set-index* nil (chess-game-index game))
- (chess-display-set-perspective*
- nil (chess-game-data game 'my-color))))
+ ((eq event 'orient)
+ ;; Set the display's perspective to whichever color I'm
+ ;; playing; also set the index just to be sure
+ (chess-display-set-index* nil (chess-game-index game))
+ (chess-display-set-perspective*
+ nil (chess-game-data game 'my-color))))
- (if (memq event '(orient setup-game move game-over resign))
- (chess-display-set-index* nil (chess-game-index game)))
+ (if (memq event '(orient setup-game move game-over resign))
+ (chess-display-set-index* nil (chess-game-index game)))
- (unless (eq event 'shutdown)
- (chess-display-update
- nil (memq event chess-display-momentous-events))))))
+ (let ((momentous (memq event chess-display-momentous-events)))
+ (if (or momentous (memq event chess-display-interesting-events))
+ (chess-display-update nil momentous)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; chess-display-mode
;;
-(defvar chess-display-mode-map
+(defvar chess-display-safe-map
(let ((map (make-keymap)))
(suppress-keymap map)
(set-keymap-parent map nil)
@@ -424,16 +424,38 @@ See `chess-display-type' for the different kinds of displays."
(define-key map [(control ?i)] 'chess-display-invert)
(define-key map [tab] 'chess-display-invert)
+ (define-key map [??] 'describe-mode)
+ (define-key map [?B] 'chess-display-list-buffers)
+ ;;(define-key map [?C] 'chess-display-duplicate)
+ (define-key map [?I] 'chess-display-invert)
+
+ (define-key map [?<] 'chess-display-move-first)
+ (define-key map [?,] 'chess-display-move-backward)
+ (define-key map [(meta ?<)] 'chess-display-move-first)
+ (define-key map [?>] 'chess-display-move-last)
+ (define-key map [?.] 'chess-display-move-forward)
+ (define-key map [(meta ?>)] 'chess-display-move-last)
+
+ (define-key map [(meta ?w)] 'chess-display-kill-board)
+
+ (define-key map [(control ?l)] 'chess-display-redraw)
+
+ map)
+ "The mode map used in read-only display buffers.")
+
+(defvar chess-display-mode-map
+ (let ((map (make-keymap)))
+ (suppress-keymap map)
+ (set-keymap-parent map chess-display-safe-map)
+
(define-key map [? ] 'chess-display-pass)
(define-key map [??] 'describe-mode)
(define-key map [?@] 'chess-display-remote)
(define-key map [?A] 'chess-display-abort)
- (define-key map [?B] 'chess-display-list-buffers)
(define-key map [?C] 'chess-display-duplicate)
(define-key map [?D] 'chess-display-draw)
(define-key map [?E] 'chess-display-edit-board)
(define-key map [?F] 'chess-display-set-from-fen)
- (define-key map [?I] 'chess-display-invert)
;;(define-key map [?M] 'chess-display-manual-move)
(define-key map [?M] 'chess-display-match)
(define-key map [?N] 'chess-display-abort)
@@ -442,18 +464,8 @@ See `chess-display-type' for the different kinds of displays."
(define-key map [?U] 'chess-display-undo)
(define-key map [?X] 'chess-display-quit)
- (define-key map [?<] 'chess-display-move-first)
- (define-key map [?,] 'chess-display-move-backward)
- (define-key map [(meta ?<)] 'chess-display-move-first)
- (define-key map [?>] 'chess-display-move-last)
- (define-key map [?.] 'chess-display-move-forward)
- (define-key map [(meta ?>)] 'chess-display-move-last)
-
- (define-key map [(meta ?w)] 'chess-display-kill-board)
(define-key map [(control ?y)] 'chess-display-yank-board)
- (define-key map [(control ?l)] 'chess-display-redraw)
-
(dolist (key '(?a ?b ?c ?d ?e ?f ?g ?h
?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8
?r ?n ?b ?q ?k ?o))
@@ -497,13 +509,16 @@ See `chess-display-type' for the different kinds of displays."
(erase-buffer)
(chess-display-update nil))
-(defun chess-display-mode ()
+(defun chess-display-mode (&optional read-only)
"A mode for displaying and interacting with a chessboard.
+If READ-ONLY is non-nil, then no modifications are allowed.
The key bindings available in this mode are:
\\{chess-display-mode-map}"
(interactive)
(setq major-mode 'chess-display-mode mode-name "Chessboard")
- (use-local-map chess-display-mode-map)
+ (if read-only
+ (use-local-map chess-display-safe-map)
+ (use-local-map chess-display-mode-map))
(buffer-disable-undo)
(setq buffer-auto-save-file-name nil
mode-line-format 'chess-display-mode-line))
diff --git a/chess-none.el b/chess-none.el
new file mode 100644
index 0000000..d916adf
--- /dev/null
+++ b/chess-none.el
@@ -0,0 +1,28 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; A null engine, used when two humans play each on the same display.
+;;
+;; $Revision$
+
+(require 'chess-engine)
+
+(defun chess-none-handler (event &rest args)
+ "Initialize the network chess engine."
+ (cond
+ ((eq event 'send))
+
+ ((eq event 'ready)
+ (and (chess-engine-game nil)
+ (chess-game-set-data (chess-engine-game nil) 'active t)))
+
+ ((memq event '(resign abort))
+ (and (chess-engine-game nil)
+ (chess-engine-set-start-position nil)))
+
+ ((eq event 'undo)
+ (if (chess-engine-game nil)
+ (chess-game-undo (chess-engine-game nil) (car args))))))
+
+(provide 'chess-none)
+
+;;; chess-none.el ends here
diff --git a/chess-sound.el b/chess-sound.el
new file mode 100644
index 0000000..c8f554c
--- /dev/null
+++ b/chess-sound.el
@@ -0,0 +1,101 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; This is very similar to chess-announce, except it uses specific
+;; .WAV files instead of text-to-speech.
+;;
+;; $Revision$
+
+(require 'chess-game)
+
+(defgroup chess-sound nil
+ "Code to play specific sounds when announcing chess moves."
+ :group 'chess)
+
+(defcustom chess-sound-directory
+ (expand-file-name "sounds"
+ (file-name-directory
+ (or load-file-name buffer-file-name)))
+ "The directory where chess sounds can be found."
+ :type 'directory
+ :group 'chess-sound)
+
+(defcustom chess-sound-play-function (if (fboundp 'play-sound-file)
+ 'play-sound-file
+ 'chess-sound-play)
+ "Non-nil if chess-sound should play sounds ."
+ :type 'file
+ :group 'chess-sound)
+
+(defcustom chess-sound-program (or (executable-find "esdplay")
+ (executable-find "play"))
+ "Program used to play sounds, if `play-sound-file' does not exist."
+ :type 'file
+ :group 'chess-sound)
+
+(defcustom chess-sound-args nil
+ "Additional args to pass to `chess-sound-program', before the .WAV file."
+ :type '(repeat string)
+ :group 'chess-sound)
+
+(defun chess-sound-for-game (game)
+ "Announce the opponent's moves in GAME."
+ (if (and (file-directory-p chess-sound-directory)
+ (file-exists-p (expand-file-name "tap.wav"
+ chess-sound-directory)))
+ (chess-game-add-hook game 'chess-sound-event-handler)))
+
+(defun chess-sound (ch)
+ (let ((file
+ (cond
+ ((stringp ch)
+ (format "%s.wav" ch))
+ ((memq ch '(?\# ?\+ ?k ?q ?b ?n ?r ?p ?x))
+ (format "%c_.wav" ch))
+ (t
+ (format "%s.wav" (chess-index-to-coord ch))))))
+ (funcall chess-sound-play-function
+ (expand-file-name file chess-sound-directory))))
+
+(defun chess-sound-play (file)
+ (apply 'call-process chess-sound-program
+ nil nil nil chess-sound-args))
+
+(defun chess-sound-event-handler (game ignore event &rest args)
+ "This display module presents a standard chessboard.
+See `chess-display-type' for the different kinds of displays."
+ (cond
+ ((memq event '(move game-over))
+ (let* ((ply (chess-game-ply game (1- (chess-game-index game))))
+ (pos (chess-ply-pos ply)))
+ (if (eq (chess-game-data game 'my-color)
+ (chess-pos-side-to-move pos))
+ (chess-sound "tap")
+ (let* ((source (chess-ply-source ply))
+ (target (chess-ply-target ply))
+ (s-piece (chess-pos-piece pos source))
+ (t-piece (chess-pos-piece pos target))
+ text)
+ (cond
+ ((memq :castle changes)
+ (chess-sound "O-O"))
+ ((memq :long-castle changes)
+ (chess-sound "O-O-O"))
+ ((= t-piece ? )
+ (chess-sound (downcase s-piece))
+ (chess-sound target))
+ (t
+ (chess-sound (downcase s-piece))
+ (chess-sound ?x)
+ (chess-sound (downcase t-piece))
+ (chess-sound target)))
+ (if (memq :check changes)
+ (chess-sound ?+))
+ (if (memq :checkmate changes)
+ (chess-sound ?#))
+ (if (memq :stalemate changes)
+ (chess-sound "smate")))))
+ nil)))
+
+(provide 'chess-sound)
+
+;;; chess-sound.el ends here
diff --git a/chess.el b/chess.el
index 9e4e17c..2fb35ad 100644
--- a/chess.el
+++ b/chess.el
@@ -98,7 +98,7 @@ a0 243
"Default engine to be used when starting a chess session."
:type 'sexp
:group 'chess)
-(defcustom chess-announce-moves t
+(defcustom chess-announce-module 'chess-announce
(defcustom chess-announce-moves t
This happens verbally if 'festival' is installed, otherwise it just
prints a message in your minibuffer, which works well for Emacspeak
@@ -123,6 +123,14 @@ minibuffer, which works well for Emacspeak users."
(display (chess-display-create chess-default-display my-color))
(game (chess-game-create)))
+ (when (and (eq chess-default-display 'chess-images)
+ (with-current-buffer display
+ (null chess-images-size)))
+ (message "Could not find suitable chess images; using ics1 display")
+ (chess-display-destroy display)
+ (require 'chess-ics1)
+ (setq display (chess-display-create 'chess-ics1 my-color)))
+
(chess-display-disable-popup display))
(chess-display-set-game display game)
@@ -143,9 +151,10 @@ minibuffer, which works well for Emacspeak users."
;; computerized engines fall into this category), we need to
;; let them know we're ready to begin
(chess-engine-command engine 'ready))
- (when chess-announce-moves
- (require 'chess-announce)
- (chess-announce-for-game game))))
+ (when chess-announce-module
+ (require chess-announce-module)
+ (funcall (intern (concat (symbol-name chess-announce-module)
+ "-for-game")) game))))
(chess-announce-for-game game)))))))
(chess-display-update display t)))
(cons display engine)))