;;; Selector server: main routine
(defun selector ()
"Operate as a move selector server; invoked from a command line call."
(let ((pse (mk-pse)) (form nil))
(selector-init pse)
(dountil (pse-exiting pse)
(sleep 0.01)
(dowhile (and (listen (pse-to-pipe pse)) (not (pse-exiting pse)))
(setf form (read (pse-to-pipe pse)))
(write form) (newline t) ; For testing
(selector-dispatch pse form)))
(selector-term pse))
(values))
Here follows the somewhat skeletal dispatch routine. It can be called from either the main line (as seen above) or from the search at semi-regular intervals. Some commands make sense if no search is running, some others only work if a search is active. (Appropriate diagnostics are issued if there is a phase error.) Note how he dispatcher handles each command based on whether or not a search is underway:
The new CIL Toolkit has a PSE (Persistent Search Environment) structure that acts as a class with each instance holding all the state data for a search. An instance also hold the variables used by the toolkit's move selector server.
Since there have been a lot of revisions to the PSE, I'll repost the source:
;;; PSE Resets
(defun reset-pse-search-control (my-pse)
"Reset the search control items in a PSE."
(setf (pse-searching my-pse) nil)
(setf (pse-pondering my-pse) nil)
(setf (pse-canceling my-pse) nil)
my-pse)
(defun reset-pse-pir-vec (my-pse)
"Reset the ply indexed entries in the given PSE."
(when (pse-pir-vec my-pse)
(clear-pir-vec (pse-pir-vec my-pse))))
(defun reset-pse-history (my-pse)
"Reset the history entries in the given PSE."
(when (pse-history-vec my-pse)
(reset-history-vec (pse-history-vec my-pse))))
(defun reset-pse-transtables (my-pse)
"Reset the transposition table entries in the given PSE."
(when (pse-tt-pawn my-pse)
(clear-hashflt (pse-tt-pawn my-pse)))
(when (pse-tt-eval-vec my-pse)
(clear-hashflt-vec (pse-tt-eval-vec my-pse)))
(when (pse-tt-hint-vec my-pse)
(clear-hashflt-vec (pse-tt-hint-vec my-pse)))
(when (pse-tt-main-vec my-pse)
(clear-hashflt-vec (pse-tt-main-vec my-pse)))
(when (pse-tt-tbas-vec my-pse)
(clear-hashflt-vec (pse-tt-tbas-vec my-pse))))
(defun reset-pse-search-result (my-pse)
"Reset the search results in the given PSE."
(setf (pse-sts my-pse) sts-unterminated)
(setf (pse-pv my-pse) nil)
(setf (pse-expect my-pse) neginf-score)
(setf (pse-node-count my-pse) 0)
my-pse)
(defun reset-pse (my-pse)
"Reset the given PSE."
(setf (pse-pos my-pse) (clone-pos initial-array-pos))
(reset-pse-search-control my-pse)
(reset-pse-search-result my-pse)
(reset-pse-pir-vec my-pse)
(reset-pse-history my-pse)
(reset-pse-transtables my-pse)
my-pse)
Loading a position, plus play and unplay. The "advance-pos" name is the new name for the "execute-move" routine, likewise for "retreat-pos" vs "retract-move":
;;; PSE position loading
(defun load-pse-pos (my-pse my-pos)
"Load the given position into the given PSE."
(reset-pse my-pse)
(setf (pse-pos my-pse) (clone-pos my-pos))
my-pse)
;;; PSE advance/retreat
(defun advance-pse (my-pse my-move)
"Advance a PSE by playing a move."
(let ((old-pv (pse-pv my-pse)))
(reset-pse-search-result my-pse)
(mark-san-flags-move (pse-pos my-pse) my-move)
(advance-pos (pse-pos my-pse) my-move)
(when (and old-pv (same-move? my-move (first old-pv)))
(setf (pse-pv my-pse) (rest old-pv))))
my-pse)
(defun retreat-pse (my-pse)
"Retreat a PSE by unplaying the last played move."
(unless (positive? (pos-push-count (pse-pos my-pse)))
(error "retreat-pse: no move to unplay"))
(retreat-pos (pse-pos my-pse))
(reset-pse-search-result my-pse)
my-pse)
;;; Selector status association list construction assistant
(defun add-status-reply (my-pse my-ssr my-sal)
"Add a serial number and a reply symbol to a status association list."
(push (cons 'serial (pse-serial my-pse)) my-sal)
(push (cons 'action (svref as-ssr-vec my-ssr)) my-sal)
(push (cons 'reply my-ssr) my-sal)
(incf (pse-serial my-pse))
my-sal)
;;; Selector status association list output
(defun send-client (my-pse my-sal)
"Send the given status association list to the command processor client."
(write my-sal :stream (pse-to-pipe my-pse))
(newline (pse-to-pipe my-pse)))
* (time (scms-driver 2 "2r1nr1k/pp1q1p1p/3bpp2/5P2/1P1Q4/P3P3/1B3P1P/R3K1R1 w Q - 0 1"))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
Mate in two found: 1. Qxf6+ Ng7 2. Qxg7#
; Evaluation took:
; 0.01 seconds of real time
; 5.21e-4 seconds of user run time
; 1.2e-4 seconds of system run time
; 3,685,208 CPU cycles
; 0 page faults and
; 65,816 bytes consed.
;
[CB:Unsearched EX:MateIn2 NC:10 PV:(Qxf6+ Ng7 Qxg7#)]
* (time (scms-driver 2 "rnbqkbn1/ppppp3/7r/6pp/3P1p2/3BP1B1/PPP2PPP/RN1QK1NR w KQq - 0 1"))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
Mate in two found: 1. Qxh5+ Rg6 2. Qxg6#
; Evaluation took:
; 0.0 seconds of real time
; 0.001735 seconds of user run time
; 3.17e-4 seconds of system run time
; 5,474,272 CPU cycles
; 0 page faults and
; 297,128 bytes consed.
;
[CB:Unsearched EX:MateIn2 NC:64 PV:(Qxh5+ Rg6 Qxg6#)]
* (time (scms-driver 3 "r1b1k2r/pp2bppp/8/3N2q1/2p5/8/PPP2PPP/R2QR1K1 w kq - 0 1"))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
Mate in three found: 1. Nc7+ Kf8 2. Qd8+ Bxd8 3. Re8#
; Evaluation took:
; 0.0 seconds of real time
; 0.004883 seconds of user run time
; 9.67e-4 seconds of system run time
; 15,608,240 CPU cycles
; 0 page faults and
; 873,848 bytes consed.
;
[CB:Unsearched EX:MateIn3 NC:146 PV:(Nc7+ Kf8 Qd8+ Bxd8 Re8#)]
* (time (scms-driver 4 "r1b2rk1/pp1p1pp1/1b1p2B1/n1qQ2p1/8/5N2/P3RPPP/4R1K1 w - - 0 1"))
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:
Mate in four found: 1. Qxf7+ Rxf7 2. Re8+ Rf8 3. Rxf8+ Kxf8 4. Re8#
; Evaluation took:
; 1.56 seconds of real time
; 1.332603 seconds of user run time
; 0.22375 seconds of system run time
; 4,145,771,368 CPU cycles
; [Run times include 0.34 seconds GC run time]
; 0 page faults and
; 183,792,464 bytes consed.
;
[CB:Unsearched EX:MateIn4 NC:47122 PV:(Qxf7+ Rxf7 Re8+ Rf8 Rxf8+ Kxf8 Re8#)]
I've now gotten end-to-end move synchronization and position tracking working between a command processor and the move selection server. There's still some way to go here, but the basic linkage is working.
Here's an example logfile generated by the interactive command processor. It shows three "g" (go) commands and the resulting client/server communication:
Here are the routines that gate high level move generation requests to the low level generator functions. Note the use of filtering and the in-check status test:
;;; Move generation: checkmating moves
(defun generate-checkmates (my-pos)
"Return a list of checkmating moves for the given position."
(filter-checkmating-moves (mark-checkmate-flags my-pos (generate-checks my-pos))))
;;; Move generation: checking only legal moves
(defun generate-checks (my-pos)
"Return a list of checking moves for the given position."
(if (checked? my-pos)
(filter-checking-moves (mark-check-flags my-pos (generate-evasion my-pos)))
(generate-checks-nic my-pos)))
;;; Move generation: holder only legal moves
(defun generate-holders (my-pos)
"Return a list of holder moves for the given position."
(if (checked? my-pos)
(filter-holder-moves (generate-evasion my-pos))
(generate-holders-nic my-pos)))
;;; Move generation: gainer only legal moves
(defun generate-gainers (my-pos)
"Return a list of gainer moves for the given position."
(if (checked? my-pos)
(filter-gainer-moves (generate-evasion my-pos))
(generate-gainers-nic my-pos)))
;;; Move generation: full set of legal moves
(defun generate (my-pos)
"Return a complete list of moves for the given position."
(if (checked? my-pos)
(generate-evasion my-pos)
(generate-nic my-pos)))
;;; Move generation: full set of marked legal moves
(defun generate-marked (my-pos)
"Return a complete list of moves for the given position, fully marked."
(mark-san-flags my-pos (generate my-pos)))
;;; Move generation: sorted full set of marked legal moves
(defun generate-canon (my-pos)
"Return a complete list of moves for the given position, fully marked and sorted by SAN."
(sort-moves-by-san (generate-marked my-pos)))
;;; SAN string list generation
(defun generate-san-strings (my-pos)
"Produce a list of SAN move strings for the moves in the given position."
(mapcar #'san-string (generate-canon my-pos)))
And the following move generator is used in the example mate search; it generates all the moves and presents the result list with all the checking moves at the front of the list.
I am not sure whether I should write this comment. But here are some thoughts:
- For nearly two months now we are observing live the developing of a chess engine.
- It is written in a language that I do not understand very easily.
- This thread has grown and is now a big part of my dayly dose of CCC.
- The forum has the title "... discussion" but I only see a big monologue (with rare exceptions).
Here are some suggestions:
- Perhaps you can reduce the number of single posts to weekly updates or to important milestones.
- Perhaps you can start new smaller threads when a milestone is reached.
- Perhaps you can find a webspace and do the development there.
- Perhaps You could do the basic and boring work a little less open, like others do.
On the other hand:
- It is nice that you are very involved and obviously enthusiastic.
- It is a thread about programming and it is technical.
- I should better be working on my own engine and post some achievements here.
- It' s just my thoughts.