diff options
| author | John Wiegley <johnw@newartisans.com> | 2002-03-01 06:17:46 +0000 |
|---|---|---|
| committer | John Wiegley <johnw@newartisans.com> | 2002-03-01 06:17:46 +0000 |
| commit | f115e4627966ae900aef55cb10f9e6207dbe7adf (patch) | |
| tree | 0718fd859aa8995889a1d7b7a5dc9007c39767df /chess-standard.el | |
Initial revision
Diffstat (limited to 'chess-standard.el')
| -rw-r--r-- | chess-standard.el | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/chess-standard.el b/chess-standard.el new file mode 100644 index 0000000..4e11b1f --- /dev/null +++ b/chess-standard.el @@ -0,0 +1,218 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Standard Chess rules module +;; +;; This module implements the following events: +;; +;; `move' +;; +;; Make a move on the current board, if it is legal. +;; +;; `search' +;; +;; Pieces can be located by searching all legal paths a piece might +;; use to reach a particular square. Thus, to find all white pawn(s) +;; that could make it to e4 (either by taking a piece, or by moving +;; there) you'd call: +;; +;; (chess-standard-search POSITION (chess-coord-to-index "e4") ?P) +;; +;; This returns a list of indices specifying all white pawns that +;; could reach e4 in one move. NOTE: The general search order is from +;; upper-left clockwise. + +;; $Revision$ + +(defgroup chess-standard nil + "The rules of standard chess." + :group 'chess) + +;;; Code: + +;;;###autoload +(defun chess-standard (session var event &rest args) + (cond + ((eq event 'move) + (ignore + (chess-standard-validate (car args)) + (chess-game-move (chess-session-data session 'current-game) + (car args)))) + ((eq event 'search) + (apply 'chess-standard-search args)))) + +(defun chess-standard-validate (ply) + "Validate the given PLY against standard chess rules." + (let* ((pos (chess-ply-pos ply)) + (color (chess-pos-side-to-move pos)) + (source (car (chess-ply-changes ply))) + (piece (chess-pos-piece pos source)) + (target (cadr (chess-ply-changes ply)))) + + (if (eq piece ? ) + (signal 'chess-illegal + "Cannot move from a square that is empty")) + + (if (if color + (> piece ?a) + (< piece ?a)) + (signal 'chess-illegal + "Cannot move your opponents pieces")) + + (let ((enemy-piece (chess-pos-piece pos target))) + (if (and (not (eq enemy-piece ? )) + (if color + (< enemy-piece ?a) + (> enemy-piece ?a))) + (signal 'chess-illegal + "Cannot move on top of your own pieces"))) + + (unless (chess-standard-search pos target piece) + (signal 'chess-illegal "Illegal move")))) + +(defun chess-standard-search (position target piece) + "Look on POSITION from position TARGET for PIECE. +This routine looks along legal paths of movement for PIECE. + +If PIECE is t or nil, legal piece movements for any piece of that +color will be considered (t for white, nil for black). Otherwise, the +case of the PIECE determines color. + +The return value is a list of candidates, which means a list of +indices which indicate where a piece may have moved from." + (let* ((bias (if (and (char-valid-p piece) + (< piece ?a)) -1 1)) + (c (= bias -1)) + p pos candidates) + (cond + ;; if the piece is `t', it means to find the candidates resulting + ;; from any piece movement. This is useful for testing whether a + ;; king is in check, for example. + ((memq piece '(t nil)) + (setq candidates (list t)) + (dolist (p '(?P ?R ?N ?B ?K ?Q)) + (nconc candidates + (chess-standard-search position target + (if piece p (downcase p))))) + (setq candidates (cdr candidates))) + + ;; pawn movement, which is diagonal 1 when taking, but forward + ;; 1 or 2 when moving (the most complex piece, actually) + ((= (upcase piece) ?P) + (let ((p (chess-pos-piece position target))) + (if (if (= p ? ) + ;; check for en passant + (and (= (chess-index-rank target) (if c 2 5)) + (setq pos (chess-add-index target bias 0)) + (chess-pos-piece-p position pos (if c ?p ?P)) + (and (chess-pos-en-passant position) + (= (chess-pos-en-passant position) target)) + (setq candidates (list pos))) + (if c (> p ?a) (< p ?a))) + (let ((cands (list t))) + (setq pos (chess-add-index target (- bias) -1)) + (if (and pos (chess-pos-piece-p position pos piece)) + (nconc cands (list pos))) + (setq pos (chess-add-index target (- bias) 1)) + (if (and pos (chess-pos-piece-p position pos piece)) + (nconc cands (list pos))) + (if candidates + (nconc candidates (cdr cands)) + (setq candidates (cdr cands)))) + (if (setq pos (chess-add-index target (- bias) 0)) + (if (chess-pos-piece-p position pos piece) + (setq candidates (list pos)) + (when (and (= ? (chess-pos-piece position pos)) + (= (if c 4 3) (chess-index-rank target))) + (setq pos (chess-add-index pos (- bias) 0)) + (if (and pos (chess-pos-piece-p position pos piece)) + (setq candidates (list pos))))))))) + + ;; the rook, bishop and queen are the easiest; just look along + ;; rank and file and/or diagonal for the nearest pieces! + ((memq (upcase piece) '(?R ?B ?Q)) + (setq candidates (list t)) + (dolist (dir (cond + ((= (upcase piece) ?R) + '( (-1 0) + (0 -1) (0 1) + (1 0))) + ((= (upcase piece) ?B) + '((-1 -1) (-1 1) + + (1 -1) (1 1))) + ((= (upcase piece) ?Q) + '((-1 -1) (-1 0) (-1 1) + (0 -1) (0 1) + (1 -1) (1 0) (1 1))))) + ;; up the current file + (setq pos (apply 'chess-add-index target dir)) + (while pos + (if (chess-pos-piece-p position pos piece) + (progn + (nconc candidates (list pos)) + (setq pos nil)) + (if (/= (chess-pos-piece position pos) ? ) + (setq pos nil) + (setq pos (apply 'chess-add-index pos dir)))))) + (setq candidates (cdr candidates))) + + ;; the king is a trivial case of the queen, except when castling + ((= (upcase piece) ?K) + (let ((dirs '((-1 -1) (-1 0) (-1 1) + (0 -1) (0 1) + (1 -1) (1 0) (1 1)))) + (while dirs + ;; up the current file + (setq pos (apply 'chess-add-index target (car dirs))) + (if (and pos (chess-pos-piece-p position pos piece)) + (setq candidates (list pos) dirs nil) + (setq dirs (cdr dirs))))) + (let ((rank (if c 7 0))) + ;; if we can still castle, then the king and rook are in their + ;; squares; also, make sure that the user is not attempting to + ;; castle through check + (if (and + (null candidates) + (or (and (equal target (chess-rf-to-index rank 6)) + (chess-pos-can-castle position (if c ?K ?k)) + (setq pos (chess-rf-to-index rank 5)) + (chess-pos-piece-p position pos ? ) + (not (chess-standard-search position pos (not c))) + (setq pos (chess-rf-to-index rank 6)) + (chess-pos-piece-p position pos ? ) + (not (chess-standard-search position pos (not c)))) + (and (equal target (cons rank 2)) + (chess-pos-can-castle position (if c ?Q ?q)) + (setq pos (chess-rf-to-index rank 1)) + (chess-pos-piece-p position pos ? ) + (not (chess-standard-search position pos (not c))) + (setq pos (chess-rf-to-index rank 2)) + (chess-pos-piece-p position pos ? ) + (not (chess-standard-search position pos (not c))) + (setq pos (chess-rf-to-index rank 3)) + (chess-pos-piece-p position pos ? ) + (not (chess-standard-search position pos (not c)))))) + (setq candidates (list (chess-rf-to-index rank 4)))))) + + ;; the knight is a zesty little piece; there may be more than + ;; one, but at only one possible square in each direction + ((= (upcase piece) ?N) + (setq candidates (list t)) + (dolist (dir '((-2 -1) (-2 1) + (-1 -2) (-1 2) + (1 -2) (1 2) + (2 -1) (2 1))) + ;; up the current file + (if (and (setq pos (apply 'chess-add-index target dir)) + (chess-pos-piece-p position pos piece)) + (nconc candidates (list pos)))) + (setq candidates (cdr candidates))) + + (t (error "Unrecognized piece identifier"))) + + ;; return the discovered candidates list + candidates)) + +(provide 'chess-standard) + +;;; chess-standard.el ends here |
