These routines can be employed in several areas in a chess program:
1) At the beginning of a search before any time consuming complex initialization, the quick mate/lose recognizers can be run on all the ply one positions to either find a fast and certain win or to exclude quick and certain losses. If nothing else, it can greatly reduce embarrassment due to possible inadequacies in the primary search.
2) The "is-lose-in-1? predicate can be used to implement the Capablanca resignation facility; this is named after Jose Capablanca who was never checkmated in professional play. Symbolic has used this from its beginning; more recently, the program has been using the "is-lose-in-2?" predicate as a resignation trigger as it assumes the opponent can see a mate in two.
Note the different move generator calls that impose a mild enhancement of move ordering:
Code: Select all
;;; Basic mate/lose predicates
(defun is-mate-in-1? (my-pos)
"Return t if the position is a mate in one; else return nil."
(let ((result nil) (moves (generate-checks my-pos)) (best-move nil))
(dountil (or result (null? moves))
(let ((move (pop moves)))
(advance-pos my-pos move)
(when (no-moves? my-pos)
(setf best-move move)
(set-mf best-move mf-mate)
(setf result t))
(retreat-pos my-pos)))
(values result best-move)))
(defun is-lose-in-1? (my-pos)
"Return t if the position is a lose in one; else return nil."
(let ((result t) (moves (generate-gainers-first my-pos)) (best-move nil))
(dowhile (and result moves)
(let ((move (pop moves)))
(advance-pos my-pos move)
(unless (is-mate-in-1? my-pos)
(setf best-move move)
(setf result nil))
(retreat-pos my-pos)))
(values result best-move)))
(defun is-mate-in-2? (my-pos)
"Return t if the position is a mate in two; else return nil."
(let ((result nil) (moves (generate-gainer-checks-first my-pos)) (best-move nil))
(dountil (or result (null? moves))
(let ((move (pop moves)))
(advance-pos my-pos move)
(when (is-lose-in-1? my-pos)
(setf best-move move)
(setf result t))
(retreat-pos my-pos)))
(values result best-move)))
(defun is-lose-in-2? (my-pos)
"Return t if the position is a lose in two; else return nil."
(let ((result t) (moves (generate-gainers-first my-pos)) (best-move nil))
(dowhile (and result moves)
(let ((move (pop moves)))
(advance-pos my-pos move)
(unless (is-mate-in-2? my-pos)
(setf best-move move)
(setf result nil))
(retreat-pos my-pos)))
(values result best-move)))
(defun is-mate-in-3? (my-pos)
"Return t if the position is a mate in three; else return nil."
(let ((result nil) (moves (generate-gainer-checks-first my-pos)) (best-move nil))
(dountil (or result (null? moves))
(let ((move (pop moves)))
(advance-pos my-pos move)
(when (is-lose-in-2? my-pos)
(setf best-move move)
(setf result t))
(retreat-pos my-pos)))
(values result best-move)))
Code: Select all
> (setf p (calc-pos-from-str "r1b1k2r/pp2bppp/8/3N2q1/2p5/8/PPP2PPP/R2QR1K1 w kq - 0 1"))
> (is-mate-in-1? p)
NIL ;
NIL
> (is-mate-in-2? p)
NIL ;
NIL
> (is-mate-in-3? p)
T ;
Nc7+