diff options
| -rw-r--r-- | TODO | 23 | ||||
| -rw-r--r-- | chess-announce.el | 8 | ||||
| -rw-r--r-- | chess-display.el | 105 | ||||
| -rw-r--r-- | chess-none.el | 28 | ||||
| -rw-r--r-- | chess-sound.el | 101 | ||||
| -rw-r--r-- | chess.el | 17 |
6 files changed, 213 insertions, 69 deletions
@@ -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 @@ -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))) |
