summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO88
-rw-r--r--chess-algebraic.el3
-rw-r--r--chess-display.el242
-rw-r--r--chess-game.el30
-rw-r--r--chess-german.el98
-rw-r--r--chess-ics1.el11
-rw-r--r--chess-images.el9
-rw-r--r--chess-input.el132
-rw-r--r--chess-pgn.el2
-rw-r--r--chess-plain.el46
-rw-r--r--chess-ply.el28
-rw-r--r--chess-pos.el78
12 files changed, 431 insertions, 336 deletions
diff --git a/TODO b/TODO
index 095fd18..f57adf2 100644
--- a/TODO
+++ b/TODO
@@ -1,36 +1,52 @@
-- Feature work remaining:
- 2.0
- annotations
- chatting
- 2.x
- display/database tie-in
- analysis/highlight tools
- bughouse/crazyhouse
+ Feature work remaining
+
+ 2.0 annotations
+ chatting
+
+ 2.x display/database tie-in
+ analysis/highlight tools
+ bughouse/crazyhouse
----------------------------------------------------------------------
-- Make use of the new chess-incr-index*, which does no checks
+ Hotlist
-- Fix highlighting in chess-plain when chess-plain-spacing is set.
- chess-coord needs to be applied to a larger area, and also the way
- highlight locations are found needs to be recalculated.
+- Make the mode-line much more informative, with information on who is
+ playing, etc.
-- Move keyboard shortcutting and mouse selection into their own
- modules
+- Add an "mlang" user, and "chess" group to my laptop.
-- Use chess-ply-set-keyword wherever keywords are being set now.
+- Polish chess-input.el
- Find a way that regexp-alist entries that only need to fire once are
only scanned once.
-- Extend elp.el to do any kind of memory profiling
+- Make any game-modifying commands in a display use C-c C-?
+
+- Copy some of ics.el's nicer functionality into chess-ics.el
+
+- Complete the ICS12 parser, based on Mario's comments
+
+- Add support for ICS observing
-- Queue up input that comes in from an engine, then process the queue
- -- instead of using chess-engine-last-pos
+- Use server-side sockets in chess-network, if Emacs supports it
+
+- Allow ASCII displays to use a separate frame
+
+- Still need to test many areas: position editing
+
+- Add chess-game-strip-annotations, for removing all annotations from
+ a game object
+
+- Let the user specify a default size for the chess-images display
+
+- Resize the chess board on a window resize event, if possible.
----------------------------------------------------------------------
+ To-do List
+
- Need to implement the "50 moves after irreversible" draw rule
- The game should go inactive once I lose by stalemate/checkmate
@@ -43,7 +59,7 @@
- in chess-engine-filter and chess-ics-filter, if an error is
encountered, skip that line so the same error isn't triggered again
-- SPACE to pass, then try to move (against an engine) fails saying
+- SPACE to pass, then try to move (against an engine) fails saying:
it's not our turn to move
- Add support for adjournments; also, implement this is such a way
@@ -63,46 +79,23 @@
- Read-only mode needs to be more vigorous. There's nothing
preventing the user from using M-x commands.
-- Complete chess-pgn-mode's automatic entry
-
-- Make any game-modifying commands in a display use C-c C-?
+- Complete chess-pgn-mode's automatic entry, using chess-input, and
+ making space/return perform the move
- Use MIME attachments (application/x-chess-pgn) for sending e-mail
chess games back and forth.
-- Copy some of ics.el's nicer functionality into chess-ics.el
-
-- Complete the ICS12 parser, based on Mario's comments
-
-- Add support for ICS observing
-
-- Make the mode-line much more informative, with information on who is
- playing, etc.
-
-- Use server-side sockets in chess-network, if Emacs supports it
-
-- Allow ASCII displays to use a separate frame
-
- In edit mode, mouse-2 and mouse-3 should provide a drop-down list of
pieces the square can be set to. Cursor movement is really not the
best for chess-images. I still need to figure out how best to
handle cursor-type with that display.
-- Still need to test many areas: position editing
-
- Implement engine options; then, in chess-puzzle set the option that
tells the engine not to resign.
- Remote displays are horribly insecure.
-- Resize the chess board on a window resize event, if possible.
-
-- Add chess-game-strip-annotations, for removing all annotations from
- a game object
-
-- Let the user specify a default size for the chess-images display
-
-- Port image display code to XEmacs
+----------------------------------------------------------------------
Training features
@@ -114,6 +107,8 @@
- Add a chess tutor program, to teach people how to play chess
+----------------------------------------------------------------------
+
Analysis features
- Add an engine function for obtaining an evaluation of the current
@@ -137,8 +132,11 @@
defense/attack/both, etc. Basically, everything that can be known
about the current board, and one move ahead (on both sides).
+----------------------------------------------------------------------
+
BEFORE FINAL RELEASE
+ port the code to XEmacs
profile
mem profile
docstring
diff --git a/chess-algebraic.el b/chess-algebraic.el
index b84bf7d..0bdcac8 100644
--- a/chess-algebraic.el
+++ b/chess-algebraic.el
@@ -148,8 +148,7 @@ If LONG is non-nil, render the move into long notation."
((= rank 1)
(setq differentiator (+ (- 7 from-rank) ?1)))
(t (chess-error 'could-not-diff)))
- (nconc (chess-ply-changes ply)
- (list :which differentiator)))))
+ (chess-ply-set-keyword ply :which differentiator))))
(concat
(unless (= (upcase from-piece) ?P)
(char-to-string (upcase from-piece)))
diff --git a/chess-display.el b/chess-display.el
index 037d44b..ff2da04 100644
--- a/chess-display.el
+++ b/chess-display.el
@@ -7,6 +7,8 @@
(require 'chess-var)
(require 'chess-algebraic)
(require 'chess-fen)
+(require 'chess-mouse)
+(require 'chess-input)
(defgroup chess-display nil
"Common code used by chess displays."
@@ -29,7 +31,7 @@
(mode-checkmate . "CHECKMATE")
(mode-resigned . "RESIGNED")
(mode-stalemate . "STALEMATE")
- (mode-drawn . "DRAWMN")))
+ (mode-drawn . "DRAWN")))
(defcustom chess-display-mode-line-format
'(" " chess-display-side-to-move " "
@@ -215,8 +217,10 @@ also view the same game."
"Just redraw the current display."
(interactive)
(chess-with-current-buffer display
- (erase-buffer)
- (chess-display-update nil)))
+ (let ((here (point)))
+ (erase-buffer)
+ (chess-display-update nil)
+ (goto-char here))))
(defvar chess-display-index-positions nil)
@@ -265,6 +269,18 @@ If only START is given, it must be in algebraic move notation."
(chess-game-move chess-module-game ply))
(error "What to do here?? NYI"))))
+(defun chess-assert-can-move (position)
+ (if (and (chess-display-active-p)
+ ;; `active' means we're playing against an engine
+ (chess-game-data chess-module-game 'active)
+ (not (eq (chess-game-data chess-module-game 'my-color)
+ (chess-pos-side-to-move position))))
+ (chess-error 'not-your-move)
+ (if (and (= chess-display-index
+ (chess-game-index chess-module-game))
+ (chess-game-over-p chess-module-game))
+ (chess-error 'game-is-over))))
+
(defun chess-display-highlight (display &rest args)
"Highlight the square at INDEX on the current position.
The given highlighting MODE is used, or the default if the style you
@@ -304,6 +320,31 @@ that is supported by most displays, and is the default mode."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
+;; Default window and frame popup functions
+;;
+
+(defun chess-display-popup-in-window ()
+ "Popup the given DISPLAY, so that it's visible to the user."
+ (unless (get-buffer-window (current-buffer))
+ (fit-window-to-buffer (display-buffer (current-buffer)))))
+
+(defun chess-display-popup-in-frame (height width &optional display)
+ "Popup the given DISPLAY, so that it's visible to the user."
+ (let ((window (get-buffer-window (current-buffer) t)))
+ (if window
+ (let ((frame (window-frame window)))
+ (unless (eq frame (selected-frame))
+ (raise-frame frame)))
+ (let ((params (list (cons 'name "*Chessboard*")
+ (cons 'height height)
+ (cons 'width width))))
+ (if display
+ (push (cons 'display display) params))
+ (select-frame (make-frame params))
+ (set-window-dedicated-p (selected-window) t)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
;; Event handler
;;
@@ -463,7 +504,8 @@ The key bindings available in this mode are:
(use-local-map chess-display-mode-map)
(buffer-disable-undo)
(setq buffer-auto-save-file-name nil
- mode-line-format 'chess-display-mode-line-format))
+ mode-line-format 'chess-display-mode-line-format)
+ (setq chess-input-move-function 'chess-display-move))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
@@ -750,184 +792,6 @@ to the end or beginning."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; Default window and frame popup functions
-;;
-
-(defun chess-display-popup-in-window ()
- "Popup the given DISPLAY, so that it's visible to the user."
- (unless (get-buffer-window (current-buffer))
- (fit-window-to-buffer (display-buffer (current-buffer)))))
-
-(defun chess-display-popup-in-frame (height width &optional display)
- "Popup the given DISPLAY, so that it's visible to the user."
- (let ((window (get-buffer-window (current-buffer) t)))
- (if window
- (let ((frame (window-frame window)))
- (unless (eq frame (selected-frame))
- (raise-frame frame)))
- (let ((params (list (cons 'name "*Chessboard*")
- (cons 'height height)
- (cons 'width width))))
- (if display
- (push (cons 'display display) params))
- (select-frame (make-frame params))
- (set-window-dedicated-p (selected-window) t)))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Maintain a face cache for given color strings
-;;
-
-(defvar chess-display-face-cache '((t . t)))
-
-(defun chess-display-get-face (color)
- (or (cdr (assoc color chess-display-face-cache))
- (let ((face (make-face 'chess-display-highlight)))
- (set-face-attribute face nil :background color)
- (add-to-list 'chess-display-face-cache (cons color face))
- face)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Keyboard entry of algebraic notation, using shortcutting
-;;
-;; This scheme was adapted from the way SCID
-;; (http://scid.sourceforge.net), by Shane Hudson, behaves. It's the
-;; only way to move your pieces around!
-;;
-
-(defvar chess-move-string "")
-(defvar chess-legal-moves-pos nil)
-(defvar chess-legal-moves nil)
-
-(make-variable-buffer-local 'chess-move-string)
-(make-variable-buffer-local 'chess-legal-moves-pos)
-(make-variable-buffer-local 'chess-legal-moves)
-
-(chess-message-catalog 'english
- '((not-your-move . "It is not your turn to move")
- (game-is-over . "This game is over")))
-
-(defun chess-display-assert-can-move (position)
- (if (and (chess-display-active-p)
- ;; `active' means we're playing against an engine
- (chess-game-data chess-module-game 'active)
- (not (eq (chess-game-data chess-module-game 'my-color)
- (chess-pos-side-to-move position))))
- (chess-error 'not-your-move)
- (if (and (= chess-display-index
- (chess-game-index chess-module-game))
- (chess-game-over-p chess-module-game))
- (chess-error 'game-is-over))))
-
-(defun chess-keyboard-test-move (move-ply)
- "Return the given MOVE if it matches the user's current input."
- (let* ((move (cdr move-ply))
- (i 0) (x 0) (l (length move))
- (xl (length chess-move-string))
- (match t))
- (unless (or (and (equal (downcase chess-move-string) "ok")
- (string-match "\\`O-O[+#]\\'" move))
- (and (equal (downcase chess-move-string) "oq")
- (string-match "\\`O-O-O[+#]\\'" move)))
- (while (and (< i l) (< x xl))
- (let ((move-char (aref move i))
- (entry-char (aref chess-move-string x)))
- (if (and (= move-char ?x)
- (/= entry-char ?x))
- (setq i (1+ i))
- (if (/= entry-char (if (< entry-char ?a)
- move-char
- (downcase move-char)))
- (setq match nil i l)
- (setq i (1+ i) x (1+ x)))))))
- (if match
- move-ply)))
-
-(defsubst chess-keyboard-display-moves (&optional move-list)
- (if (> (length chess-move-string) 0)
- (message "[%s] %s" chess-move-string
- (mapconcat 'cdr
- (or move-list
- (delq nil (mapcar 'chess-keyboard-test-move
- (cdr chess-legal-moves))))
- " "))))
-
-(defun chess-keyboard-shortcut-delete ()
- (interactive)
- (when (and chess-move-string
- (stringp chess-move-string)
- (> (length chess-move-string) 0))
- (setq chess-move-string
- (substring chess-move-string 0 (1- (length chess-move-string))))
- (chess-keyboard-display-moves)))
-
-(defun chess-keyboard-shortcut (&optional display-only)
- (interactive)
- (let* ((position (chess-display-position nil))
- (color (chess-pos-side-to-move position))
- char)
- (chess-display-assert-can-move position)
- (unless (memq last-command '(chess-keyboard-shortcut
- chess-keyboard-shortcut-delete))
- (setq chess-move-string nil))
- (unless display-only
- (setq chess-move-string
- (concat chess-move-string (char-to-string last-command-char))))
- (unless (and chess-legal-moves
- (eq position chess-legal-moves-pos)
- (or (> (length chess-move-string) 1)
- (eq (car chess-legal-moves) last-command-char)))
- (setq char (if (eq (downcase last-command-char) ?o) ?k
- last-command-char)
- chess-legal-moves-pos position
- chess-legal-moves
- (cons char
- (sort
- (mapcar
- (function
- (lambda (ply)
- (cons ply (chess-ply-to-algebraic ply))))
- (if (eq char ?b)
- (append (chess-legal-plies
- position :piece (if color ?P ?p) :file 1)
- (chess-legal-plies
- position :piece (if color ?B ?b)))
- (if (and (>= char ?a)
- (<= char ?h))
- (chess-legal-plies position
- :piece (if color ?P ?p)
- :file (- char ?a))
- (chess-legal-plies position
- :piece (if color
- (upcase char)
- (downcase char))))))
- (function
- (lambda (left right)
- (string-lessp (cdr left) (cdr right)))))))))
- (let ((moves (delq nil (mapcar 'chess-keyboard-test-move
- (cdr chess-legal-moves)))))
- (cond
- ((or (= (length moves) 1)
- ;; if there is an exact match except for case, it must be an
- ;; abiguity between a bishop and a b-pawn move. In this
- ;; case, always take the b-pawn move; to select the bishop
- ;; move, use B to begin the keyboard shortcut
- (and (= (length moves) 2)
- (string= (downcase (cdr (car moves)))
- (downcase (cdr (cadr moves))))
- (setq moves (cdr moves))))
- (chess-display-move nil (caar moves))
- (setq chess-move-string nil
- chess-legal-moves nil
- chess-legal-moves-pos nil))
- ((null moves)
- (chess-keyboard-shortcut-delete))
- (t
- (chess-keyboard-display-moves moves)))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
;; Mousing around on the chess-display
;;
@@ -1019,6 +883,20 @@ Clicking once on a piece selects it; then click on the target location."
(goto-char (posn-point (event-end event)))
(chess-display-select-piece))))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Maintain a face cache for given color strings
+;;
+
+(defvar chess-display-face-cache '((t . t)))
+
+(defun chess-display-get-face (color)
+ (or (cdr (assoc color chess-display-face-cache))
+ (let ((face (make-face 'chess-display-highlight)))
+ (set-face-attribute face nil :background color)
+ (add-to-list 'chess-display-face-cache (cons color face))
+ face)))
+
(provide 'chess-display)
;;; chess-display.el ends here
diff --git a/chess-game.el b/chess-game.el
index a34d6a3..43a62e5 100644
--- a/chess-game.el
+++ b/chess-game.el
@@ -232,24 +232,30 @@ progress (nil), if it is drawn, resigned, mate, etc."
(let ((current-ply (chess-game-ply game))
(changes (chess-ply-changes ply))
(position (chess-ply-pos ply)))
+
(if (chess-ply-final-p current-ply)
(chess-error 'add-to-completed))
+
(assert (equal position (chess-ply-pos current-ply)))
(chess-ply-set-changes current-ply changes)
(chess-game-add-ply game (chess-ply-create*
(chess-ply-next-pos current-ply) t))
- (if (> (length changes) 2)
- (if (chess-ply-any-keyword ply :resign :checkmate)
- (let ((color (chess-game-side-to-move game)))
- (chess-game-set-tag game "Result" (if color "0-1" "1-0"))
- (if (chess-ply-keyword ply :resign)
- (chess-game-run-hooks game 'resign color)
- (chess-game-run-hooks game 'move current-ply)))
- (when (chess-ply-any-keyword ply :draw :perpetual :repetition
- :stalemate)
- (chess-game-set-tag game "Result" "1/2-1/2")
- (chess-game-run-hooks game 'drawn)))
- (chess-game-run-hooks game 'move current-ply))
+
+ (let ((long (> (length changes) 2)))
+ (cond
+ ((and long (chess-ply-any-keyword ply :resign :checkmate))
+ (let ((color (chess-game-side-to-move game)))
+ (chess-game-set-tag game "Result" (if color "0-1" "1-0"))
+ (if (chess-ply-keyword ply :resign)
+ (chess-game-run-hooks game 'resign color)
+ (chess-game-run-hooks game 'move current-ply))))
+ ((and long (chess-ply-any-keyword ply :draw :perpetual :repetition
+ :stalemate))
+ (chess-game-set-tag game "Result" "1/2-1/2")
+ (chess-game-run-hooks game 'drawn))
+ (t
+ (chess-game-run-hooks game 'move current-ply))))
+
(chess-game-run-hooks game 'post-move)))
(defsubst chess-game-end (game keyword)
diff --git a/chess-german.el b/chess-german.el
index be8c720..3ada61d 100644
--- a/chess-german.el
+++ b/chess-german.el
@@ -1,21 +1,89 @@
(require 'chess-message)
(chess-message-catalog 'german
- '((queen . "Dame")
- (king . "König")
- (bishop . "Läufer")
- (knight . "Springer")
- (rook . "Turm")
- (pawn . "Bauer")
- (short-castle . "Kurze Rochade")
- (long-castle . "Lange Rochade")
- (check . "Schach")
- (checkmate . "Schach matt")
- (stalemate . "Patt")
- (en-passant . "on possont")
- (piece-moves . "%s nach %s")
- (piece-takes . "%s schlägt %s auf %s")))
+ '((queen . "Dame")
+ (king . "König")
+ (bishop . "Läufer")
+ (knight . "Springer")
+ (rook . "Turm")
+ (pawn . "Bauer")
+ (short-castle . "Kurze Rochade")
+ (long-castle . "Lange Rochade")
+ (check . "Schach")
+ (checkmate . "Schach matt")
+ (stalemate . "Patt")
+ (en-passant . "on possont")
+ (piece-moves . "%s nach %s")
+ (piece-takes . "%s schlägt %s auf %s")
+
+ (illegal-move . "Illegal move")
+ (draw-offer-declined . "Your draw offer was declined")
+ (could-not-find-engine . "Cannot find %s executable; check `%s'")
+ (starting-engine-done . "Starting chess program '%s'...done")
+ (starting-engine . "Starting chess program '%s'...")
+ (chess-delete-autosave . "Delete the autosave file? ")
+ (chess-read-autosave . "There is a chess autosave file, read it? ")
+ (piece-images-loaded . "Loading chess piece images...done")
+ (piece-images-loading . "Loading chess piece images...")
+ (no-images . "Cannot find any piece images; check `chess-images-directory'")
+ (no-images-fallback . "Could not find suitable chess images")
+ (no-such-database . "There is no such chess database module '%s'")
+ (engine-not-running . "The engine you were using is no longer running")
+ (failed-start . "Failed to start chess engine process")
+ (opp-illegal . "Your opponent states your last command was illegal")
+ (opp-undo-ret . "Your opponent has retracted their request to undo %d moves")
+ (opp-abort-ret . "Your opponent has retracted their offer to abort")
+ (opp-draw-ret . "Your opponent has retracted their draw offer")
+ (opp-undo-dec . "Your request to undo %d moves was decline")
+ (opp-abort-dec . "Your offer to abort was declined")
+ (opp-draw-dec . "Your draw offer was declined")
+ (opp-undo-acc . "Request to undo %d moves was accepted")
+ (opp-abort-acc . "Your offer to abort was accepted")
+ (opp-draw-acc . "Your draw offer was accepted")
+ (opp-ready-a . "Your opponent is now ready to play")
+ (opp-ready . "Your opponent, %s, is now ready to play")
+ (opp-undo . "Your opponent wants to take back %d moves, accept? ")
+ (opp-abort . "Your opponent wants to abort this game, accept? ")
+ (opp-draw . "Your opponent offers a draw, accept? ")
+ (opp-resigned . "Your opponent has resigned")
+ (opp-quit . "Your opponent has quit playing")
+ (want-to-play-a . "Do you wish to play a chess game against an anonymous opponent? ")
+ (want-to-play . "Do you wish to play a chess game against %s? ")
+ (move-passed . "Your opponent has passed the move to you")
+ (now-black . "Your opponent played the first move, you are now black")
+ (invalid-pgn . "Received invalid PGN text")
+ (invalid-fen . "Received invalid FEN string: %s")
+ (piece-immobile . "That piece cannot move now")
+ (selected-empty . "You cannot select an empty square")
+ (wrong-color . "You cannot move your opponent's pieces")
+ (move-not-legal . "That is not a legal move")
+ (cannot-mount . "You cannot move pieces on top of each other")
+ (game-is-over . "This game is over")
+ (not-your-move . "It is not your turn to move")
+ (editing-directly . "Now editing position directly, use S when complete...")
+ (return-to-current . "Use '>' to return to the current position")
+ (draw-offer . "You offer a draw")
+ (want-to-quit . "Do you really want to quit? ")
+ (illegal-notation . "Illegal move notation: %s")
+ (no-such-style . "There is no such chessboard display style '%s'")
+ (mode-drawn . "DRAWN")
+ (mode-stalemate . "STALEMATE")
+ (mode-resigned . "RESIGNED")
+ (mode-checkmate . "CHECKMATE")
+ (mode-start . "START")
+ (mode-black . "Black")
+ (mode-white . "White")
+ (no-candidates . "There are no candidate moves for '%s'")
+ (could-not-diff . "Could not differentiate piece")
+ (could-not-clarify . "Could not determine which piece to use")
+ (clarify-piece . "Clarify piece to move by rank or file")
+ (no-such-module . "There is no module named '%s'")
+ (add-to-completed . "Cannot add moves to a completed game")
+ (undo-limit-reached . "Cannot undo further")
+ (pawn-promote-query . "Promote pawn to queen/rook/knight/bishop? ")
+ (piece-unrecognized . "Unrecognized piece identifier")
+ (move-from-blank . "Attempted piece move from blank square %s")))
(provide 'chess-german)
-;;; chess-german.el ends here
+;;;
diff --git a/chess-ics1.el b/chess-ics1.el
index 067a67a..4d0d35c 100644
--- a/chess-ics1.el
+++ b/chess-ics1.el
@@ -63,10 +63,10 @@
(defsubst chess-ics1-draw-square (pos piece index)
"Draw a piece image at point on an already drawn display."
(save-excursion
- (goto-char pos)
- (forward-char)
- (delete-char 1)
- (insert (chess-ics1-piece-text piece))))
+ (let ((inhibit-redisplay t))
+ (goto-char pos)
+ (delete-char 3)
+ (insert ? (chess-ics1-piece-text piece) ? ))))
(defun chess-ics1-draw (position perspective)
"Draw the given POSITION from PERSPECTIVE's point of view.
@@ -76,8 +76,7 @@ PERSPECTIVE is t for white or nil for black."
(erase-buffer)
(let* ((inverted (not perspective))
(rank (if inverted 7 0))
- (file (if inverted 7 0))
- beg)
+ (file (if inverted 7 0)) beg)
(insert "\n +---+---+---+---+---+---+---+---+\n")
(while (if inverted (>= rank 0) (< rank 8))
(if (/= rank (if inverted 7 0))
diff --git a/chess-images.el b/chess-images.el
index f92b9c7..597b2c8 100644
--- a/chess-images.el
+++ b/chess-images.el
@@ -415,11 +415,10 @@ This is necessary for bizzare Emacs reasons."
`(("background" . ,chess-images-dark-color)
("background" . ,chess-images-light-color))))))
(if foreground
- (nconc
- syms
- (list (nth foreground
- `(("dark_piece" . ,chess-images-black-color)
- ("light_piece" . ,chess-images-white-color))))))
+ (nconc syms
+ (list (nth foreground
+ `(("dark_piece" . ,chess-images-black-color)
+ ("light_piece" . ,chess-images-white-color))))))
(if chess-images-border-width
(create-image
(chess-images-hack-xpm file chess-images-border-width
diff --git a/chess-input.el b/chess-input.el
new file mode 100644
index 0000000..9a3a28a
--- /dev/null
+++ b/chess-input.el
@@ -0,0 +1,132 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Keyboard entry of algebraic notation, using shortcut notation
+;;
+;; This scheme was adapted from the way SCID
+;; (http://scid.sourceforge.net), by Shane Hudson, behaves. It's the
+;; only way to move your pieces around!
+;;
+
+(defvar chess-move-string "")
+(defvar chess-legal-moves-pos nil)
+(defvar chess-legal-moves nil)
+(defvar chess-input-move-function nil)
+
+(make-variable-buffer-local 'chess-move-string)
+(make-variable-buffer-local 'chess-legal-moves-pos)
+(make-variable-buffer-local 'chess-legal-moves)
+(make-variable-buffer-local 'chess-input-move-function)
+
+(chess-message-catalog 'english
+ '((not-your-move . "It is not your turn to move")
+ (game-is-over . "This game is over")))
+
+(defun chess-keyboard-test-move (move-ply)
+ "Return the given MOVE if it matches the user's current input."
+ (let* ((move (cdr move-ply))
+ (i 0) (x 0) (l (length move))
+ (xl (length chess-move-string))
+ (match t))
+ (unless (or (and (equal (downcase chess-move-string) "ok")
+ (string-match "\\`O-O[+#]?\\'" move))
+ (and (equal (downcase chess-move-string) "oq")
+ (string-match "\\`O-O-O[+#]?\\'" move)))
+ (while (and (< i l) (< x xl))
+ (let ((move-char (aref move i))
+ (entry-char (aref chess-move-string x)))
+ (if (and (= move-char ?x)
+ (/= entry-char ?x))
+ (setq i (1+ i))
+ (if (/= entry-char (if (< entry-char ?a)
+ move-char
+ (downcase move-char)))
+ (setq match nil i l)
+ (setq i (1+ i) x (1+ x)))))))
+ (if match
+ move-ply)))
+
+(defsubst chess-keyboard-display-moves (&optional move-list)
+ (if (> (length chess-move-string) 0)
+ (message "[%s] %s" chess-move-string
+ (mapconcat 'cdr
+ (or move-list
+ (delq nil (mapcar 'chess-keyboard-test-move
+ (cdr chess-legal-moves))))
+ " "))))
+
+(defun chess-keyboard-shortcut-delete ()
+ (interactive)
+ (when (and chess-move-string
+ (stringp chess-move-string)
+ (> (length chess-move-string) 0))
+ (setq chess-move-string
+ (substring chess-move-string 0 (1- (length chess-move-string))))
+ (chess-keyboard-display-moves)))
+
+(defun chess-keyboard-shortcut (&optional display-only)
+ (interactive)
+ (let* ((position (chess-display-position nil))
+ (color (chess-pos-side-to-move position))
+ char)
+ (chess-display-assert-can-move position)
+ (unless (memq last-command '(chess-keyboard-shortcut
+ chess-keyboard-shortcut-delete))
+ (setq chess-move-string nil))
+ (unless display-only
+ (setq chess-move-string
+ (concat chess-move-string (char-to-string last-command-char))))
+ (unless (and chess-legal-moves
+ (eq position chess-legal-moves-pos)
+ (or (> (length chess-move-string) 1)
+ (eq (car chess-legal-moves) last-command-char)))
+ (setq char (if (eq (downcase last-command-char) ?o) ?k
+ last-command-char)
+ chess-legal-moves-pos position
+ chess-legal-moves
+ (cons char
+ (sort
+ (mapcar
+ (function
+ (lambda (ply)
+ (cons ply (chess-ply-to-algebraic ply))))
+ (if (eq char ?b)
+ (append (chess-legal-plies
+ position :piece (if color ?P ?p) :file 1)
+ (chess-legal-plies
+ position :piece (if color ?B ?b)))
+ (if (and (>= char ?a)
+ (<= char ?h))
+ (chess-legal-plies position
+ :piece (if color ?P ?p)
+ :file (- char ?a))
+ (chess-legal-plies position
+ :piece (if color
+ (upcase char)
+ (downcase char))))))
+ (function
+ (lambda (left right)
+ (string-lessp (cdr left) (cdr right)))))))))
+ (let ((moves (delq nil (mapcar 'chess-keyboard-test-move
+ (cdr chess-legal-moves)))))
+ (cond
+ ((or (= (length moves) 1)
+ ;; if there is an exact match except for case, it must be an
+ ;; abiguity between a bishop and a b-pawn move. In this
+ ;; case, always take the b-pawn move; to select the bishop
+ ;; move, use B to begin the keyboard shortcut
+ (and (= (length moves) 2)
+ (string= (downcase (cdr (car moves)))
+ (downcase (cdr (cadr moves))))
+ (setq moves (cdr moves))))
+ (funcall chess-input-move-function nil (caar moves))
+ (setq chess-move-string nil
+ chess-legal-moves nil
+ chess-legal-moves-pos nil))
+ ((null moves)
+ (chess-keyboard-shortcut-delete))
+ (t
+ (chess-keyboard-display-moves moves)))))
+
+(provide 'chess-input)
+
+;;; chess-input.el ends here
diff --git a/chess-pgn.el b/chess-pgn.el
index 5460caf..41abf86 100644
--- a/chess-pgn.el
+++ b/chess-pgn.el
@@ -26,7 +26,7 @@
(goto-char (match-end 0))
(setq prevpos position)
(let* ((move (match-string 0))
- (ply (chess-algebraic-to-ply position (match-string 0))))
+ (ply (chess-algebraic-to-ply position move)))
(unless ply
(chess-error 'pgn-read-error move))
(setq position (chess-ply-next-pos ply))
diff --git a/chess-plain.el b/chess-plain.el
index bc0e46f..6041dba 100644
--- a/chess-plain.el
+++ b/chess-plain.el
@@ -66,11 +66,31 @@ modify `chess-plain-piece-chars' to avoid real confusion.)"
:type '(choice (const 'color) (const 'square-color)))
;; fails somehow
-(defcustom chess-plain-spacing 0
+(defcustom chess-plain-spacing 1
"*Number of spaces between pieces."
:group 'chess-plain
:type 'integer)
+(defface chess-plain-black-face
+ '((((class color) (background light)) (:foreground "Green"))
+ (((class color) (background dark)) (:foreground "Green"))
+ (t (:bold t)))
+ "*The face used for black pieces on the ASCII display."
+ :group 'chess-plain)
+
+(defface chess-plain-white-face
+ '((((class color) (background light)) (:foreground "Yellow"))
+ (((class color) (background dark)) (:foreground "Yellow"))
+ (t (:bold t)))
+ "*The face used for white pieces on the ASCII display."
+ :group 'chess-plain)
+
+(defface chess-plain-highlight-face
+ '((((class color) (background light)) (:background "#add8e6"))
+ (((class color) (background dark)) (:background "#add8e6")))
+ "Face to use for highlighting pieces that have been selected."
+ :group 'chess-plain)
+
(defcustom chess-plain-popup-function 'chess-display-popup-in-window
"The function used to popup a chess-plain display."
:type 'function
@@ -101,14 +121,19 @@ modify `chess-plain-piece-chars' to avoid real confusion.)"
(if white-square
chess-plain-white-square-char
chess-plain-black-square-char)
- (let ((what chess-plain-upcase-indicates)
- (pchar (cdr (assq piece chess-plain-piece-chars))))
- (cond
- ((eq what 'square-color)
- (if white-square
- (downcase pchar)
- (upcase pchar)))
- (t pchar))))))
+ (let* ((what chess-plain-upcase-indicates)
+ (pchar (cdr (assq piece chess-plain-piece-chars)))
+ (piece (cond
+ ((eq what 'square-color)
+ (if white-square
+ (downcase pchar)
+ (upcase pchar)))
+ (t pchar)))
+ (p (char-to-string piece)))
+ (add-text-properties 0 1 (list 'face (if (> piece ?a)
+ 'chess-ics1-black-face
+ 'chess-ics1-white-face)) p)
+ p))))
(defsubst chess-plain-draw-square (pos piece index)
"Draw a piece image at point on an already drawn display."
@@ -126,8 +151,7 @@ PERSPECTIVE is t for white or nil for black."
(erase-buffer)
(let* ((inverted (not perspective))
(rank (if inverted 7 0))
- (file (if inverted 7 0))
- beg)
+ (file (if inverted 7 0)) beg)
(if chess-plain-draw-border
(insert ? (nth 0 chess-plain-border-chars)
(make-string (+ 8 (* 7 chess-plain-spacing))
diff --git a/chess-ply.el b/chess-ply.el
index f44553b..27154bc 100644
--- a/chess-ply.el
+++ b/chess-ply.el
@@ -107,8 +107,7 @@
(defun chess-ply-create-castle (position &optional long king-index)
"Create castling changes; this function supports Fischer Random castling."
(let* ((color (chess-pos-side-to-move position))
- (king (or king-index
- (car (chess-pos-search position (if color ?K ?k)))))
+ (king (or king-index (chess-pos-king-index position color)))
(king-target (chess-rf-to-index (if color 7 0)
(if long 2 6)))
(king-file (chess-index-file king))
@@ -203,9 +202,10 @@ maneuver."
(let* ((chess-ply-checking-mate t)
(next-pos (chess-ply-next-pos ply))
(next-color (not color))
- (king (car (chess-pos-search next-pos
- (if next-color ?K ?k))))
- in-check)
+ (king (chess-pos-king-index next-pos next-color))
+ (in-check (catch 'in-check
+ (chess-search-position next-pos king
+ (not next-color) t))))
;; first, see if the moves leaves the king in check.
;; This is tested by seeing if any of the opponent's
;; pieces can reach the king in the position that will
@@ -213,10 +213,7 @@ maneuver."
;; will then test for checkmate by seeing if any of his
;; subjects can move or not. That test will also
;; confirm stalemate for us.
- (if (or (setq in-check
- (catch 'in-check
- (chess-search-position next-pos king
- (not next-color) t)))
+ (if (or in-check
(null (chess-legal-plies next-pos :any :index king)))
;; is the opponent's king in check/mate or stalemate
;; now, as a result of the changes?
@@ -245,7 +242,7 @@ maneuver."
(defsubst chess-ply--add (rank-adj file-adj &optional pos)
"This is totally a shortcut."
- (let ((target (or pos (chess-incr-index candidate rank-adj file-adj))))
+ (let ((target (or pos (chess-incr-index* candidate rank-adj file-adj))))
(if (and (or (not specific-target)
(= target specific-target))
(chess-pos-legal-moves position color target
@@ -320,10 +317,10 @@ criteria."
(ahead (chess-incr-index candidate bias 0))
(2ahead (chess-incr-index candidate (if color -2 2) 0)))
(when (chess-pos-piece-p position ahead ? )
- (chess-ply--add bias 0)
+ (chess-ply--add bias 0 ahead)
(if (and (= (if color 6 1) (chess-index-rank candidate))
- (chess-pos-piece-p position 2ahead ? ))
- (chess-ply--add (if color -2 2) 0)))
+ 2ahead (chess-pos-piece-p position 2ahead ? ))
+ (chess-ply--add (if color -2 2) 0 2ahead)))
(when (setq pos (chess-incr-index candidate bias -1))
(if (chess-pos-piece-p position pos (not color))
(chess-ply--add nil nil pos))
@@ -374,9 +371,8 @@ criteria."
(0 -1) (0 1)
(1 -1) (1 0) (1 1)))
(setq pos (apply 'chess-incr-index candidate dir))
- (if (and pos
- (or (chess-pos-piece-p position pos ? )
- (chess-pos-piece-p position pos (not color))))
+ (if (and pos (or (chess-pos-piece-p position pos ? )
+ (chess-pos-piece-p position pos (not color))))
(chess-ply--add nil nil pos)))
(if (chess-pos-can-castle position (if color ?K ?k))
diff --git a/chess-pos.el b/chess-pos.el
index 9564d6d..d016eef 100644
--- a/chess-pos.el
+++ b/chess-pos.el
@@ -106,7 +106,7 @@
"Return the piece on POSITION at INDEX."
(aref position index))
-(defsubst chess-pos-king-pos (position color)
+(defsubst chess-pos-king-index (position color)
(aref position (if color 72 73)))
(defsubst chess-pos-set-king-pos (position color index)
@@ -176,7 +176,7 @@ Also, EPD evaluation numbers/strings can be set here."
(nconc ann (list annotation))
(aset position 71 (list annotation)))))
-(defun chess-pos-copy (position)
+(defsubst chess-pos-copy (position)
"Create a new chess position, set at the starting position.
If BLANK is non-nil, all of the squares will be empty.
The current side-to-move always starts as white."
@@ -188,7 +188,7 @@ The current side-to-move always starts as white."
(chess-pos-set-status copy (copy-alist i)))
copy))
-(defun chess-pos-create (&optional blank)
+(defsubst chess-pos-create (&optional blank)
"Create a new chess position, set at the starting position.
If BLANK is non-nil, all of the squares will be empty.
The current side-to-move is always white."
@@ -242,17 +242,11 @@ color will do."
"Look on POSITION anywhere for PIECE-OR-COLOR, returning all coordinates.
If PIECE-OR-COLOR is t for white or nil for black, any piece of that
color will do."
- (cond
- ((eq piece-or-color ?K)
- (list (chess-pos-king-pos position t)))
- ((eq piece-or-color ?k)
- (list (chess-pos-king-pos position nil)))
- (t
- (let (found)
- (dotimes (i 64)
- (if (chess-pos-piece-p position i piece-or-color)
- (push i found)))
- found))))
+ (let (found)
+ (dotimes (i 64)
+ (if (chess-pos-piece-p position i piece-or-color)
+ (push i found)))
+ found))
(defsubst chess-pos-to-string (position &optional full)
(chess-pos-to-fen position full))
@@ -304,9 +298,9 @@ trying to move a blank square."
;; if the move was en-passant, remove the captured pawn
(if (memq :en-passant changes)
- (chess-pos-set-piece position (chess-incr-index (cadr changes)
- (if color 1 -1) 0)
- ? ))
+ (chess-pos-set-piece position
+ (chess-incr-index (cadr changes)
+ (if color 1 -1) 0) ? ))
;; once a piece is moved, en passant is no longer available
(chess-pos-set-en-passant position nil)
@@ -321,7 +315,7 @@ trying to move a blank square."
(chess-pos-set-can-castle position (if color ?Q ?q) nil))
((= piece ?r)
- (let ((king (car (chess-pos-search position (if color ?K ?k)))))
+ (let ((king (chess-pos-king-index position color)))
(if (and (chess-pos-can-castle position (if color ?K ?k))
(< (chess-index-file (car changes)) king))
(chess-pos-set-can-castle position (if color ?K ?k) nil)
@@ -413,7 +407,7 @@ indices which indicate where a piece may have moved from."
(and (= (chess-index-rank target) (if color 2 5))
;; make this fail if no en-passant is possible
(= (or (chess-pos-en-passant position) 100)
- (chess-incr-index target (if color 1 -1) 0))
+ (or (chess-incr-index target (if color 1 -1) 0) 200))
(or (and (setq pos (chess-incr-index target
(if color 1 -1) -1))
(chess-pos-piece-p position pos
@@ -424,21 +418,20 @@ indices which indicate where a piece may have moved from."
(if color ?P ?p)))))
(if color (> p ?a) (< p ?a)))
(progn
- (setq pos (chess-incr-index target (- bias) -1))
- (if (and pos (chess-pos-piece-p position pos piece))
+ (if (and (setq pos (chess-incr-index target (- bias) -1))
+ (chess-pos-piece-p position pos piece))
(chess--add-candidate pos))
- (setq pos (chess-incr-index target (- bias) 1))
- (if (and pos (chess-pos-piece-p position pos piece))
+ (if (and (setq pos (chess-incr-index target (- bias) 1))
+ (chess-pos-piece-p position pos piece))
(chess--add-candidate pos)))
(if (setq pos (chess-incr-index target (- bias) 0))
(if (chess-pos-piece-p position pos piece)
(chess--add-candidate pos)
- (when (and (chess-pos-piece-p position pos ? )
- (= (if color 4 3)
- (chess-index-rank target)))
- (setq pos (chess-incr-index pos (- bias) 0))
- (if (and pos (chess-pos-piece-p position pos piece))
- (chess--add-candidate pos))))))))
+ (if (and (chess-pos-piece-p position pos ? )
+ (= (if color 4 3) (chess-index-rank target))
+ (setq pos (chess-incr-index pos (- bias) 0))
+ (chess-pos-piece-p position pos piece))
+ (chess--add-candidate pos)))))))
;; the rook, bishop and queen are the easiest; just look along
;; rank and file and/or diagonal for the nearest pieces!
@@ -465,9 +458,8 @@ indices which indicate where a piece may have moved from."
(progn
(chess--add-candidate pos)
(setq pos nil))
- (if (not (chess-pos-piece-p position pos ? ))
- (setq pos nil)
- (setq pos (apply 'chess-incr-index pos dir)))))))
+ (setq pos (and (chess-pos-piece-p position pos ? )
+ (apply 'chess-incr-index pos dir)))))))
;; the king is a trivial case of the queen, except when castling
((= test-piece ?K)
@@ -492,7 +484,7 @@ indices which indicate where a piece may have moved from."
(chess-pos-can-castle position (if color ?K ?k)))
(and (equal target (chess-rf-to-index rank 2))
(chess-pos-can-castle position (if color ?Q ?q)))))
- (let* ((king (car (chess-pos-search position piece)))
+ (let* ((king (chess-pos-king-index position color))
(king-file (chess-index-file king))
(long (= 2 (chess-index-file target)))
(file (if long 1 6))
@@ -542,28 +534,32 @@ be moved, and TARGET is the index of the location to be moved to.
Note: All of the pieces specified by CANDIDATES must be of the same
type."
(let ((cand candidates)
- (test-piece (upcase (chess-pos-piece position (car candidates))))
- last-cand pos king-pos)
+ (piece (chess-pos-piece position (car candidates)))
+ (pos (chess-pos-copy position))
+ last-cand king-pos)
(while cand
;; determine the resulting position
- (setq pos (chess-pos-move (chess-pos-copy position)
- (car cand) target))
+ (chess-pos-set-piece pos (car cand) ? )
+ (chess-pos-set-piece pos target piece)
;; find the king (only once if the king isn't moving)
(if (or (null king-pos)
- (= test-piece ?K))
- (setq king-pos (chess-pos-search pos (if color ?K ?k))))
+ (memq piece '(?K ?k)))
+ (setq king-pos (chess-pos-king-index pos color)))
;; can anybody from the opposite side reach him? if so,
;; drop the candidate
(if (catch 'in-check
- (chess-search-position pos (car king-pos) (not color) t))
+ (chess-search-position pos king-pos (not color) t))
(if last-cand
(setcdr last-cand (cdr cand))
(setq candidates (cdr candidates)))
(setq last-cand cand))
+ ;; return the position to its original state
+ (when (cdr cand)
+ (chess-pos-set-piece pos target ? )
+ (chess-pos-set-piece pos (car cand) piece))
(setq cand (cdr cand)))
candidates))
-
(provide 'chess-pos)
;;; chess-pos.el ends here