@@ -156,13 +156,19 @@ func root(toEngine chan bool, frEngine chan string) {
156156 alpha , beta = minEval , maxEval
157157 for ix , mv := range ml {
158158 childPV .clear ()
159-
160159 b .move (mv )
161160 tell ("info depth " , strconv .Itoa (depth ), " currmove " , mv .String (), " currmovenumber " , strconv .Itoa (ix + 1 ))
162161 score := - search (- beta , - alpha , depth - 1 , 1 , & childPV , b )
163-
162+
164163 b .unmove (mv )
165164
165+ ////////////////////////////////////
166+ if ! checkKey (b ) {
167+ fmt .Println ("fullkey=" , b .fullKey (), "Key" , b .key , mv .StringFull ())
168+ fmt .Println ("INVALID KEY AFTER UNMOVE ROOT" )
169+ }
170+ ///////////////////////////////////////
171+
166172 if limits .stop {
167173 break
168174 }
@@ -201,11 +207,8 @@ func root(toEngine chan bool, frEngine chan string) {
201207 }
202208}
203209
204- //TODO search: hash table/transposition table
205-
206- //TODO search: history table and maybe counter move table
207- //TODO search: move generation. More fast and accurate
208210//TODO search: Null Move
211+
209212//TODO search: Late Move Reduction
210213//TODO search: Internal Iterative Depening
211214//TODO search: Delta Pruning
@@ -219,15 +222,19 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
219222 }
220223 pv .clear ()
221224
225+ pvNode := depth > 0 && beta != alpha + 1
226+
222227 transMove := noMove
228+ useTT := depth >= 0
223229 transDepth := depth
224- pvNode := depth > 0 && beta != alpha + 1
230+ inCheck := b . isAttacked ( b . King [ b . stm ], b . stm . opp ())
225231
226- if depth < 0 { // inCheck?
232+ if depth < 0 && inCheck {
233+ useTT = true
227234 transDepth = 0
228235 }
229236
230- { // keep spme variables local just to be sure - TRANS.RETRIEVE
237+ if useTT {
231238 var transSc , scType int
232239 ok := false
233240
@@ -248,14 +255,34 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
248255
249256 var childPV pvList
250257 childPV .new () // TODO? make it smaller for each depth maxDepth-ply
258+ /////////////////////////////////////// NULL MOVE /////////////////////////////////////////
259+ ev := signEval (b .stm , evaluate (b ))
260+ // null-move pruning
261+ if ! pvNode && depth > 0 && ! isMateScore (beta ) && ! inCheck && ! b .isAntiNullMove () && ev >= beta {
262+ nullMv := b .moveNull ()
263+ sc := minEval
264+ if depth <= 3 { // static
265+ // if you don't beat me with 100 points,
266+ // then I think your position sucks
267+ sc = - qs (- beta + 1 , b )
268+ } else { // dynamic
269+ sc = - search (- beta , - beta + 1 , depth - 3 - 1 , ply , & childPV , b )
270+ }
271+
272+ b .undoNull (nullMv )
273+
274+ if sc >= beta {
275+ if useTT {
276+ trans .store (b .fullKey (), noMove , transDepth , ply , sc , scoreTypeLower )
277+ }
278+ return sc
279+ }
280+ }
281+ /////////////////////// NULL MOVE END //////////////////////
282+
251283 bs , score := noScore , noScore
252284 bm := noMove
253285
254- /* var ml moveList
255- ml.new(60)
256- genInOrder(b, &ml, ply, transMove)
257- for _, mv := range ml {
258- */
259286 var genInfo = genInfoStruct {sv : 0 , ply : ply , transMove : transMove }
260287 next = nextNormal
261288 for mv , msg := next (& genInfo , b ); mv != noMove ; mv , msg = next (& genInfo , b ) {
@@ -267,36 +294,37 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
267294
268295 childPV .clear ()
269296
270- /* if pvNode && bm != noMove {
271- score = -search(-alpha-1, -alpha, depth-1, ply+1, &childPV, b)
272- if score > alpha { // PVS/LMR re-search
297+ if pvNode && bm != noMove {
298+ score = - search (- alpha - 1 , - alpha , depth - 1 , ply + 1 , & childPV , b )
299+ if score > alpha {
273300 score = - search (- beta , - alpha , depth - 1 , ply + 1 , & childPV , b )
274301 }
275302 } else {
276- */
277- score = - search (- beta , - alpha , depth - 1 , ply + 1 , & childPV , b )
278- // }
303+ score = - search (- beta , - alpha , depth - 1 , ply + 1 , & childPV , b )
304+ }
279305
280306 b .unmove (mv )
281-
307+
282308 if score > bs {
283309 bs = score
284310 bm = mv
285311 pv .catenate (mv , & childPV )
286312 if score > alpha {
287313 alpha = score
288- trans .store (b .fullKey (), mv , depth , ply , score , scoreType (score , alpha , beta ))
314+ if useTT {
315+ trans .store (b .fullKey (), mv , transDepth , ply , score , scoreType (score , alpha , beta ))
316+ }
289317 }
290318
291319 if score >= beta { // beta cutoff
292320 // add killer and update history
293321 if mv .cp () == empty && mv .pr () == empty {
294322 killers .add (mv , ply )
323+ history .inc (mv .fr (), mv .to (), b .stm , depth )
295324 }
296325 if mv .cmp (transMove ) {
297326 trans .cPrune ++
298327 }
299- history .inc (mv .fr (), mv .to (), b .stm , depth )
300328 return score
301329 }
302330 }
@@ -328,6 +356,14 @@ func search(alpha, beta, depth, ply int, pv *pvList, b *boardStruct) int {
328356 return bs
329357}
330358
359+ // is this a position to avoid null move?
360+ func (b * boardStruct ) isAntiNullMove () bool {
361+ if b .wbBB [b .stm ] == b .pieceBB [King ]& b .wbBB [b .stm ] {
362+ return true
363+ }
364+ return false
365+ }
366+
331367func initQS (ml * moveList , b * boardStruct ) {
332368 ml .clear ()
333369 b .genAllCaptures (ml )
@@ -448,8 +484,8 @@ func see(fr, to int, b *boardStruct) int {
448484 n ++
449485
450486 // save the value of tha capturing piece to be used later
451- lastAtkVal = pVal [pt2pc (pt , WHITE )] // using WHITE always gives positive integer
452- stm = stm .opp () // next side to move
487+ lastAtkVal = pVal [pt2pc (pt , WHITE )] // using WHITE always gives positive integer
488+ stm = stm .opp () // next side to move
453489
454490 if pt == King && (attackingBB & b .wbBB [stm ]) != 0 { //NOTE: just changed stm-color above
455491 // if king capture and 'they' are atting we have to stop
@@ -582,7 +618,7 @@ func (k *killerStruct) clear() {
582618
583619// add killer 1 and 2 (Not inCheck, caaptures and promotions)
584620func (k * killerStruct ) add (mv move , ply int ) {
585- if ! k [ply ].k1 .cmp (mv ) {
621+ if ! k [ply ].k1 .cmpFrTo (mv ) {
586622 k [ply ].k2 = k [ply ].k1
587623 k [ply ].k1 = mv
588624 }
@@ -691,6 +727,7 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
691727 if b .isLegal (genInfo .transMove ) {
692728 return genInfo .transMove , "transMove"
693729 }
730+ genInfo .transMove = noMove
694731 }
695732 fallthrough
696733 case nextFirstGoodCp :
@@ -747,7 +784,7 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
747784 fallthrough
748785 case nextK1 : // not transMove
749786 genInfo .sv = nextK2
750- if killers [genInfo .ply ].k1 != noMove && ! genInfo .transMove .cmpFrTo (killers [genInfo .ply ].k1 ) {
787+ if killers [genInfo .ply ].k1 != noMove && ! genInfo .transMove .cmpFrToP (killers [genInfo .ply ].k1 ) {
751788 if b .isLegal (killers [genInfo .ply ].k1 ) {
752789 var mv move
753790 mv .packMove (killers [genInfo .ply ].k1 .fr (), killers [genInfo .ply ].k1 .to (), b .sq [killers [genInfo .ply ].k1 .fr ()], b .sq [killers [genInfo .ply ].k1 .to ()], killers [genInfo .ply ].k1 .pr (), b .ep , b .castlings )
@@ -758,7 +795,7 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
758795 fallthrough
759796 case nextK2 : // not transMove
760797 genInfo .sv = nextCounterMv
761- if killers [genInfo .ply ].k2 != noMove && ! genInfo .transMove .cmpFrTo (killers [genInfo .ply ].k2 ) {
798+ if killers [genInfo .ply ].k2 != noMove && ! genInfo .transMove .cmpFrToP (killers [genInfo .ply ].k2 ) {
762799 if b .isLegal (killers [genInfo .ply ].k2 ) {
763800 var mv move
764801 mv .packMove (killers [genInfo .ply ].k2 .fr (), killers [genInfo .ply ].k2 .to (), b .sq [killers [genInfo .ply ].k2 .fr ()], b .sq [killers [genInfo .ply ].k2 .to ()], killers [genInfo .ply ].k2 .pr (), b .ep , b .castlings )
@@ -768,11 +805,12 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
768805
769806 fallthrough
770807 case nextCounterMv : // not transMove, not killer1, not killer2
808+ genInfo .counterMv = noMove
771809 genInfo .sv = nextFirstNonCp
772- // if counterMovex[genInfo.ply][mv.to()] != noMove { // and not transMove and not k1 and not k2
810+ // if genInfo.counterMv != noMove && !genInfo.counterMv.cmpFrTo(genInfo.transMove) &&
811+ // genInfo.counterMv.cmpFrTo(killers[genInfo.ply].k1) && genInfo.counterMv.cmpFrTo(killers[genInfo.ply].k2) {
773812 // var mv move
774813 // mv.packMove(counterMv.fr(), counterMv.to(),b.sq[counterMv.fr()],b.sq[counterMv.to()],counterMv.pr(), b.ep,b.castlings)
775- genInfo .counterMv = noMove
776814 //check if it is a valid move
777815 // return sv, counterMovex[genInfo.ply][mv.to()]
778816 // }
@@ -786,7 +824,8 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
786824 bs := minEval
787825 bIx := - 1
788826 for ix := 0 ; ix < len (* ml ); ix ++ {
789- if (* ml )[ix ].cmp (genInfo .transMove ) || (* ml )[ix ].cmp (genInfo .counterMv ) || (* ml )[ix ].cmp (killers [genInfo .ply ].k1 ) || (* ml )[ix ].cmp (killers [genInfo .ply ].k2 ) {
827+ if (* ml )[ix ].cmpFrToP (genInfo .transMove ) || (* ml )[ix ].cmpFrToP (genInfo .counterMv ) ||
828+ (* ml )[ix ].cmpFrToP (killers [genInfo .ply ].k1 ) || (* ml )[ix ].cmpFrToP (killers [genInfo .ply ].k2 ) {
790829 continue
791830 }
792831 sc := int (history .get ((* ml )[ix ].fr (), (* ml )[ix ].to (), b .stm ))
@@ -814,8 +853,9 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
814853 bIx := - 1
815854 ml := & genInfo .nonCapt
816855 for ix := 0 ; ix < len (* ml ); ix ++ {
817- if (* ml )[ix ].cmp (genInfo .transMove ) || (* ml )[ix ].cmp (genInfo .counterMv ) || (* ml )[ix ].cmp (killers [genInfo .ply ].k1 ) || (* ml )[ix ].cmp (killers [genInfo .ply ].k2 ) {
818- continue
856+ if (* ml )[ix ].cmpFrToP (genInfo .transMove ) || (* ml )[ix ].cmpFrToP (genInfo .counterMv ) ||
857+ (* ml )[ix ].cmpFrToP (killers [genInfo .ply ].k1 ) || (* ml )[ix ].cmpFrToP (killers [genInfo .ply ].k2 ) {
858+ continue
819859 }
820860 sc := int (history .get ((* ml )[ix ].fr (), (* ml )[ix ].to (), b .stm ))
821861 if sc > bs {
@@ -837,13 +877,14 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
837877 // pick a bad capt - use see?
838878 mv := noMove
839879 ml := & genInfo .captures
840- for ix := len (* ml )- 1 ; ix >= 0 ; ix -- {
880+ for ix := len (* ml ) - 1 ; ix >= 0 ; ix -- {
841881 if (* ml )[ix ].cmp (genInfo .transMove ) {
882+ * ml = (* ml )[:len (* ml )- 1 ]
842883 continue
843884 }
844885
845886 mv = (* ml )[ix ]
846- // (*ml)[ix], (*ml)[len(*ml)-1] = (*ml)[len(*ml)-1], (*ml)[ix]
887+ // (*ml)[ix], (*ml)[len(*ml)-1] = (*ml)[len(*ml)-1], (*ml)[ix]
847888 * ml = (* ml )[:len (* ml )- 1 ]
848889 break
849890 }
@@ -853,3 +894,90 @@ func nextNormal(genInfo *genInfoStruct, b *boardStruct) (move, string) {
853894 panic ("neve come here! nextNormal sv=" + strconv .Itoa (genInfo .sv ))
854895 }
855896}
897+
898+ // StartPerft starts the Perft command that generates all moves until the given depth.
899+ // It counts the leafs only taht is printed out for each possible move from current pos
900+ func startPerft (depth int , bd * boardStruct ) uint64 {
901+ if depth <= 0 {
902+ fmt .Printf ("Total:\t %v\n " , 1 )
903+ return 0
904+ }
905+
906+ transMove := noMove
907+ transMove , _ , _ , _ = trans .retrieve (bd .fullKey (), depth , 0 )
908+
909+ totCount := uint64 (0 )
910+ var genInfo = genInfoStruct {sv : 0 , ply : 0 , transMove : transMove }
911+ next = nextNormal
912+ ix := 0
913+ for mv , msg := next (& genInfo , bd ); mv != noMove ; mv , msg = next (& genInfo , bd ) {
914+ if ! bd .move (mv ) {
915+ continue
916+ }
917+ dbg := false
918+ /*
919+ /////////////////////////////////////////////////////////////
920+ if mv.fr() == D4 && mv.to() == F4 {
921+ dbg = true
922+ }
923+ /////////////////////////////////////////////////////////////
924+ */
925+ count := perft (dbg , depth - 1 , 1 , bd )
926+ totCount += count
927+ fmt .Printf ("%2d: %v \t %v \t %v\n " , ix + 1 , mv .String (), count , msg )
928+
929+ bd .unmove (mv )
930+ ix ++
931+ }
932+ fmt .Println ("------------------" )
933+ fmt .Println ()
934+ fmt .Printf ("Total:\t %v\n " , totCount )
935+ return totCount
936+ }
937+
938+ func perft (dbg bool , depth , ply int , bd * boardStruct ) uint64 {
939+ if depth == 0 {
940+ return 1
941+ }
942+
943+ transMove := noMove
944+ transMove , _ , _ , _ = trans .retrieve (bd .fullKey (), depth , ply )
945+ ix := 0
946+ count := uint64 (0 )
947+ var genInfo = genInfoStruct {sv : 0 , ply : ply , transMove : transMove }
948+ next = nextNormal
949+ for mv , msg := next (& genInfo , bd ); mv != noMove ; mv , msg = next (& genInfo , bd ) {
950+ if ! bd .move (mv ) {
951+ continue
952+ }
953+ _ = msg
954+ deb := false
955+ /*
956+ ////////////////////////////////////////////////////////////////
957+ if dbg && mv.fr() == F5 && mv.to() == F4 {
958+ deb = true
959+ }
960+ if dbg && mv.fr() == E2 && mv.to() == E4 {
961+ deb = true
962+ }
963+ ////////////////////////////////////////////////////////////////
964+ */
965+ cnt := perft (deb , depth - 1 , ply + 1 , bd )
966+ count += cnt
967+ /*
968+ /////////////////////////////////////////////
969+ if dbg && !deb {
970+ fmt.Println(ix+1, ":(e4) ", mv.String(), msg, "\t", cnt)
971+ if ix==1{
972+ fmt.Println("K1",killers[ply].k1.StringFull())
973+ fmt.Println("K2",killers[ply].k2.StringFull())
974+ }
975+ }
976+ ////////////////////////////////////////////
977+ */
978+ bd .unmove (mv )
979+ ix ++
980+ }
981+
982+ return count
983+ }
0 commit comments