diff options
| author | John Wiegley <johnw@newartisans.com> | 2002-04-07 08:44:00 +0000 |
|---|---|---|
| committer | John Wiegley <johnw@newartisans.com> | 2002-04-07 08:44:00 +0000 |
| commit | 2ef00c4beddb77e546cdc212cc502c5a9a4bba6c (patch) | |
| tree | f20a250ce3e162fd5580696528d7b8251cdcfcf4 | |
| parent | b148d84be1c3bd781c4fd11dd0a928fbeeda28fd (diff) | |
Simplified the event model, and removed session objects. Now
everything just revolves around a game. Gnuchess is now playable to
the end, except don't make moves that leave your king in check! The
engine still doesn't check for this yet.
| -rw-r--r-- | TODO | 12 | ||||
| -rw-r--r-- | chess-algebraic.el | 6 | ||||
| -rw-r--r-- | chess-crafty.el | 17 | ||||
| -rw-r--r-- | chess-display.el | 255 | ||||
| -rw-r--r-- | chess-engine.el | 77 | ||||
| -rw-r--r-- | chess-game.el | 77 | ||||
| -rw-r--r-- | chess-gnuchess.el | 25 | ||||
| -rw-r--r-- | chess-ics1.el | 2 | ||||
| -rw-r--r-- | chess-images.el | 3 | ||||
| -rw-r--r-- | chess-pgn.el | 9 | ||||
| -rw-r--r-- | chess-pos.el | 12 | ||||
| -rw-r--r-- | chess-process.el | 207 | ||||
| -rw-r--r-- | chess-session.el | 59 | ||||
| -rw-r--r-- | chess-standard.el | 2 | ||||
| -rw-r--r-- | chess.el | 55 |
15 files changed, 279 insertions, 539 deletions
@@ -1,15 +1,3 @@ -Don't ever use chess-pos-move directly, but instead create ply and -call chess-ply-next-pos. This will make sure that castling moves are -interpreted correctly. - -Pawn captures while playing gnuchess cause an error. - -Is the whole event model just a bit too fragile? - -Write a debug function for displaying positions. - -The mode-line display is wrong (it's one ply off). ----------------------------------------------------------------------- * 2.0 ** Breakdown diff --git a/chess-algebraic.el b/chess-algebraic.el index cd67c6a..3905659 100644 --- a/chess-algebraic.el +++ b/chess-algebraic.el @@ -62,6 +62,8 @@ This regexp handles both long and short form.") (if (eq piece ?O) (let ((rank (if color 7 0)) (long (= (length (match-string 1 move)) 5))) + ;; jww (2002-04-07): This should be moderated by + ;; chess-standard!! (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)) @@ -104,8 +106,8 @@ This regexp handles both long and short form.") (list (if (equal mate "#") ':checkmate ':check)))) - (assert changes) - (apply 'chess-ply-create position changes))) + (and changes + (apply 'chess-ply-create position changes)))) (defun chess-ply-to-algebraic (ply &optional long search-func) "Convert the given PLY to algebraic notation. diff --git a/chess-crafty.el b/chess-crafty.el index d9038db..a88a831 100644 --- a/chess-crafty.el +++ b/chess-crafty.el @@ -14,13 +14,16 @@ chess-algebraic-regexp "\\)\\s-*$") (function (lambda () - (let ((position (chess-engine-position nil))) - (if (string= (if (chess-pos-side-to-move position) - "White" "Black") - (match-string 1)) - (funcall chess-engine-response-handler 'move - (chess-algebraic-to-ply position - (match-string 2)))))))) + (let ((position (chess-engine-position nil)) + (move (match-string 2)) + ply) + (when (string= (if (chess-pos-side-to-move position) + "White" "Black") + (match-string 1)) + (setq ply (chess-algebraic-to-ply position move)) + (unless ply + (error "Could not convert engine move: %s" move)) + (funcall chess-engine-response-handler 'move ply)))))) (cons "Illegal move:\\s-*\\(.*\\)" (function (lambda () diff --git a/chess-display.el b/chess-display.el index 5f11e56..e8fc67d 100644 --- a/chess-display.el +++ b/chess-display.el @@ -6,7 +6,6 @@ ;;; Code: -(require 'chess-session) (require 'chess-game) (require 'chess-algebraic) (require 'chess-fen) @@ -47,7 +46,6 @@ ;; User interface ;; -(defvar chess-display-session) (defvar chess-display-game) (defvar chess-display-search-function) (defvar chess-display-variation) @@ -60,7 +58,6 @@ (defvar chess-display-edit-mode nil) (defvar chess-display-mode-line "") -(make-variable-buffer-local 'chess-display-session) (make-variable-buffer-local 'chess-display-game) (make-variable-buffer-local 'chess-display-search-function) (make-variable-buffer-local 'chess-display-variation) @@ -80,7 +77,7 @@ ,@body) ,@body))) -(defun chess-display-create (style perspective &optional session search-func) +(defun chess-display-create (style perspective &optional search-func) "Create a chess display, for displaying chess objects." (let* ((name (symbol-name style)) (draw (intern-soft (concat name "-draw"))) @@ -90,7 +87,6 @@ (error "There is no known chessboard display style '%s'" name)) (with-current-buffer (generate-new-buffer "*Chessboard*") (setq cursor-type nil - chess-display-session session chess-display-draw-function draw chess-display-highlight-function highlight chess-display-perspective perspective @@ -98,10 +94,6 @@ (chess-display-mode) (if initialize (funcall initialize)) - (if session - (let ((game (chess-session-data session 'current-game))) - (if game - (chess-display-set-game nil game)))) (current-buffer)))) (defsubst chess-display-destroy (display) @@ -110,10 +102,6 @@ (if (buffer-live-p buf) (kill-buffer buf)))) -(defsubst chess-display-session (display) - (chess-with-current-buffer display - chess-display-session)) - (defsubst chess-display-perspective (display) (chess-with-current-buffer display chess-display-perspective)) @@ -149,6 +137,8 @@ without a game object. If the position is merely edited, it will change the POSITION object that was passed in." (chess-with-current-buffer display + (if chess-display-game + (chess-display-detach-game nil)) (setq chess-display-game nil chess-display-search-function search-func chess-display-variation nil @@ -175,6 +165,8 @@ This differs from a position display, only in that the algebraic form of the move made to the reach the displayed position will be shown in the modeline." (chess-with-current-buffer display + (if chess-display-game + (chess-display-detach-game nil)) (setq chess-display-game nil chess-display-search-function search-func chess-display-variation nil @@ -198,6 +190,8 @@ the user able to scroll back and forth through the moves in the variation. Any moves made on the board will extend/change the variation that was passed in." (chess-with-current-buffer display + (if chess-display-game + (chess-display-detach-game nil)) (setq chess-display-game nil chess-display-search-function search-func chess-display-variation plies @@ -218,14 +212,27 @@ This will cause the first ply in the game's main variation to be displayed. Also, information about the game is shown in the modeline." (chess-with-current-buffer display + (if chess-display-game + (chess-display-detach-game nil)) (setq chess-display-game game chess-display-search-function nil chess-display-variation nil chess-display-index (or index 0) chess-display-ply nil chess-display-position nil) + (chess-game-add-hook game 'chess-display-event-handler display) (chess-display-update nil))) +(defun chess-display-detach-game (display) + "Set the display game. +This will cause the first ply in the game's main variation to be +displayed. Also, information about the game is shown in the +modeline." + (chess-with-current-buffer display + (if chess-display-game + (chess-game-remove-hook chess-display-game + 'chess-display-event-handler)))) + (defsubst chess-display-game (display) (chess-with-current-buffer display chess-display-game)) @@ -234,17 +241,13 @@ modeline." (chess-with-current-buffer display (unless chess-display-index (error "There is no game or variation currently being displayed.")) - (if chess-display-game - (unless (chess-game-pos chess-display-game index) - (error "There is no position in the displayed game at index %d." - index)) - (if (or (not (integerp index)) - (< index 0) - (>= index (length chess-display-variation))) - (error "There is no position in the display variation at index %d." - index))) - (setq chess-display-index index) - (chess-display-update nil))) + (unless (or (not (integerp index)) + (< index 0) + (> index (if chess-display-game + (chess-game-index chess-display-game) + (1+ (length chess-display-variation))))) + (setq chess-display-index index) + (chess-display-update nil)))) (defsubst chess-display-index (display) (chess-with-current-buffer display @@ -266,23 +269,20 @@ The position of PLY must match the currently displayed position. If only START is given, it must be in algebraic move notation." (chess-with-current-buffer display (cond - ((chess-display-active-p) - (chess-session-event chess-display-session 'move ply)) (chess-display-game ;; jww (2002-03-28): This should beget a variation within the ;; game, or alter the game, just as SCID allows (if (= (chess-display-index nil) (chess-game-index chess-display-game)) - (setq chess-display-index - (1+ (chess-game-index chess-display-game)))) - (chess-game-move chess-display-game ply)) + (chess-game-move chess-display-game ply) + (error "What to do here?? NYI"))) (chess-display-variation ;; jww (2002-04-02): what if we're in the middle? (nconc chess-display-variation (list ply)) (setq chess-display-index (1- (length chess-display-variation)))) (chess-display-ply (setq chess-display-ply ply)) - (t ; an ordinary position + (chess-display-position ; an ordinary position (setq chess-display-position (chess-ply-next-pos ply)))) (chess-display-update nil))) @@ -292,50 +292,32 @@ The given highlighting MODE is used, or the default if the style you are displaying with doesn't support that mode. `selected' is a mode that is supported by most displays, and is the default mode." (chess-with-current-buffer display - (if (chess-display-active-p) - (chess-session-event chess-display-session 'highlight - index (or mode 'selected)) - (if chess-display-highlight-function - (funcall chess-display-highlight-function index - (or mode 'selected)))))) + (if chess-display-highlight-function + (funcall chess-display-highlight-function index + (or mode 'selected))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Module method +;; Event handler ;; -;;;###autoload -(defun chess-display (session display event &rest args) +(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." - (if (eq event 'initialize) - (chess-display-create - (car args) (chess-session-data session 'my-color) session) - (ignore - (cond - ((eq event 'shutdown) - (chess-display-destroy display)) - - ((eq event 'setup) - (chess-display-set-game display (car args))) - - ((eq event 'highlight) - ;; calling `chess-display-highlight' here would be recursive - (if chess-display-highlight-function - (funcall chess-display-highlight-function - (car args) (cadr args)))) - - ((eq event 'pass) - (chess-display-set-perspective - display (not (chess-display-perspective display)))) - - ((eq event 'move) - (chess-display-set-index - display (chess-game-index (chess-display-game display))) - (chess-display-update display)) - - (t - (chess-display-update display)))))) + (cond + ((eq event 'shutdown) + (chess-display-destroy display)) + + ((eq event 'pass) + (chess-display-set-perspective + display (not (chess-display-perspective display)))) + + ((eq event 'move) + (chess-display-set-index + display (chess-game-index (chess-display-game display))))) + + (unless (eq event 'shutdown) + (chess-display-update display))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -358,10 +340,10 @@ See `chess-display-type' for the different kinds of displays." (define-key map [?X] 'chess-display-quit) (define-key map [?M] 'chess-display-manual-move) - (define-key map [?<] 'chess-display-move-backward) + (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-forward) + (define-key map [?>] 'chess-display-move-last) (define-key map [?.] 'chess-display-move-forward) (define-key map [(meta ?>)] 'chess-display-move-last) @@ -406,33 +388,38 @@ The key bindings available in this mode are: (defun chess-display-set-modeline () "Set the modeline to reflect the current game position." - (let ((ply (chess-display-ply nil)) - (color (chess-pos-side-to-move (chess-display-position nil))) - (index (chess-display-index nil))) + (let ((color (chess-pos-side-to-move (chess-display-position nil))) + (index (chess-display-index nil)) + ply) (if (and index (= index 0)) (setq chess-display-mode-line (format " %s START" (if color "White" "BLACK"))) - (setq chess-display-mode-line - (concat - " " (if color "White" "BLACK") - (if index - (concat " " (int-to-string - (if (> index 1) - (/ index 2) (1+ (/ index 2)))))) - (if ply - (concat ". " (if color "... ") - (chess-ply-to-algebraic - ply nil (chess-display-search-function nil))))))))) + (cond + (chess-display-ply + (setq ply chess-display-ply)) + (chess-display-game + (setq ply (chess-game-ply chess-display-game (1- index)))) + (chess-display-variation + (setq ply (nth chess-display-variation (1- index))))) + (if ply + (setq chess-display-mode-line + (concat + " " (if color "White" "BLACK") + (if index + (concat " " (int-to-string + (if (> index 1) + (/ index 2) (1+ (/ index 2)))))) + (if ply + (concat ". " (if color "... ") + (or (chess-ply-to-algebraic + ply nil + (chess-display-search-function nil)) + "???"))))))))) (defsubst chess-display-active-p () "Return non-nil if the displayed chessboard reflects an active game. -Basically, it means we are not editing or reviewing. -The game must be part of an active session (i.e., not just reviewing a -game object), and the board must represent the current position in -that game (i.e., not editing the position, or reviewing an earlier -position within the game)." - (and chess-display-session - chess-display-game +Basically, it means we are playing, not editing or reviewing." + (and chess-display-game (= (chess-display-index nil) (chess-game-index chess-display-game)) (not chess-display-edit-mode))) @@ -475,8 +462,8 @@ position within the game)." (defun chess-display-quit () "Quit the current game." (interactive) - (if (chess-display-active-p) - (chess-session-event chess-display-session 'shutdown) + (if chess-display-game + (chess-game-run-hooks chess-display-game 'shutdown) (chess-display-destroy nil))) (defun chess-display-manual-move (move) @@ -487,9 +474,12 @@ position within the game)." (if (chess-pos-side-to-move (chess-display-position nil)) "White" "Black") (1+ (/ (or (chess-display-index nil) 0) 2)))))) - (chess-display-move nil (chess-algebraic-to-ply - (chess-display-position nil) move - (chess-display-search-function nil)))) + (let ((ply (chess-algebraic-to-ply + (chess-display-position nil) move + (chess-display-search-function nil)))) + (unless ply + (error "Illegal move notation: %s" move)) + (chess-display-move nil ply))) (defun chess-display-set-current (dir) "Change the currently displayed board. @@ -504,7 +494,8 @@ to the end or beginning." (if chess-display-game (chess-game-index chess-display-game) (1- (length chess-display-variation))))) - (message "Use '>' to return to the current position"))) + (unless (chess-display-active-p) + (message "Use '>' to return to the current position")))) (defun chess-display-move-backward () (interactive) @@ -562,9 +553,10 @@ to the end or beginning." (defun chess-display-send-board () "Send the current board configuration to the user." (interactive) - (if chess-display-session - (chess-session-event chess-display-session 'setup - (chess-game-create (chess-display-position nil)))) + (if chess-display-game + (chess-game-set-plies + chess-display-game + (list (chess-ply-create (chess-display-position nil))))) (setq cursor-type nil chess-display-edit-mode nil)) @@ -599,11 +591,39 @@ to the end or beginning." (make-variable-buffer-local 'chess-legal-moves-pos) (make-variable-buffer-local 'chess-legal-moves) +(defun chess-keyboard-test-move (move) + "Return the given MOVE if it matching the user's current input." + (let ((i 0) (x 0) + (l (length move)) + (xl (length chess-move-string)) + (match t)) + (unless (or (and (equal chess-move-string "ok") + (equal move "O-O")) + (and (equal chess-move-string "oq") + (equal move "O-O-O"))) + (while (and (< i l) (< x xl)) + (if (= (aref move i) ?x) + (setq i (1+ i))) + (if (/= (downcase (aref move i)) + (aref chess-move-string x)) + (setq match nil i l) + (setq i (1+ i) x (1+ x))))) + (if match move))) + +(defsubst chess-keyboard-display-moves (&optional move-list) + (if (> (length chess-move-string) 0) + (message "[%s] %s" chess-move-string + (mapconcat 'identity + (or move-list + (delq nil (mapcar 'chess-keyboard-test-move + chess-legal-moves))) " ")))) + (defun chess-keyboard-shortcut-delete () (interactive) (setq chess-move-string (substring chess-move-string 0 - (1- (length chess-move-string))))) + (1- (length chess-move-string)))) + (chess-keyboard-display-moves)) (defun chess-keyboard-shortcut (&optional display-only) (interactive) @@ -625,40 +645,23 @@ to the end or beginning." (chess-ply-to-algebraic ply nil search-func))) (chess-legal-plies position search-func)) 'string-lessp))) - (let ((moves (mapcar - (function - (lambda (move) - (let ((i 0) (x 0) - (l (length move)) - (xl (length chess-move-string)) - (match t)) - (unless (or (and (equal chess-move-string "ok") - (equal move "O-O")) - (and (equal chess-move-string "oq") - (equal move "O-O-O"))) - (while (and (< i l) (< x xl)) - (if (= (aref move i) ?x) - (setq i (1+ i))) - (if (/= (downcase (aref move i)) - (aref chess-move-string x)) - (setq match nil i l) - (setq i (1+ i) x (1+ x))))) - (if match move)))) - chess-legal-moves))) - (setq moves (delq nil moves)) + (let ((moves (delq nil (mapcar 'chess-keyboard-test-move + chess-legal-moves)))) (cond ((= (length moves) 1) - (chess-display-move nil (chess-algebraic-to-ply - (chess-display-position nil) (car moves) - (chess-display-search-function nil))) + (let ((ply (chess-algebraic-to-ply + (chess-display-position nil) (car moves) + (chess-display-search-function nil)))) + (unless ply + (error "Illegal move notation: %s" (car moves))) + (chess-display-move nil ply)) (setq chess-move-string nil chess-legal-moves nil chess-legal-moves-pos nil)) ((null moves) (chess-keyboard-shortcut-delete)) (t - (message "[%s] %s" chess-move-string - (mapconcat 'identity moves " "))))))) + (chess-keyboard-display-moves moves)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; diff --git a/chess-engine.el b/chess-engine.el index 40090ab..19bd4cc 100644 --- a/chess-engine.el +++ b/chess-engine.el @@ -6,7 +6,6 @@ ;;; Commentary: -(require 'chess-session) (require 'chess-game) (defgroup chess-engine nil @@ -16,7 +15,6 @@ (defvar chess-engine-regexp-alist nil) (defvar chess-engine-event-handler nil) (defvar chess-engine-response-handler nil) -(defvar chess-engine-session nil) (defvar chess-engine-position nil) (defvar chess-engine-game nil) (defvar chess-engine-search-function nil) @@ -24,7 +22,6 @@ (make-variable-buffer-local 'chess-engine-regexp-alist) (make-variable-buffer-local 'chess-engine-event-handler) (make-variable-buffer-local 'chess-engine-response-handler) -(make-variable-buffer-local 'chess-engine-session) (make-variable-buffer-local 'chess-engine-position) (make-variable-buffer-local 'chess-engine-game) (make-variable-buffer-local 'chess-engine-search-function) @@ -51,26 +48,22 @@ (defun chess-engine-do-move (ply) (cond - ((and chess-engine-session - chess-engine-game) - (chess-session-event chess-engine-session event ply)) (chess-engine-game (chess-game-move chess-engine-game ply)) - (t - (apply 'chess-pos-move ply)))) + (chess-engine-position + (setq chess-engine-position (chess-ply-next-pos ply))))) (defun chess-engine-default-handler (event &rest args) (cond ((eq event 'move) (chess-engine-do-move (car args))))) -(defun chess-engine-create (module &optional user-handler session search-func) +(defun chess-engine-create (module &optional user-handler search-func) (let ((regexp-alist (intern-soft (concat (symbol-name module) "-regexp-alist"))) (handler (intern-soft (concat (symbol-name module) "-handler")))) (with-current-buffer (generate-new-buffer " *chess-engine*") - (setq chess-engine-session session - chess-engine-regexp-alist (symbol-value regexp-alist) + (setq chess-engine-regexp-alist (symbol-value regexp-alist) chess-engine-event-handler handler chess-engine-response-handler (or 'chess-engine-default-handler user-handler)) @@ -79,11 +72,6 @@ (error "Failed to start chess engine process")) (set-process-buffer proc (current-buffer)) (set-process-filter proc 'chess-engine-filter)) - (if session - (let ((game (chess-session-data session 'current-game))) - (if game - (chess-engine-set-game nil game))) - (chess-engine-set-game nil (chess-game-create nil search-func))) (current-buffer)))) (defun chess-engine-destroy (engine) @@ -97,18 +85,21 @@ (defun chess-engine-search-function (engine) (chess-with-current-buffer engine - chess-engine-search-function)) - -(defun chess-engine-set-search-function (engine search-func) - (chess-with-current-buffer engine (if chess-engine-game (chess-game-search-function chess-engine-game) (or chess-engine-search-function 'chess-standard-search-position)))) -(defun chess-engine-session (engine) +(defun chess-engine-set-search-function (engine search-func) + (chess-with-current-buffer engine + (if chess-engine-game + (error "Engine is currently linked to a game") + (setq chess-engine-search-function search-func)))) + +(defsubst chess-engine-search-position (engine position target piece) (chess-with-current-buffer engine - chess-engine-session)) + (funcall (chess-engine-search-function nil) + position target piece))) (defun chess-engine-set-option (engine option value) (chess-with-current-buffer engine @@ -120,6 +111,8 @@ (defun chess-engine-set-position (engine position) (chess-with-current-buffer engine + (if chess-engine-game + (chess-engine-detach-game nil)) (setq chess-engine-game nil chess-engine-position position) (chess-engine-command nil 'setup position))) @@ -132,10 +125,19 @@ (defun chess-engine-set-game (engine game) (chess-with-current-buffer engine + (if chess-engine-game + (chess-engine-detach-game nil)) (setq chess-engine-game game chess-engine-position nil) + (chess-game-add-hook game 'chess-engine-event-handler engine) (chess-engine-command nil 'setup (chess-game-pos game)))) +(defun chess-engine-detach-game (engine) + (chess-with-current-buffer engine + (if chess-engine-game + (chess-game-remove-hook chess-engine-game + 'chess-engine-event-handler)))) + (defun chess-engine-game (engine) (chess-with-current-buffer engine chess-engine-game)) @@ -160,26 +162,23 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Module method +;; Primary event handler ;; -;;;###autoload -(defun chess-engine (session engine event &rest args) +(defun chess-engine-event-handler (game engine event &rest args) "Handle any commands being sent to this instance of this module." - (if (eq event 'initialize) - (chess-engine-create (car args) - 'chess-engine-session-callback session) - (with-current-buffer engine - (unless (apply chess-engine-event-handler event args) - (cond - ((eq event 'shutdown) - (chess-engine-destroy engine)) - - ((eq event 'setup) - (chess-engine-set-game engine (car args))) - - ((eq event 'pass) - (chess-engine-pass engine))))))) + (with-current-buffer engine + (assert (eq game (chess-engine-game nil))) + (apply chess-engine-event-handler event args) + (cond + ((eq event 'shutdown) + (chess-engine-destroy engine)) + + ((eq event 'setup) + (chess-engine-set-game engine (car args))) + + ((eq event 'pass) + (chess-engine-pass engine))))) (defun chess-engine-filter (proc string) "Process filter for receiving text from a chess process." diff --git a/chess-game.el b/chess-game.el index 7e28405..fd3c5fa 100644 --- a/chess-game.el +++ b/chess-game.el @@ -25,21 +25,33 @@ ("Result" . "*") ("TimeControl" . "-"))) -(defsubst chess-game-tags (game) +(defsubst chess-game-hooks (game) "Return the tags alist associated with GAME." (car game)) -(defsubst chess-game-set-tags (game tags) +(defsubst chess-game-set-hooks (game hooks) "Return the tags alist associated with GAME." - (setcar game tags)) + (setcar game hooks)) -(defsubst chess-game-plies (game) +(defun chess-game-add-hook (game function &optional data prepend) "Return the tags alist associated with GAME." - (cddr game)) + (let ((hooks (chess-game-hooks game))) + (if (null hooks) + (chess-game-set-hooks game (list (cons function data))) + (if prepend + (chess-game-set-hooks game (cons (cons function data) hooks)) + (nconc hooks (list (cons function data))))))) + +(defsubst chess-game-remove-hook (game function) + "Return the tags alist associated with GAME." + (chess-game-set-hooks game (assq-delete-all function + (chess-game-hooks game)))) -(defsubst chess-game-set-plies (game plies) +(defsubst chess-game-run-hooks (game &rest args) "Return the tags alist associated with GAME." - (setcdr (cdr game) plies)) + (dolist (hook (chess-game-hooks game)) + (apply (car hook) game (cdr hook) args))) + (defsubst chess-game-search-function (game) "Return the tags alist associated with GAME." @@ -47,7 +59,18 @@ (defsubst chess-game-set-search-function (game func) "Return the tags alist associated with GAME." - (setcar (cdr game) func)) + (setcar (cdr game) func) + (chess-game-run-hooks game 'rule-change)) + + +(defsubst chess-game-tags (game) + "Return the tags alist associated with GAME." + (nth 2 game)) + +(defsubst chess-game-set-tags (game tags) + "Return the tags alist associated with GAME." + (setcar (nthcdr 2 game) tags) + (chess-game-run-hooks game 'set-tags)) (defsubst chess-game-tag (game tag) "Return the value for TAG in GAME." @@ -58,16 +81,27 @@ "Set a TAG for GAME to VALUE." (let ((tags (chess-game-tags game))) (if (null tags) - (setcar game (list (cons tag value))) + (chess-game-set-tags game (list (cons tag value))) (let ((entry (assoc tag tags))) (if entry (setcdr entry value) - (nconc (car game) - (list (cons tag value)))))))) + (nconc tags (list (cons tag value))))))) + (chess-game-run-hooks game 'set-tag tag)) -(defun chess-game-del-tag (game tag) +(defsubst chess-game-del-tag (game tag) "Set a TAG for GAME to VALUE." - (setcar game (assq-delete-all tag (chess-game-tags game)))) + (chess-game-set-tags game (assq-delete-all tag (chess-game-tags game))) + (chess-game-run-hooks game 'delete-tag tag)) + + +(defsubst chess-game-plies (game) + "Return the tags alist associated with GAME." + (nth 3 game)) + +(defsubst chess-game-set-plies (game plies) + "Return the tags alist associated with GAME." + (setcdr (nthcdr 2 game) plies) + (chess-game-run-hooks game 'set-plies)) (defsubst chess-game-index (game) "Return the GAME's current position index." @@ -97,6 +131,7 @@ "Return the position related to GAME's INDEX position." (car (chess-game-ply game index))) + (defun chess-game-create (&optional position search-func tags) "Create a new chess game object. Optionally use the given starting POSITION (which is recorded using @@ -104,17 +139,16 @@ the game's FEN tag). SEARCH-FUNC specifies the function used to test the legality of moves. TAGS is the starting set of game tags (which can always be changed later using the various tag-related methods)." - (let ((game (list tags - (or search-func 'chess-standard-search-position)))) + (let ((game + (list nil + (or search-func 'chess-standard-search-position) + tags + (list (chess-ply-create (or position + (chess-pos-create))))))) (dolist (tag (cons (cons "Date" (format-time-string "%Y.%m.%d")) chess-game-default-tags)) (unless (chess-game-tag game (car tag)) (chess-game-set-tag game (car tag) (cdr tag)))) - (chess-game-add-ply game (chess-ply-create - (or position - (chess-pos-create)))) - (if position - (chess-game-set-tag game "FEN" (chess-pos-to-fen position))) game)) (defun chess-game-move (game ply) @@ -144,7 +178,8 @@ progress (nil), if it is drawn, resigned, mate, etc." "0-1" "1-0"))) (t (chess-game-add-ply game (chess-ply-create - (chess-ply-next-pos current-ply))))))) + (chess-ply-next-pos current-ply))) + (chess-game-run-hooks game 'move current-ply))))) ;; A few convenience functions diff --git a/chess-gnuchess.el b/chess-gnuchess.el index 09fe89e..1f0e68b 100644 --- a/chess-gnuchess.el +++ b/chess-gnuchess.el @@ -8,13 +8,19 @@ (require 'chess-fen) (require 'chess-algebraic) +(defvar chess-gnuchess-now-moving nil) + (defvar chess-gnuchess-regexp-alist (list (cons (concat "My move is : \\(" chess-algebraic-regexp "\\)") (function (lambda () - (funcall chess-engine-response-handler 'move - (chess-algebraic-to-ply (chess-engine-position nil) - (match-string 1)))))) + (let* ((move (match-string 1)) + (ply (chess-algebraic-to-ply + (chess-engine-position nil) move))) + (unless ply + (error "Could not convert engine move: %s" move)) + (let ((chess-gnuchess-now-moving t)) + (funcall chess-engine-response-handler 'move ply)))))) (cons "Illegal move:" (function (lambda () @@ -29,18 +35,23 @@ (executable-find "gnuchess"))) (message "Starting chess program 'gnuchess'...done") proc)) + ((eq event 'shutdown) (chess-engine-send nil "quit\n")) + ((eq event 'setup) (chess-engine-send nil (format "setboard %s\n" (chess-pos-to-fen (car args))))) + ((eq event 'pass) (chess-engine-send nil "go\n")) + ((eq event 'move) - (chess-engine-send - nil (concat (chess-ply-to-algebraic - (car args) nil - (chess-engine-search-function nil)) "\n"))))) + (unless chess-gnuchess-now-moving + (chess-engine-send nil (concat (chess-ply-to-algebraic + (car args) nil + (chess-engine-search-function nil)) + "\n")))))) (provide 'chess-gnuchess) diff --git a/chess-ics1.el b/chess-ics1.el index e086772..1491a63 100644 --- a/chess-ics1.el +++ b/chess-ics1.el @@ -8,7 +8,7 @@ ;;; Code: -(defun chess-display-position (&optional position) +(defun chess-debug-position (&optional position) "This is a debugging function, and not meant from general use." (interactive) (let ((pos (or position (chess-engine-position nil)))) diff --git a/chess-images.el b/chess-images.el index eb47d02..b4b9488 100644 --- a/chess-images.el +++ b/chess-images.el @@ -67,8 +67,7 @@ light_piece." "The name of the file used for background squares. This file is optional. If there is no file available by this name, a solid color square will be created and used. This option exists so -that specialized squares may be used such as marble tiles, etc. -jww (2001-06-23): This is still not fully implemented." +that specialized squares may be used such as marble tiles, etc." :type 'file :set 'chess-images-clear-image-cache :group 'chess-images) diff --git a/chess-pgn.el b/chess-pgn.el index 36ce3f0..04a8575 100644 --- a/chess-pgn.el +++ b/chess-pgn.el @@ -18,7 +18,10 @@ ((looking-at chess-algebraic-regexp) (goto-char (match-end 0)) (setq prevpos position) - (let ((ply (chess-game-algebraic-to-ply game (match-string 0)))) + (let* ((move (match-string 0)) + (ply (chess-game-algebraic-to-ply game (match-string 0)))) + (unless ply + (error "Error reading move: %s" move)) (setq position (chess-ply-next-pos ply)) (nconc plies (list ply)))) ((and top-level @@ -138,9 +141,7 @@ If INDENTED is non-nil, indent the move texts." ;; (setq move (buffer-substring-no-properties (point) end) ;; coords (chess-algebraic-to-ply chess-display-position move)) ;; ;; it will just get reinserted again -;; (delete-region (point) end)) -;; (chess-session-event chess-current-session 'move -;; (chess-algebraic-to-ply chess-display-position)))) +;; (delete-region (point) end))) ;; ;; (defun chess-pgn-insert-move (move &optional color sequence) ;; "Insert an algebraic move description into a PGN buffer. diff --git a/chess-pos.el b/chess-pos.el index 5eef9c1..b67070a 100644 --- a/chess-pos.el +++ b/chess-pos.el @@ -239,13 +239,13 @@ trying to move a blank square." (if (= piece ? ) (error "Attempted piece move from blank square %s" from)) (chess-pos-set-piece position from ? ) - (chess-pos-set-piece position to piece) - ;; once a piece is moved, en passant is no longer available - (chess-pos-set-en-passant position nil) - ;; toggle the side whose move it is - (chess-pos-set-side-to-move - position (not (chess-pos-side-to-move position))))) + (chess-pos-set-piece position to piece))) (setq changes (cddr changes))) + ;; once a piece is moved, en passant is no longer available + (chess-pos-set-en-passant position nil) + ;; toggle the side whose move it is + (chess-pos-set-side-to-move position + (not (chess-pos-side-to-move position))) position) (provide 'chess-pos) diff --git a/chess-process.el b/chess-process.el deleted file mode 100644 index 21d4cdb..0000000 --- a/chess-process.el +++ /dev/null @@ -1,207 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Obtain movements and other information from a subprocess -;; -;; $Revision$ - -;;; Commentary: - -(require 'chess-session) -(require 'chess-game) -(require 'chess-algebraic) - -(defgroup chess-process nil - "Code for reading movements and other commands from a subprocess." - :group 'chess) - -(defvar chess-process-command-or-host nil - "What type of process is it? -This could be a string, naming a command to run, in which case it is a -local connection. -It could be a cons cell, giving the (HOST . PORT) of a network connection. -It could be a Lisp function, which will be called to establish -whatever type of connection it wishes, so long as it returns a buffer -related to the resulting process.") - -(make-variable-buffer-local 'chess-process-command-or-host) - -(defvar chess-process-arguments nil - "If `chess-process-where' is a string or Lisp function, pass these args.") -(defvar chess-process-game) -(defvar chess-process-last-pos) -(defvar chess-process-working nil) - -(make-variable-buffer-local 'chess-process-arguments) -(make-variable-buffer-local 'chess-process-game) -(make-variable-buffer-local 'chess-process-last-pos) -(make-variable-buffer-local 'chess-process-working) - -(defvar chess-process-triggers nil - "A list of regexps and the commands that they trigger. -The alist should be of the form: - - ((REGEXP COMMAND ARG1-GROUP ARG2-GROUP ...) ...) - -Where the ARG*-GROUP entries specify which parenthesis groups in the -regexp demarcate those arguments. Anything more complicated than this -must be handled by modules that derive from this module.") - -(make-variable-buffer-local 'chess-process-triggers) - -;;; Code: - -(defun chess-process (session buffer event &rest args) - "Handle any commands being sent to this instance of this module." - (cond - ((eq event 'initialize) - (let ((buf (generate-new-buffer " *chess-process*"))) - (with-current-buffer buf - (setq chess-process-triggers (nth 0 args) - chess-process-command-or-host (nth 1 args) - chess-process-arguments (nthcdr 2 args)) - (let ((proc - (if (stringp chess-process-command-or-host) - (prog2 - (message "Starting chess program '%s'..." - chess-process-command-or-host) - (apply 'start-process "chess-process" - (current-buffer) - chess-process-command-or-host - chess-process-arguments) - (message "Starting chess program '%s'...done" - chess-process-command-or-host)) - (prog2 - (message "Connecting to host %s:%d..." - (car chess-process-command-or-host) - (cdr chess-process-command-or-host)) - (open-network-stream - "chess-process" (current-buffer) - (car chess-process-command-or-host) - (cdr chess-process-command-or-host)) - (message "Connecting to host %s:%d...done" - (car chess-process-command-or-host) - (cdr chess-process-command-or-host)))))) - (unless (and proc (memq (process-status proc) '(run open))) - (error "Failed to start chess process")) - (set-process-filter proc 'chess-process-filter)) - buf))) - ((eq event 'shutdown) - (if (buffer-live-p buffer) - (kill-buffer buffer))) - (t - (ignore - (with-current-buffer buffer - (cond - ((eq event 'setup) - (setq chess-process-game (car args) - chess-process-last-pos (point-min))))))))) - -(defun chess-process-filter (proc string) - "Process filter for receiving text from a chess process." - (let ((buf (process-buffer proc))) - (when (buffer-live-p buf) - (with-current-buffer buf - (let ((moving (= (point) (process-mark proc)))) - (save-excursion - ;; Insert the text, advancing the process marker. - (goto-char (process-mark proc)) - (insert string) - (set-marker (process-mark proc) (point))) - (if moving (goto-char (process-mark proc)))) - (unless chess-process-working - (setq chess-process-working t) - (unwind-protect - (progn - (goto-char chess-process-last-pos) - (beginning-of-line) - (while (not (eobp)) - (condition-case err - (let ((triggers chess-process-triggers)) - (while triggers - ;; this could be accelerated by joining - ;; together the regexps - (when (looking-at (caar triggers)) - (let ((command (nth 1 (car triggers))) - (args (mapcar 'match-string - (nthcdr 2 (car triggers))))) - (cond - ((functionp command) - (apply command args)) - ((symbolp command) - (chess-session-event chess-current-session - command args)) - (t (eval command))))) - (setq triggers (cdr triggers)))) - (chess-illegal (error-message-string err))) - (forward-line))) - (setq chess-process-last-pos (point) - chess-process-working nil))))))) - -(defun chess-process-let (forms) - `(let ((str (progn ,@forms))) - (if (stringp str) - (ignore - (process-send-string (get-buffer-process (current-buffer)) - (concat str "\n"))) - str))) - -(defun chess-process-insert-forms (event) - (if (assq event forms) - (chess-process-let - (prog1 - (cdr (assq event forms)) - (setq forms (assq-delete-all event forms)))))) - -(defmacro define-chess-engine (name ignored triggers &rest forms) - "Define a chess engine. -NAME is an unquoted symbol name that denotes the engine. This name is -used as the default string for the chess engine's external command -name. -TRIGGERS is a list of process triggers, which fire when the output -from the process matches certain regexps. See -`chess-process-triggers' for more information. -FORMS is an alist of event symbols, and forms to evaluate when such an -event is received by the module. If these forms return a string, this -string will be sent to the engine process. -See the file chess-engines.el for code examples." - (let ((namestr (symbol-name name))) - `(progn - (defcustom ,(intern (concat "chess-" namestr "-command")) - (and (require 'executable) - (executable-find ,namestr)) - ,(concat "The name of the " namestr " program.") - :type 'file - :group 'chess-process) - - (defun ,(intern (concat "chess-" namestr)) - (session buffer event &rest args) - (cond - ((eq event 'initialize) - (with-current-buffer - (chess-process session buffer event ,triggers - ,(intern (concat "chess-" namestr "-command"))) - ,(chess-process-insert-forms 'init) - (current-buffer))) - ((eq event 'shutdown) - (when (buffer-live-p buffer) - (ignore-errors - ,(chess-process-insert-forms 'shutdown)) - (kill-buffer buffer))) - (t - (ignore - (with-current-buffer buffer - (cond - ((eq event 'setup) - (apply 'chess-process session buffer event args) - ,(chess-process-insert-forms 'setup)) - ,@(mapcar - (function - (lambda (entry) - `((eq event (quote ,(car entry))) - ,(chess-process-let (cdr entry))))) forms) - (t - (apply 'chess-process session buffer event args))))))))))) - -(provide 'chess-process) - -;;; chess-process.el ends here diff --git a/chess-session.el b/chess-session.el deleted file mode 100644 index 8ddf3c6..0000000 --- a/chess-session.el +++ /dev/null @@ -1,59 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Manage a chess playing/viewing session -;; -;; $Revision$ - -;;; Commentary: - -(defun chess-session-create () - (cons nil nil)) - -(defun chess-session-add-listener (session listener &optional front object) - (if (or front (not (cdr session))) - (setcdr session (cons (cons listener object) - (cdr session))) - (nconc session (list (cons listener object))))) - -(defun chess-session-remove-listener (session listener) - (setcdr session (delq (assq listener (cdr session)) - (cdr session)))) - -(defun chess-session-data (session sym) - (cdr (assq sym (car session)))) - -(defun chess-session-set-data (session sym value) - (let ((entry (assq sym (car session)))) - (if entry - (setcdr entry value) - (setcar session (cons (cons sym value) - (car session)))))) - -(defun chess-session-del-data (session sym) - (setcar session (delq (assq sym (car session)) - (car session)))) - -(defun chess-session-event (session event &rest args) - (let ((listeners (cdr session)) result) - (while (and (or (eq event 'initialize) - (null result)) listeners) - (setq result (apply (caar listeners) session (cdar listeners) - event args)) - (if (eq event 'initialize) - (setcdr (car listeners) result)) - (setq listeners (cdr listeners))) - result)) - -;; (define-chess-module MODULE (&rest args)) -;; (define-chess-module (MODULE BASES...) (&rest args) -;; :ctor-args (ARGS...) -;; :create-buffer -;; (EVENT -;; FORMS...) -;; ...) -;; -;; (chess-call-derived args &optional base-module) - -(provide 'chess-session) - -;;; chess-session.el ends here diff --git a/chess-standard.el b/chess-standard.el index 68c207c..165cdce 100644 --- a/chess-standard.el +++ b/chess-standard.el @@ -73,6 +73,8 @@ indices which indicate where a piece may have moved from." (< piece ?a)) -1 1)) (c (= bias -1)) p pos candidates) + ;; jww (2002-04-07): Don't return candidates that leave the king + ;; in check. (cond ;; if the piece is `t', it means to find the candidates resulting ;; from any piece movement. This is useful for testing whether a @@ -73,7 +73,6 @@ a0 243 ;; interface commands available in each of those buffer types. ;;; Code: -(require 'chess-session) (require 'chess-game) (require 'chess-display) @@ -82,7 +81,7 @@ a0 243 (defgroup chess nil "An Emacs chess playing program." :group 'games) -(defconst chess-version "2.0a1" +(defconst chess-version "2.0a2" (defconst chess-version "2.0a7" "The version of the Emacs chess program.") @@ -100,50 +99,14 @@ a0 243 (defun chess (&optional arg) "Start a game of chess." (interactive "P") - (let ((session (chess-session-create)) - (perspective t)) ; start out as white always - ;; setup `chess-handler' to receive all events first - (chess-session-add-listener session 'chess-handler) - (chess-session-set-data session 'my-color perspective) - ;; initialize all of the modules, and setup a new game - (chess-session-event session 'initialize) - (chess-session-event session 'setup (chess-game-create)) - ;; create a display object linked to the session, and add it to - ;; the event chain; it is via this object that session events will - ;; for the most part be generated - (require chess-default-display) - (chess-session-add-listener session 'chess-display nil - (chess-display-create chess-default-display - perspective session)) - ;; unless prefix arg is given, use `chess-default-engine' to play - ;; against; otherwise, just create a board for play between two - ;; people - (unless arg - (require chess-default-engine) - (chess-session-add-listener session 'chess-engine nil - (chess-engine-create chess-default-engine - nil session))))) - -(defun chess-handler (session window-config event &rest args) - "React to changes on the chess board in a global Emacs way." - (if (eq event 'initialize) - (current-window-configuration) - (ignore - (cond - ((eq event 'shutdown) - (set-window-configuration window-config)) - - ((eq event 'setup) - (chess-session-set-data session 'current-game (car args))) - - ((eq event 'pass) - (let ((color (not (chess-session-data session 'my-color)))) - (message "You are now playing %s" (if color "White" "Black")) - (chess-session-set-data session 'my-color (not color)))) - - ((eq event 'move) - (chess-game-move (chess-session-data session 'current-game) - (car args))))))) + (require chess-default-display) + (require chess-default-engine) + (let ((game (chess-game-create)) ; start out as white always + display engine) + (chess-display-set-game + (chess-display-create chess-default-display t) game) + (chess-engine-set-game + (chess-engine-create chess-default-engine) game))) (aset chess-puzzle-locations 3 puzzle-engine))))))) (provide 'chess) |
