diff options
| author | Mario Lang <mlang@delysid.org> | 2014-04-18 11:20:50 +0200 |
|---|---|---|
| committer | Mario Lang <mlang@delysid.org> | 2014-04-18 11:20:50 +0200 |
| commit | b66e365a32c042a62311e614db25a807b6022302 (patch) | |
| tree | cf59301bd3e584d26aa1c8928246073f0249bd6c | |
| parent | fd3dffbc98cc69abaf26dc3eaa4cecaeb4e541c3 (diff) | |
chess-perft.el: Count en passant and promotion plies.
Also, add ERT tags to selectively run tests which validate a certain
ply property, like :castle or :check.
| -rw-r--r-- | chess-perft.el | 115 |
1 files changed, 95 insertions, 20 deletions
diff --git a/chess-perft.el b/chess-perft.el index ea73533..b055d2c 100644 --- a/chess-perft.el +++ b/chess-perft.el @@ -20,22 +20,44 @@ ;;; Commentary: -;; Counts all leave nodes to a certain depth. +;; The classic perft function counts all leave nodes at a certain depth. +;; To make it easier to identify specific problems we also count properties +;; of the individual (final) plies. We count capturing plies, en passant plies, +;; castling plies, plies that promote to a piece, +;; plies which bring the opponent king in check and plies which result in +;; checkmate. + +;; Typically depths greater than 4 will result in very long runtimes. +;; We only define tests which do not take a lot of execution time +;; (less than a million nodes). + +;; To make it easier to selectively run tests, all tests provide tags +;; to indentify which type of ply they are covering. +;; The available ERT tags are: +;; :capture, :en-passant, :castle, :promotion, :check and :checkmate. +;; +;; For instance, to make sure castling plies work as expected, run +;; M-: (ert '(tag :castle)) RET ;;; Code: (require 'chess-fen) (require 'chess-ply) (require 'chess-pos) +(require 'cl-lib) (require 'ert) (defun chess-perft (position depth) + "Count all leave nodes of the tree starting at POSITION pruned at DEPTH. +The result is a list of the form + (LEAVES CAPTURES EN-PASSANTS CASTLES PROMOTIONS CHECKS CHECKMATES)." (if (zerop depth) (cl-values 1 0 0 0 0) (let ((plies (chess-legal-plies position :color (chess-pos-side-to-move position)))) (if (= depth 1) (cl-values (length plies) + ;; Captures (cl-count-if (lambda (ply) (or (chess-pos-piece-p @@ -44,110 +66,163 @@ (let ((en-passant (chess-pos-en-passant (chess-ply-pos ply)))) (and en-passant + (chess-pos-piece-p (chess-ply-pos ply) + en-passant + (not + (chess-pos-side-to-move + (chess-ply-pos ply)))) + (/= (chess-ply-target ply) en-passant) (/= (chess-pos-piece (chess-ply-pos ply) en-passant) (chess-pos-piece (chess-ply-next-pos ply) en-passant)))))) plies) + ;; En passants + (cl-count-if + (lambda (ply) + (let ((en-passant (chess-pos-en-passant + (chess-ply-pos ply)))) + (and en-passant + (chess-pos-piece-p (chess-ply-pos ply) + en-passant + (not + (chess-pos-side-to-move + (chess-ply-pos ply)))) + (/= (chess-ply-target ply) en-passant) + (/= (chess-pos-piece + (chess-ply-pos ply) en-passant) + (chess-pos-piece + (chess-ply-next-pos ply) en-passant))))) + plies) + ;; Castles (cl-count-if (lambda (ply) (chess-ply-any-keyword ply :castle :long-castle)) plies) + ;; Promotions + (cl-count-if + (lambda (ply) + (chess-ply-keyword ply :promote)) + plies) + ;; Checks (cl-count-if (lambda (ply) (chess-ply-any-keyword ply :check :checkmate)) plies) + ;; Checkmates (cl-count-if (lambda (ply) (chess-ply-any-keyword ply :checkmate)) plies)) - (let ((nodes 0) (captures 0) (castles 0) (checks 0) (checkmates 0)) - (dolist (ply plies (cl-values nodes captures castles checks checkmates)) - (cl-multiple-value-bind (n c ca ch cm) + (let ((nodes 0) (captures 0) (en-passants 0) + (castles 0) (promotions 0) + (checks 0) (checkmates 0)) + (dolist (ply plies (cl-values nodes + captures en-passants + castles promotions + checks checkmates)) + (cl-multiple-value-bind (n c e ca p ch cm) (chess-perft (chess-ply-next-pos ply) (1- depth)) (cl-incf nodes n) (cl-incf captures c) + (cl-incf en-passants e) (cl-incf castles ca) + (cl-incf promotions p) (cl-incf checks ch) (cl-incf checkmates cm)))))))) (ert-deftest chess-perft-startpos-depth1 () - (should (equal (chess-perft (chess-pos-create) 1) '(20 0 0 0 0)))) + (should (equal (chess-perft (chess-pos-create) 1) '(20 0 0 0 0 0 0)))) (ert-deftest chess-perft-startpos-depth2 () - (should (equal (chess-perft (chess-pos-create) 2) '(400 0 0 0 0)))) + (should (equal (chess-perft (chess-pos-create) 2) '(400 0 0 0 0 0 0)))) (ert-deftest chess-perft-startpos-depth3 () - (should (equal (chess-perft (chess-pos-create) 3) '(8902 34 0 12 0)))) + :tags '(:capture :check) + (should (equal (chess-perft (chess-pos-create) 3) '(8902 34 0 0 0 12 0)))) (ert-deftest chess-perft-startpos-depth4 () - (should (equal (chess-perft (chess-pos-create) 4) '(197281 1576 0 469 8)))) + :tags '(:capture :check :checkmate) + (should (equal (chess-perft (chess-pos-create) 4) '(197281 1576 0 0 0 469 8)))) (ert-deftest chess-perft-kiwipete-depth1 () + :tags '(:capture :castle) (let ((position (chess-fen-to-pos "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -"))) - (should (equal (chess-perft position 1) '(48 8 2 0 0))))) + (should (equal (chess-perft position 1) '(48 8 0 2 0 0 0))))) (ert-deftest chess-perft-kiwipete-depth2 () + :tags '(:capture :en-passant :castle :check) (let ((position (chess-fen-to-pos "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -"))) - (should (equal (chess-perft position 2) '(2039 351 91 3 0))))) + (should (equal (chess-perft position 2) '(2039 351 1 91 0 3 0))))) (ert-deftest chess-perft-kiwipete-depth3 () + "This test is expected to fail due to a (undetermined) bug in castling +generation. We do generate too many castling moves." + :tags '(:capture :en-passant :castle :check :checkmate) (let ((position (chess-fen-to-pos "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -"))) - (should (equal (chess-perft position 3) '(97862 17102 3162 993 1))))) + (should (equal (chess-perft position 3) '(97862 17102 45 3162 0 993 1))))) (ert-deftest chess-perft-pos3-depth1 () + :tags '(:capture :check) (let ((position (chess-fen-to-pos "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"))) - (should (equal (chess-perft position 1) '(14 1 0 2 0))))) + (should (equal (chess-perft position 1) '(14 1 0 0 0 2 0))))) (ert-deftest chess-perft-pos3-depth2 () + :tags '(:capture :check) (let ((position (chess-fen-to-pos "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"))) - (should (equal (chess-perft position 2) '(191 14 0 10 0))))) + (should (equal (chess-perft position 2) '(191 14 0 0 0 10 0))))) (ert-deftest chess-perft-pos3-depth3 () + :tags '(:capture :en-passant :check) (let ((position (chess-fen-to-pos "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"))) - (should (equal (chess-perft position 3) '(2812 209 0 267 0))))) + (should (equal (chess-perft position 3) '(2812 209 2 0 0 267 0))))) (ert-deftest chess-perft-pos3-depth4 () + :tags '(:capture :en-passant :check :checkmate) (let ((position (chess-fen-to-pos "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"))) - (should (equal (chess-perft position 4) '(43238 3348 0 1680 17))))) + (should (equal (chess-perft position 4) '(43238 3348 123 0 0 1680 17))))) (ert-deftest chess-perft-pos3-depth5 () + :tags '(:capture :en-passant :check) (let ((position (chess-fen-to-pos "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"))) - (should (equal (chess-perft position 5) '(674624 52051 0 52950 0))))) + (should (equal (chess-perft position 5) '(674624 52051 1165 0 0 52950 0))))) (ert-deftest chess-perft-pos4-depth1 () (let ((chess-ply-allow-interactive-query nil) (position (chess-fen-to-pos "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq -"))) - (should (equal (chess-perft position 1) '(6 0 0 0 0))))) + (should (equal (chess-perft position 1) '(6 0 0 0 0 0 0))))) (ert-deftest chess-perft-pos4-depth2 () + :tags '(:capture :castle :promotion :check) (let ((chess-ply-allow-interactive-query nil) (position (chess-fen-to-pos "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq -"))) - (should (equal (chess-perft position 2) '(264 87 6 10 0))))) + (should (equal (chess-perft position 2) '(264 87 0 6 48 10 0))))) (ert-deftest chess-perft-pos4-depth3 () + :tags '(:capture :en-passant :promotion :check :checkmate) (let ((chess-ply-allow-interactive-query nil) (position (chess-fen-to-pos "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq -"))) - (should (equal (chess-perft position 3) '(9467 1021 0 38 22))))) + (should (equal (chess-perft position 3) '(9467 1021 4 0 120 38 22))))) (ert-deftest chess-perft-pos4-depth4 () + :tags '(:capture :castle :promotion :check :checkmate) (let ((chess-ply-allow-interactive-query nil) (position (chess-fen-to-pos "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq -"))) - (should (equal (chess-perft position 4) '(422333 131393 7795 15492 5))))) + (should (equal (chess-perft position 4) '(422333 131393 0 7795 60032 15492 5))))) (provide 'chess-perft) ;;; chess-perft.el ends here |
