@@ -14,7 +14,6 @@ const (
1414
1515var cntNodes uint64
1616
17- //TODO search limits: start clock and testing for movetime
1817//TODO search limits: counting nodes and testing for limit.nodes
1918//TODO search limits: limit.depth
2019
@@ -95,8 +94,6 @@ func engine() (toEngine chan bool, frEngine chan string) {
9594 return
9695}
9796
98- //TODO root: Iterative Depening
99-
10097//TODO root: Aspiration search
10198func root (toEngine chan bool , frEngine chan string ) {
10299 var depth int
@@ -152,8 +149,8 @@ func root(toEngine chan bool, frEngine chan string) {
152149 }
153150}
154151
155- //TODO search: generate all moves and put captures first (temporary)
156- //TODO search: qs
152+ //TODO search: qs with SEE
153+
157154//TODO search: hash table/transposition table
158155//TODO search: history table and maybe counter move table
159156//TODO search: move generation. More fast and accurate
@@ -217,6 +214,146 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
217214 return bs
218215}
219216
217+ func initQS (ml * moveList , b * boardStruct ){
218+ ml .clear ()
219+ b .genAllCaptures (ml )
220+ }
221+ func qs (beta int , b * boardStruct ) int {
222+ ev := signEval (b .stm , evaluate (b ))
223+ if ev >= beta {
224+ // we are good. No need to try captures
225+ return ev
226+ }
227+ bs := ev
228+
229+ qsList := make (moveList ,0 ,60 )
230+ initQS (& qsList ,b ) // create attacks
231+
232+ done := bitBoard (0 )
233+
234+ // move loop
235+ for _ ,mv := range qsList {
236+ fr := mv .fr ()
237+ to := mv .to ()
238+
239+ // This works because we pick lower value pieces first
240+ if done .test (to ) {// Don't do the same to-sw again
241+ continue
242+ }
243+ done .set (to )
244+
245+ see := see (fr ,to ,b )
246+
247+ if see < 0 {
248+ continue // equal captures not interesting
249+ }
250+ if see == 0 && mv .cp () == empty {
251+ // must be a promotion that didn't captureand was not captured
252+ see = pieceVal [wQ ] - pieceVal [wP ]
253+ }
254+
255+ sc := ev + see
256+ if sc > bs {
257+ bs = sc
258+ if sc >= beta {
259+ return sc
260+ }
261+ }
262+ }
263+
264+ return bs
265+ }
266+ // see (Static Echange Evaluation)
267+ // Start with the capture fr-to and find out all the other captures to to-sq
268+ func see (fr , to int , b * boardStruct ) int {
269+ pc := b .sq [fr ]
270+ cp := b .sq [to ]
271+ cnt := 1
272+ us := pcColor (pc )
273+ them := us .opp ()
274+
275+ // All the attackers to the to-sq, but first remove the moving piece and use X-ray to the to-sq
276+ occ := b .allBB ()
277+ occ .clr (fr )
278+ attackingBB := mRookTab [to ].atks (occ )& (b .pieceBB [Rook ]| b .pieceBB [Queen ]) |
279+ mBishopTab [to ].atks (occ )& (b .pieceBB [Bishop ]| b .pieceBB [Queen ]) |
280+ (atksKnights [to ] & b .pieceBB [Knight ]) |
281+ (atksKings [to ] & b .pieceBB [King ]) |
282+ (b .wPawnAtksFr (to ) & b .pieceBB [Pawn ] & b .wbBB [BLACK ]) |
283+ (b .bPawnAtksFr (to ) & b .pieceBB [Pawn ] & b .wbBB [WHITE ])
284+ attackingBB &= occ
285+
286+ if (attackingBB & b .wbBB [them ]) == 0 { // 'they' have no attackers - good bye
287+ return abs (pieceVal [cp ]) // always return score from 'our' point of view
288+ }
289+
290+ // Now we continue to keep track of the material gain/loss for each capture
291+ // Always remove the last attacker and use x-ray to find possible new attackers
292+
293+ lastAtkVal := abs (pieceVal [pc ]) // save attacker piece value for later use
294+ var captureList [32 ]int
295+ n := 1
296+
297+ stm := them // change side to move
298+ var pt int
299+ var BB bitBoard
300+
301+ for {
302+ cnt ++
303+ switch { // select the least valuable attacker
304+ case (attackingBB & b .pieceBB [Pawn ] & b .wbBB [stm ]) != 0 :
305+ pt = Pawn
306+ case (attackingBB & b .pieceBB [Knight ] & b .wbBB [stm ]) != 0 :
307+ pt = Knight
308+ case (attackingBB & b .pieceBB [Bishop ] & b .wbBB [stm ]) != 0 :
309+ pt = Bishop
310+ case (attackingBB & b .pieceBB [Rook ] & b .wbBB [stm ]) != 0 :
311+ pt = Rook
312+ case (attackingBB & b .pieceBB [Queen ] & b .wbBB [stm ]) != 0 :
313+ pt = Queen
314+ case (attackingBB & b .pieceBB [King ] & b .wbBB [stm ]) != 0 :
315+ pt = King
316+ default :
317+ panic ("Don't come here in see! " )
318+ }
319+
320+ // now remove the pt above from the attackingBB and scan for new attackers by possible x-ray
321+ BB = attackingBB & (attackingBB & b .pieceBB [pt ] & b .wbBB [stm ])
322+ occ ^= (BB & - BB ) // turn off the rightmost bit from BB in occ
323+
324+ // pick sliding attacks again (do it from to-sq)
325+ attackingBB |= mRookTab [to ].atks (occ )& (b .pieceBB [Rook ]| b .pieceBB [Queen ]) |
326+ mBishopTab [to ].atks (occ )& (b .pieceBB [Bishop ]| b .pieceBB [Queen ])
327+ attackingBB &= occ // but only attacking pieces
328+
329+ captureList [n ] = - captureList [n - 1 ] + lastAtkVal
330+ n ++
331+
332+ // save the value of tha capturing piece to be used later
333+ lastAtkVal = abs (pieceVal [pt2pc (pt , stm )])
334+ stm = stm .opp () // next side to move
335+
336+ if pt == King && (attackingBB & b .pieceBB [King ]& b .wbBB [stm ]) != 0 {
337+ // if king capture and 'they' are atting we have to stop
338+ captureList [n ] = pieceVal [wK ]
339+ n ++
340+ break
341+ }
342+
343+ if attackingBB & b .wbBB [stm ] == 0 { // if no more attackers
344+ break
345+ }
346+
347+ }
348+
349+ // find the optimal capture sequence and 'our' material value will be on top
350+ for n -- ; n != 0 ; n -- {
351+ captureList [n - 1 ] = min (- captureList [n ], captureList [n - 1 ])
352+ }
353+
354+ return captureList [0 ]
355+ }
356+
220357func genAndSort (b * boardStruct , ml * moveList ) {
221358 ml .clear ()
222359 b .genAllLegals (ml )
0 commit comments