forked from MoonstoneLight/Fizbo-Chess
-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.cpp
1633 lines (1483 loc) · 75.3 KB
/
search.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// main search functionality
#include "chess.h"
#include <intrin.h>
#include <math.h>
#include "threads.h"
void pass_message_to_GUI(char *);
static const unsigned int tto[]={0,3,3,2}; // node type of other children: PV->CUT, ALL->CUT, CUT->ALL
static const int piece_value_search[]={0,106,406,449,653,1327,1625,100}; // +69 on 8/1/2017
// globals
UINT64 tbhits;
unsigned int pv10[]={0,0,3,3,5,10,100}; // used for almost everything. Need king value!
int timer_depth; // depth at which timer is checked
int see_move(board *,unsigned int,unsigned int);
static void Qsort(board*,unsigned char*,unsigned int);
static void reduce_history(unsigned int bits,board *b){for(unsigned int i=0;i<sizeof(b->history_count)/sizeof(int);++i) b->history_count[0][i]=b->history_count[0][i]>>bits;}
static UINT64 get_opp_attacks(board *b){
static const unsigned int dirs1[2][2]={{7,9},{9,7}};
UINT64 opp_attacks,o,a;
unsigned long bit;
unsigned int pll=b->player-1,opp=pll^1;
a=b->piececolorBB[0][opp];
opp_attacks=(a<<dirs1[0][pll])|(a>>dirs1[1][pll]); // all pawn attacks
opp_attacks|=king_masks[b->kp[opp]]; // all king attacks
a=b->piececolorBB[1][opp]; // all knights
o=b->colorBB[opp]; // all opponent pieces - cannot X-ray through them.
while( a ){
GET_BIT(a)
opp_attacks|=knight_masks[bit]; // all knight attacks
}
a=b->piececolorBB[2][opp]|b->piececolorBB[4][opp]; // all bishops*
while( a ){
GET_BIT(a)
opp_attacks|=attacks_bb_B(bit,o); // all bishop* attacks, including X-ray
}
a=b->piececolorBB[3][opp]|b->piececolorBB[4][opp]; // all rooks*
while( a ){
GET_BIT(a)
opp_attacks|=attacks_bb_R(bit,o); // all rook* attacks, including X-ray
}
return(opp_attacks);
}
extern int param1,param2;
extern unsigned char g_promotion;
int probe_wdl(board*,int*);// Probe EGTB.
int probe_dtz(board*,int*);
static UINT64 root_hash;
static unsigned int pc_root;
static const int FU[6]={0,80,150,250,450,910}; // move futility margin
static const int futility_margin[8]={0,0,80,150,250,450,910}; // position futility margin, for depth 1-6. d=0 is never used.
static const unsigned int MCP[10]={0,11,12,10,20,27,29,58,53,69}; // move count based pruning, for depth 1-9.
static const float lmr_a[]={0.f,-0.60f,-0.50f,1.33f}; // additive LMR
static const float lmr_m[]={0.f, 0.52f, 0.46f,0.44444f}; // multiplicative LMR
unsigned int get_all_moves_new_part1(board*,unsigned char*,UINT64*);// get list of all available moves - captures and promotions only. Return count. Put moves on the list.
unsigned int get_all_moves_new_part2(board*,unsigned char*,UINT64);// get list of all available moves - excluding captures and promotions. Return count. Put moves on the list.
static inline int return_move(move_list *m,move *m1){*m1=m->sorted_moves[m->next_move++];return(1);}// return next move
typedef struct {
float out_1_nn[64]; // outputs for 1st layer
float out_2_nn[8]; // outputs for 2nd layer
float out_lasta_nn; // outputs for last layer
unsigned int inp_nn2[64]; // index of up to 64 pieces on the board. First one is bias - always 1. Terminated by 1000. Max length=32+bias+terminator=34.
} data_nn;
//float pass_forward_2_float(board *b,data_nn *d_nnp,unsigned int from,unsigned int to,unsigned int node_type,int SEE,int hist);
static int get_next_move(board *b,unsigned int ply,move_list *m,move *m1,unsigned short int cm,int depth,int node_type){
unsigned int i,j;
int curr_score;
if( m->moves_generated>1 && m->next_move>=m->mc )
return(0); //all moves generated and processed - finish
if( m->status==0 ){ // select TT move.
m->status=1; // 1=TT move picked
if( m->TTmove ){
((short int*)&m1->from)[0]=m->TTmove;m1->score=1000000; // copy TT move into return
m->sorted_moves[0]=*m1; // copy TT move into first sorted slot. Then skip it in all sorts.
m->next_move=m->moves_avalaible=1;
return(1);
}
}
if( m->status<10 && (m->moves_generated&32) ){// initialize the list - all moves have been generated
assert(cell_under_attack(b,b->kp[b->player-1],b->player)); // this should only be called when in check
unsigned int r_h=0,ply2=ply;
if( ply>=2 ) ply2=ply-2;
unsigned short int k1=b->killer[ply][0],k2=b->killer[ply][1]; // killer moves
unsigned short int k1a=b->killer[ply2][0],k2a=b->killer[ply2][1]; // killer moves a
for(i=0;i<m->mc;++i){// init score
if( ((short int*)&m->list[2*i])[0]==m->TTmove ){ // skip TT move - it has already been processed, so exclude it from sorted move list
m->score[i]=1000000;
continue;
}
unsigned char from=m->list[2*i],to=m->list[2*i+1];
if( b->piece[to] || ((b->piece[from]&7)==1 && (to==b->last_move || (to&7)==0 || (to&7)==7)) ){ // capture or promotion or ep capture
int v1=piece_value_search[b->piece[to]&7];
int v2=piece_value_search[b->piece[from]&7];
curr_score=300+(v1<<4)-v2; // most valuable victim/least valuable attacker
}else if( cm==((short int*)&m->list[2*i])[0] )// countermove
curr_score=195;
else if( k1==((short int*)&m->list[2*i])[0] )// killer(s).
curr_score=90;
else if( k2==((short int*)&m->list[2*i])[0] )
curr_score=80;
else if( k1a==((short int*)&m->list[2*i])[0] )
curr_score=70;
else if( k2a==((short int*)&m->list[2*i])[0] )
curr_score=60;
else{// no captures, no killers - use history.
int hh=b->history_count[(b->player-1)*6+(b->piece[from]&7)-1][to];
if( b->slave_index0==0 && abs(hh)>(1<<28) ) r_h=1;// master only. Max bits are 31. Reduce if>28
curr_score=-1000000000+hh;// use history. -1 Bil
}
// insertion sort
m->score[i]=curr_score;
j=m->moves_avalaible;
while( j && curr_score > m->sorted_moves[j-1].score ){ // value to insert doesn't belong where the hole currently is, so shift
m->sorted_moves[j]=m->sorted_moves[j-1]; // shift the larger value down
j--; // move the hole position down
}
m->sorted_moves[j].score=curr_score;
((short int*)&m->sorted_moves[j].from)[0]=((short int*)&m->list[2*i])[0];
m->moves_avalaible++;
} // end of "i" loop
if( r_h ) reduce_history(1,b);
m->status=40;// fully sorted
} // end of "initialize the list"
// pick next best move - captures
if( m->status<20 ){// sort captures. Only for partial move list (empty right now).
// get mask of squares protected by pawns
/*UINT64 bb;
if( b->player==1){// white move, black pawns are protecting
UINT64 a=b->piececolorBB[0][1];
bb=(a<<7)&(a>>9);
}else{// black move, white pawns are protecting
UINT64 a=b->piececolorBB[0][0];
bb=(a<<9)&(a>>7);
}*/
m->moves_generated|=1; // captures generated
m->mc=get_all_moves_new_part1(b,&m->list[0],&m->pinBB); // captures generated
UINT64 one=1;
for(i=0;i<m->mc;++i){
if( ((short int*)&m->list[2*i])[0]==m->TTmove ){ // skip TT move
m->score[i]=1000000;
continue;
}
int v1=piece_value_search[b->piece[m->list[2*i+1]]&7];// victim, times 16
int v2=piece_value_search[b->piece[m->list[2*i]]&7];// attacker
if( !v1 && v2==piece_value_search[1] ){// P moving to empty square
if( ((one<<m->list[2*i+1])&0x8181818181818181) ){// pawn moves to empty cell on rank 1/8 - promotion. Make it Q-P captured by Q
v1=piece_value_search[5]-piece_value_search[1];
v2=piece_value_search[5];
}else if( m->list[2*i+1]==b->last_move)// EP capture: P vs P
v1=piece_value_search[1];
}
assert(v1); // captures should always have a victom
curr_score=3000+300+(v1<<4)-v2; // most valuable victim/least valuable attacker. Here range is 3,700-19,200.
assert(curr_score>=200); // capture should always be >=200
// move losing captures to the end
if( ((m->opp_attacks>>m->list[2*i+1])&1) && v1<v2 ){// only if "to" is attacked by opp. And losing capture.
int s=see_move(b,m->list[2*i],m->list[2*i+1]);
if( s<-99 )// only for material loss, not PST loss
curr_score+=s;// here range is 16,600-2,355
}
// insertion sort
j=m->moves_avalaible;
m->score[i]=curr_score; // so that later i can sort all of them together
while( j && curr_score > m->sorted_moves[j-1].score ){ // value to insert doesn't belong where the hole currently is, so shift
m->sorted_moves[j]=m->sorted_moves[j-1]; // shift the larger value down
j--; // move the hole position down
}
m->sorted_moves[j].score=curr_score;
((short int*)&m->sorted_moves[j].from)[0]=((short int*)&m->list[2*i])[0];
m->moves_avalaible++;
}
m->status=20; // captures have been sorted
}// end of "sort captures"
if( m->status<30 && m->next_move < m->moves_avalaible )// select next capture move
return(return_move(m,m1));
if( m->status<30 ){// sort killers
m->moves_generated|=2; // non-captures generated
i=m->mc;
m->previos_stages_mc=i; //save, so that i can skip them later
m->mc+=get_all_moves_new_part2(b,&m->list[2*i],m->pinBB); // non-captures generated
unsigned int ply2=ply;
if( ply>=2 ) ply2=ply-2;
unsigned short int k1=b->killer[ply][0],k2=b->killer[ply][1]; // killer moves
unsigned short int k1a=b->killer[ply2][0],k2a=b->killer[ply2][1]; // killer moves a
for(;i<m->mc;++i){
if( ((short int*)&m->list[2*i])[0]==m->TTmove ){ // skip TT move
m->score[i]=1000000;
continue;
}
if( cm==((short int*)&m->list[2*i])[0] )// countermove.
curr_score=195;
else if( k1==((short int*)&m->list[2*i])[0] )// killer(s).
curr_score=90;
else if( k2==((short int*)&m->list[2*i])[0] )
curr_score=80;
else if( k1a==((short int*)&m->list[2*i])[0] )
curr_score=70;
else if( k2a==((short int*)&m->list[2*i])[0] )
curr_score=60;
else{
m->score[i]=-1000000000; // so that i can identify these moves later, and process them
continue;
}
// insertion sort
m->score[i]=curr_score;
j=m->moves_avalaible;
while( j && curr_score > m->sorted_moves[j-1].score ){ // value to insert doesn't belong where the hole currently is, so shift
m->sorted_moves[j]=m->sorted_moves[j-1]; // shift the larger value down
j--; // move the hole position down
}
m->sorted_moves[j].score=curr_score;
((short int*)&m->sorted_moves[j].from)[0]=((short int*)&m->list[2*i])[0];
m->moves_avalaible++;
}// end of "i" loop
m->status=30; // killers have been sorted
}// end of "sort killers"
if( m->status<40 && m->next_move < m->moves_avalaible )// select next killer
return(return_move(m,m1));
if( m->status<40 ){// sort quiet moves
// prep check search data structures
UINT64 to_mask[7],from_mask,o;
if( m->MCP_depth1<128 ){
unsigned int kpo=b->kp[2-b->player]; // opp king
to_mask[1]=UINT64(1)<<kpo;
if( b->player==1 ) to_mask[1]=(to_mask[1]>>9)|(to_mask[1]<<7);// white P checking black K: K-9, K+7
else to_mask[1]=(to_mask[1]>>7)|(to_mask[1]<<9);// black P checking white K: K+9, K-7
to_mask[2]=knight_masks[kpo];// N
o=b->colorBB[0]|b->colorBB[1];
to_mask[3]=attacks_bb_B(kpo,o);// B
to_mask[4]=attacks_bb_R(kpo,o);// R
to_mask[5]=to_mask[3]|to_mask[4];// Q
to_mask[6]=0;// K
from_mask=0;
if( bishop_masks[kpo]&(b->piececolorBB[2][b->player-1]|b->piececolorBB[4][b->player-1]) ) from_mask=to_mask[3];
if( rook_masks[kpo]&(b->piececolorBB[3][b->player-1]|b->piececolorBB[4][b->player-1]) ) from_mask|=to_mask[4];
}
for(i=m->previos_stages_mc;i<m->mc;++i){// skip all the previous stages (captures)
if( ((short int*)&m->list[2*i])[0]==m->TTmove || m->score[i]>=0 ) // skip TT move, or capture, or killer
continue;
// no captures, no killers - use history.
unsigned char from=m->list[2*i],to=m->list[2*i+1];
curr_score=-1000000000+b->history_count[(b->player-1)*6+(b->piece[from]&7)-1][to];// use history. -1 Bil
j=m->moves_avalaible;
// MCP cut. This changes node count: late checking moves that were kept and cause cuts will impact history on all prevoius moves. But this removes those moves from the list!
if(
( j>=m->MCP_depth1+10 || ( j && j>=m->MCP_depth1-10 && curr_score<-1000000000 ) ) // +4 ELO vs cs<=0
&& curr_score <= m->sorted_moves[min(j,m->MCP_depth1)-1].score // make sure to use defined score!
&& (
!(to_mask[b->piece[from]&7]&(UINT64(1)<<to)) // no regular checks - estimated (=complete)
&& ( !(from_mask&(UINT64(1)<<from)) || !moving_piece_is_pinned(b,from,to,b->player) ) // no discovered checks - estimated or complete
)
)
continue;
// insertion sort
while( j && curr_score > m->sorted_moves[j-1].score ){ // value to insert doesn't belong where the hole currently is, so shift
m->sorted_moves[j]=m->sorted_moves[j-1]; // shift the larger value down
j--; // move the hole position down
}
m->sorted_moves[j].score=curr_score;
((short int*)&m->sorted_moves[j].from)[0]=((short int*)&m->list[2*i])[0];
m->moves_avalaible++;
}
m->mc=m->moves_avalaible; // update move count
m->status=40; // quiet moves have been sorted
// drop all unneeded moves now
if( m->mc > m->MCP_depth1 ){
for(j=i=m->MCP_depth1;i<m->mc;++i)
if( to_mask[b->piece[m->sorted_moves[i].from]&7]&(UINT64(1)<<m->sorted_moves[i].to) // regular check - estimated (=complete)
|| moving_piece_is_pinned(b,m->sorted_moves[i].from,m->sorted_moves[i].to,b->player) // discovered check - complete
)
m->sorted_moves[j++]=m->sorted_moves[i];
m->mc=j; // new move count
}
if( m->next_move>=m->mc )
return(0); //all moves processed
}// end of "sort quiet moves"
// select next quiet move
return(return_move(m,m1));
}
extern unsigned int MaxCardinality;
#define RET(type,score) return(ret_ms(m_excl_l,b,depth,ply,alp_in,be,node_type,((short int*)&hd.move[0])[0],type,score))
static inline int ret_ms(unsigned char *m_excl_l,board *b, const int depth, const unsigned int ply, int alp_in, int be, unsigned int node_type,short int TTmove,int type,int score){
// write log
/*static FILE *f=NULL;
static UINT64 cc=0;
if( f==NULL ){
f=fopen("c://xde//chess//out//l.csv","w");
fprintf(f,"node_type,cut,exit_type,depth,NMs,cc_notNM,cc_NM,m_excl,nullmove,in_check,stand_pat-be,stand_pat,TTmove_from,TTmove_to,FEN\n");
}
if( depth>7 && depth<11 ){
char sss[100];
print_position(sss,b);
int x;
if( abs(stand_pat-be)<300 ) x=5+10*int((stand_pat-be+10000)/10)-10000;
else if( abs(stand_pat-be)<1000 ) x=50+100*int((stand_pat-be+10000)/100)-10000;
else x=250+500*int((stand_pat-be+10000)/500)-10000;
fprintf(f,"%d,%d,%d,%d,%d,%u,%u,%d,%d,%d,%d,%d,%d,%d,%s",node_type,(score>=be?1:0),type,depth,NMs,b->node_count-nc1,nc1-nc0,(((short int*)&m_excl_l[0])[0]?1:0),b->nullmove,in_check,x,stand_pat,(TTmove&255),(TTmove>>8),sss );
cc++;
if( cc>66000 ){
fclose(f);
exit(0);
}
}*/
return(score);
}
#define bbp(bit) if( bit ) b=b_s+bit-1;else b=&b_m;
#if SLOG
typedef struct{
int cc;
char ss[100];
} bbd;
static void beta_break_threads(unsigned int sp_index,bbd* d){
#else
static void beta_break_threads(unsigned int sp_index){
#endif
board *b;
UINT64 a=sp_all[sp_index].slave_bits&~(UINT64(1)<<sp_all[sp_index].sp_created_by_thread); // list of all threads working on this SP
unsigned long bit=sp_all[sp_index].sp_created_by_thread;
unsigned int i;
a&=~(UINT64(1)<<bit); // excluding the owner
sp_all[sp_index].c_0=0; // no more moves left at this SP
InterlockedAnd64((LONG64*)&sp_open_mask,~(UINT64(1)<<sp_index));// count this as closed SP
while( a ){// here "bit" is slave thread index.
GET_BIT(a)
bbp(bit);b->em_break+=2; // beta break this thread
#if SLOG
d->ss[d->cc++]=char('A'+bit);
#endif
for(i=0;i<b->sps_created_num;++i) // and all its children
#if SLOG
beta_break_threads(b->sps_created[i],d);
#else
beta_break_threads(b->sps_created[i]);
#endif
}
}
#if SLOG
int f_timer2(void);
extern FILE *f_slog;
extern int t0;
extern int c_s;
unsigned int id0=0; // SP counter
#endif
#define LOG_SEARCH 0
#if LOG_SEARCH
FILE *fls;
#endif
extern unsigned int cc[15][2010];
extern short int adj[];
static const float ln[64]={0.693147181f,0.693147181f,0.693147181f,1.098612289f,1.386294361f,1.609437912f,1.791759469f,1.945910149f,2.079441542f,2.197224577f,2.302585093f,2.397895273f,2.48490665f,2.564949357f,2.63905733f,2.708050201f,2.772588722f,2.833213344f,2.890371758f,2.944438979f,2.995732274f,3.044522438f,3.091042453f,3.135494216f,3.17805383f,3.218875825f,3.258096538f,3.295836866f,3.33220451f,3.36729583f,3.401197382f,3.433987204f,3.465735903f,3.496507561f,3.526360525f,3.555348061f,3.583518938f,3.610917913f,3.63758616f,3.663561646f,3.688879454f,3.713572067f,3.737669618f,3.761200116f,3.784189634f,3.80666249f,3.828641396f,3.850147602f,3.871201011f,3.891820298f,3.912023005f,3.931825633f,3.951243719f,3.970291914f,3.988984047f,4.007333185f,4.025351691f,4.043051268f,4.060443011f,4.077537444f,4.094344562f,4.110873864f,4.127134385f,4.143134726f};
int Msearch(board *b, const int depth, const unsigned int ply, int alp_in, int be, unsigned int node_type){// main search function. Node type: 1=PV, 2=ALL, 3=CUT
split_point_type *spl;
hash_data hd;
UINT64 KBB;
unmake d;
move_list ml;
best_m *bm,bm_l;
move mo;
unsigned int i,j,sp_index;
int split_point,in_check,stand_pat,ext_ch,new_depth,score,s0,SEE_limit,futility_limit;
short unsigned int cm;
unsigned char player,kp0,m_excl_l[2];
#if ENGINE==0
assert(!player_is_in_check(b,3-b->player)); // make sure there is no king capture.
#endif
assert(ply<128);// check that ply is good.
if( ply>b->max_ply ) b->max_ply=ply; // if this writes to shared memory every time, multi-threaded process gets delayed a lot!
assert(b->max_ply<128);
if( b->slave_index ){// called by slave from a split-point - skip most top logic
assert(b->slave_index0<100);
split_point=1; // this is a split-point - slave thread.
spl=(split_point_type *)b->spp; // pointer to the split-point object
sp_index=spl->sp_index;
b->slave_index=0; // reset slave index
// copy some items from sp to locals
in_check=spl->in_check;
ext_ch=spl->ext_ch;
SEE_limit=futility_limit=0; // invalid
// calc some items
player=b->player;
kp0=b->kp[player-1];
bm=&spl->bm;// use SP version
m_excl_l[0]=m_excl_l[1]=0;
ml.opp_attacks=spl->mlp->opp_attacks; // save it locally
goto top_of_the_move_loop;
}
// master code*******************************************
if( ((short int*)b->move_exclude)[0] ){
((short int*)m_excl_l)[0]=((short int*)b->move_exclude)[0]; // save excluded move locally and reset it
((short int*)b->move_exclude)[0]=0;
split_point=0;
}else
((short int*)m_excl_l)[0]=0;
if( ply<MAX_MOVE_HIST ){
((short int*)&b->move_hist[ply][ply][0])[0]=0; // invalid PV terminator
if( ply<MAX_MOVE_HIST-1 ) ((short int*)&b->move_hist[ply][ply+1][0])[0]=0; // terminator
}
// check for board repetition and 50 move draw, before calling Q search.
if( b->halfmoveclock>=4 && ply ){// For repeaters, halfmoveclock 4 or more (repeats 0 and 4=current - Pmove,Omove,Punmove,Ounmove). Do not stop ply=0 - that produces no valid move.
// 50 move draw
if( b->halfmoveclock>=100 ){
if( checkmate(b)==2 )// checkmate. Return of 1 means stalemate - do not count it here!
RET(1,ply-10000); // i have been mated - low (negative) score. 100-ply.
else
//RET(2,0);// 50 move rule - draw.
RET(3,1-2*(ply&1));// Give it +1 cp for top level player to pick 50 move draws over waisting time.
}
// loop over previous positions from this search and earlier game
UINT64 n1=b->hash_key,n2=n1^player_zorb;
for(j=max(96+ply,99);j>ply+99-b->halfmoveclock && b->position_hist[j];--j){// last: 99+ply. Total: halfmoveclock. Also stop on first empty.
if( b->position_hist[j]==n1 || b->position_hist[j]==n2 )// found a match - return.
RET(3,1-2*(ply&1));// Give it +1 cp for top level player to pick repetition draws over waisting time.
}
}
#if USE_EGTB
// probe syzygy EG tablebases: before Qs, but after repetition
if( pc_root<=EGTBProbeLimit && pc_root<=MaxCardinality && ply ){
score=checkmate(b);
if( score==2 ) RET(4,ply-10000);// checkmate - return
else if( score==1 ) RET(5,0);// stalemate - return
score=probe_dtz(b,&new_depth);
if( new_depth) tbhits++; // record a hit
if( new_depth && score==0 ) RET(6,0);// always use draw score "as is"
if( new_depth && abs(score)<=100 ){// good DTZ access
if( abs(score)+b->halfmoveclock>100 ) RET(7,0);// draw by 50 move rule - return
if( score>0 ) RET(8,9000-ply-(b->halfmoveclock>0?score:0)); // zero out score if move was zeroing
else RET(9,-9000+ply-(b->halfmoveclock>0?score:0)); // zero out score if move was zeroing
}
}
#endif
// mate-distance pruning
alp_in=max(alp_in,-10000+(int)ply);
be=min(be,10000-1-(int)ply);
if( alp_in>=be ) RET(10,alp_in);
// put current position (clean hash) on history list, for repetition draw analysis
b->position_hist[ply+100]=b->hash_key;// record current position on the list of repeaters
// call Q search on last move*********************************************************
ext_ch=b->pl[ply].ch_ext; // extend if getting out of check and correct SEE
if( depth+ext_ch<=0 ) RET(11,Qsearch(b,ply,alp_in,be,node_type));
// look in the TT.
((short int*)hd.move)[0]=0;// mark as invalid move.
if( !((short int*)m_excl_l)[0] && lookup_hash(depth,b,&hd,ply) && node_type>1 ){// hash found - use the score. No cuts on PV.
if( hd.alp>=be ){
if( ply<MAX_MOVE_HIST ) ((short int*)&b->move_hist[ply][ply][0])[0]=((short int*)hd.move)[0]; // record current move.
RET(12,hd.alp); // fail high
}else if( hd.be<=alp_in ){
if( ply<MAX_MOVE_HIST ) ((short int*)&b->move_hist[ply][ply][0])[0]=((short int*)hd.move)[0]; // record current move.
RET(13,hd.be); // fail low
}
alp_in=max(alp_in,hd.alp);
be=min(be,hd.be);
assert(alp_in<be);
}
#if USE_EGTB
// use syzygy EG bitbases
if( UseEGTBInsideSearch // i'm alowed to use them
&& ply // skip ply 0 - need valid move
&& (i=(unsigned int)popcnt64l(b->colorBB[0]|b->colorBB[1]))<=min(EGTBProbeLimit,MaxCardinality) // piece count is in the EGBB range
){
// Assume good results (win with +8 material advantage) cannot get better. This reduces disc reads greatly, without impacting results
if( node_type==2 && alp_in>=7300 ) RET(14,alp_in);
if( node_type==3 && be<=-7300 ) RET(15,be);
// Do the EGBB probe
score=probe_wdl(b,&new_depth);
if( new_depth ){
tbhits++; // record a hit
int s_r=0;
if( score==-1 || score==1 ) score=0; // call these draws.
#if calc_pst==1
if( score>0 ) s_r=get_scoree(b)+6500-ply;
else if( score<0 ) s_r=get_scoree(b)-6500+ply;
#else
if( score>0 ) s_r=b->scoree+6500-ply;
else if( score<0 ) s_r=b->scoree-6500+ply;
#endif
RET(16,s_r);
}
}
#endif
// See if i'm in check.
in_check=cell_under_attack(b,b->kp[b->player-1],b->player); // from this point on in_check is defined.
// futility, for d=1-6: if Qscore>be+margin, skip. This stops "previous move is bad" situations.
if(
!((short int*)&m_excl_l[0])[0] // +6
&& depth<=6 // here 5 is -11 ELO, 4 is -12 ELO
&& node_type>1 // +9
&& !in_check // +18
&& abs(be)<3000 // a wash
){
s0=futility_margin[depth];
if( Qsearch(b,ply,be+s0-1,be+s0,4)>=be+s0 ) // null-window around be+s0. This score is never below stand pat. Comparing stand_pat to bounds is done inside this, so no need to do it explicitely.
RET(17,be); // fail-soft is a wash
// razoring: if Qscore<alp-margin, skip. +1 ELO
stand_pat=eval(b);// static eval - after futility
if( depth<4 && !((short int*)&hd.move[0])[0] ){// d=1,2,3. Here excluding TT moves is +1 ELO.
s0=365; // around 5% below -365
if( stand_pat<=alp_in-s0 && Qsearch(b,ply,alp_in-s0,alp_in-s0+1,5)<=alp_in-s0 )// null-window around alp-s0. This score is never below stand pat.
RET(18,alp_in);
}
if( b->em_break ) RET(0,alp_in);// timeout or beta cut-off break: return
}else stand_pat=eval(b);// static eval - after futility
// null-move pruning.
if( depth>=2 // d=2+: +10 ELO vs >=3, +5 ELO vs >=1
&& !((short int*)m_excl_l)[0] // not singular ext: +4 ELO
&& node_type>1 // not PV node: +1 ELO
&& !in_check // not in check: +10 ELO
&& !b->nullmove // not second NM in a row: +3 ELO
&& abs(be)<3000 // not mate score: +5 ELO
&& (b->piececolorBB[2][b->player-1]|b->piececolorBB[3][b->player-1]|b->piececolorBB[4][b->player-1]) // palyer has>0 sliders: +1 ELO
&& stand_pat>=be // PST>=be: +12 ELO
){
unsigned int R=2;
if( depth>7 && stand_pat>=be+100 ) R++; // increase R to 3 for d=8+ and good (100+) score. +6 ELO
unsigned char lm_l=b->last_move,hm_l=b->halfmoveclock;// save
if( b->last_move!=INVALID_LAST_MOVE ){
b->hash_key^=last_move_hash_adj;
b->last_move=INVALID_LAST_MOVE; // illegal last move
}
make_null_move(b);b->nullmove=1;
b->halfmoveclock=0;// reset, to avoid repeated positions after 2 moves. Need this. Otherwise opp can always force a draw.
b->pl[ply+1].ch_ext=0; //no check extension on next move after NM
b->pl[ply+1].cum_cap_val=-b->pl[ply].cum_cap_val;// cumulative value of pieces captured. Excludes pawns. Used for recaptue extension.
b->pl[ply+1].to_square=64; // invalid
score=-Msearch(b,depth-1-R,ply+1,-be,-be+1,tto[node_type]);// Cut search d by R. Zero window. Node type is opposite to current. ply+1.
b->nullmove=0;unmake_null_move(b);
b->last_move=lm_l;b->halfmoveclock=hm_l;// restore
if( b->last_move!=INVALID_LAST_MOVE ) b->hash_key^=last_move_hash_adj;
if( b->em_break ) RET(0,alp_in);// timeout or beta cut-off break: return
if( score>=be ){// here storing result in TT is -2 ELO
if( depth>=10 ){// +6 ELO
b->nullmove=1; // +6 ELO. Why?
s0=Msearch(b,depth-1-R,ply,alp_in,be,node_type);// reduce by R+1.
b->nullmove=0;
if( b->em_break ) RET(0,alp_in);// timeout or beta cut-off break: return
if( s0>=be )
RET(19,score);
}else
RET(20,score);// fail-soft. +4 ELO vs returning beta. Returning be for ALL nodes does not help.
}
}// end of NM
b->nullmove=0; // reset
// check for time out. Both master and slaves get stopped here.
if( depth>3 && depth>timer_depth ){
int now=get_time();
if( now>timeout ){
// set break for all slaves and master.
b_m.em_break=1;
for(i=0;i<(unsigned int)slave_count;++i) b_s[i].em_break=1;
timer_depth=3; // reset
RET(21,MIN_SCORE);
}
now=timeout-now;
if( now<=30 ){ if( timer_depth!=3 ) timer_depth=3; // only write it to memory if it changed
}else if( now<120 ){ if( timer_depth!=5 ) timer_depth=5;
}else if( now<350 ){ if( timer_depth!=7 ) timer_depth=7;
}else if( now<1000 ){ if( timer_depth!=9 ) timer_depth=9;
}else{ if( timer_depth!=11 ) timer_depth=11;}
}
// init for first call to search
if( ply==0 ){// top level search
#if LOG_SEARCH
if( fls==NULL ) fls=fopen("c://xde//chess//out//search_log.csv","w");
char sss[100];print_position(sss,b);
fprintf(fls,",depth,%d,FEN,%s",depth,sss);
#endif
InterlockedAnd(&sp_open_mask,0); // init
b->pl[0].ch_ext=b->pl[0].cum_cap_val=0; // reset starting pl values
for(i=0;i<(unsigned int)slave_count;++i) b_s[i].em_break=0;
b_m.em_break=0;
timer_depth=3; // init to something small
b->sp_level=0; // reset sp level
b->sps_created_num=0;
if( depth0==1 || root_hash!=b->hash_key ){// new position.
reduce_history(4,b); // this is +5 ELO vs resetting history and killers.
b->pl[0].to_square=64; // init to invalid
}else// same position, searched to deeper d - reduce by 3
reduce_history(3,b);// here 2 is -1 ELO
g_promotion=0; //init
root_hash=b->hash_key;
pc_root=(unsigned int)popcnt64l(b->colorBB[0]|b->colorBB[1]);// save root position piece count
}
// Get move list. Here i always have do use "get out of check" logic! This executes 0.073 times per node
if( in_check ){
ml.mc=get_out_of_check_moves(b,&ml.list[0],b->kp[b->player-1],in_check-64);
ml.moves_generated=32; // all moves have been generated
}else
ml.mc=ml.moves_generated=0; // moves have NOT been generated. Used for all positions not in check.
player=b->player;
kp0=b->kp[player-1];
// see if TT move is legit. I get bad TT move around 2 times an hour per core, including second part, so i need it too!
if( ((short int*)&hd.move[0])[0] && (ml.moves_generated&32) ){// have move list - look at it
unsigned int reset=30;
for(i=0;i<ml.mc;++i)// insert TT move in the first slot.
if( ((short int*)&hd.move[0])[0]==((short int*)&ml.list[2*i])[0] ){// TT move found
reset=0;
break;
}
if( reset )
((short int*)&hd.move[0])[0]=0;
}else if( ((short int*)&hd.move[0])[0] ){
unsigned int reset=0;
if( (b->piece[hd.move[0]]>>6)!=player || (b->piece[hd.move[1]]>>6)==player ) // wrong player moving, or trying to capture my own piece
reset=1;
else if( (b->piece[hd.move[1]]&7)==6 ) // king capture
reset=2;
else if( dir_norm[kp0][hd.move[0]] && moving_piece_is_pinned(b,hd.move[0],hd.move[1],3-player) ) // move of pinned piece
reset=3;
else{
switch(b->piece[hd.move[0]]&7){
case 1: if( player==1 ){// white
if( !(
(hd.move[1]==hd.move[0]+1 && b->piece[hd.move[1]]==0 )
|| ((hd.move[0]&7)==1 && hd.move[1]==hd.move[0]+2 && b->piece[hd.move[1]]==0 && b->piece[hd.move[0]+1]==0 )
|| (hd.move[1]==hd.move[0]+9 && (b->piece[hd.move[1]] || hd.move[1]==b->last_move) )
|| (hd.move[1]==hd.move[0]-7 && (b->piece[hd.move[1]] || hd.move[1]==b->last_move) )
)
)
reset=10;
}else{// black
if( !(
(hd.move[1]==hd.move[0]-1 && b->piece[hd.move[1]]==0 )
|| ((hd.move[0]&7)==6 && hd.move[1]==hd.move[0]-2 && b->piece[hd.move[1]]==0 && b->piece[hd.move[0]-1]==0 )
|| (hd.move[1]==hd.move[0]-9 && (b->piece[hd.move[1]] || hd.move[1]==b->last_move) )
|| (hd.move[1]==hd.move[0]+7 && (b->piece[hd.move[1]] || hd.move[1]==b->last_move) )
)
)
reset=10;
}
break;
case 2: if( !(knight_masks[hd.move[0]]&(UINT64(1)<<hd.move[1])) ) reset=11; // Complete
break;
case 3: if( dir_norm[hd.move[0]][hd.move[1]]!=7 && dir_norm[hd.move[0]][hd.move[1]]!=9 ) reset=12; // drop if not on diagonal. Incomplete
else if( ray_segment[hd.move[0]][hd.move[1]]&(b->colorBB[0]|b->colorBB[1]) ) reset=13; // drop if someting is in the way. Complete.
break;
case 4: if( dir_norm[hd.move[0]][hd.move[1]]!=1 && dir_norm[hd.move[0]][hd.move[1]]!=8 ) reset=14; // drop if not on a line. Incomplete
else if( ray_segment[hd.move[0]][hd.move[1]]&(b->colorBB[0]|b->colorBB[1]) ) reset=15; // drop if someting is in the way. Complete.
break;
case 5: if( dir_norm[hd.move[0]][hd.move[1]]==0 ) reset=16; // drop if not on a line or diagonal. Incomplete
else if( ray_segment[hd.move[0]][hd.move[1]]&(b->colorBB[0]|b->colorBB[1]) ) reset=17; // drop if someting is in the way. Complete.
break;
case 6:if( dist[hd.move[0]][hd.move[1]]!=1 ) reset=18; // disallow castling - too complicated to check all the conditions. Otherwise, complete
else if( dist[hd.move[1]][b->kp[2-player]]==1 ) reset=19; // make sure kings do not touch!
else if( (pawn_attacks[2-player][hd.move[1]]&b->piececolorBB[0][2-player]) ) reset=20; // make sure king is not attacked by opp pawns
else{// See if i'm in check after the move
b->colorBB[player-1]^=UINT64(1)<<kp0; // update occupied BB of player. here i only need to remove the king from its current position on colorBB board.
if( player_moved_into_check(b,hd.move[1],player)) reset=21;
b->colorBB[player-1]^=UINT64(1)<<kp0; // update occupied BB of player.
}
break;
}
}
if( !reset && in_check ){// make sure i am out of check after TT move
i=kp0;
if( i==hd.move[0] ) i=hd.move[1];
d.promotion=0;//init to Q
make_move(b,hd.move[0],hd.move[1],&d);
if( cell_under_attack(b,i,player) ) reset=4;
unmake_move(b,&d);
}
if( reset )
((short int*)hd.move)[0]=0;
}
ml.TTmove=((short int*)hd.move)[0]; // put TT move into move list object
// singular extension
if( depth>6 // d>X
&& ply // skip top position
&& ((short int*)&hd.move[0])[0] // have hash move: 93%
&& hd.depth>=depth/2 // hash position is deep enough: 91%
&& hd.bound_type<2 // TT contains low or exact score
&& !ext_ch // not extended already: 98%
&& !in_check // not in check: 93%
&& abs(hd.tt_score)<1000 // score is reasonable: 100%. Cum=45%
&& !((short int*)m_excl_l)[0] // not in SE search
&& ply+depth<115 // limit extreme depths
){
*((short int*)b->move_exclude)=((short int*)hd.move)[0]; // set move to exclude
int be1=hd.tt_score-(50+(depth-10)*6);
score=Msearch(b,depth/2,ply,be1-1,be1,node_type); // call search**********************************************************************
if( b->em_break ) RET(0,alp_in);// timeout or beta cut-off break: return
if( score<=be1-1 ) ext_ch=2; // fail low - extend. Happens 24% for margin of 25. 18% for margin of 50.
}
// shallow cut
spl=NULL; // not a split-point
sp_index=split_point=0; // this is not a split-point - master start. Change it later if needed.
SEE_limit=futility_limit=ml.moves_avalaible=ml.status=ml.next_move=0; // init
ml.MCP_depth1=128;
if( node_type>1 && !in_check && depth<10 ){
if( depth<=6 ) SEE_limit=1; // SEE cuts for depth 6- only. This is +12 ELO vs 9-. Effect is about the same for 3-6. Cuts over 6 hurt because sometimes opp piece that captures me was protecting some other opp piece, which i can now capture=SEE is incomplete.
ml.MCP_depth1=MCP[depth];
if( popcnt64l(b->colorBB[2-b->player]^b->piececolorBB[0][2-b->player])>1+1 ) futility_limit=1;// Opp has more than 1 piece (+king).
}
bm_l.alp=alp_in; // save input alpha
((short int*)bm_l.best_move)[0]=0; // this impacts count. Why?
bm_l.best_score=MIN_SCORE; // to avoid extreme results when all moves are cut (it happens)
bm_l.legal_moves=0;
bm=&bm_l;// use local version
top_of_the_move_loop:// end of top logic, excluded by slave ********************************************************************************************************************************
KBB=UINT64(1)<<kp0;
i=-1; // decrement here, since it is incremented at the top of the loop
ml.opp_attacks=get_opp_attacks(b);// get a list of all potential attacks by opponent - for SEE logic. Do this always, for recapture
if( b->pl[ply].to_square<64 ) cm=b->countermove[(2-player)*6+(b->piece[b->pl[ply].to_square]&7)-1][b->pl[ply].to_square]; // only if valid
else cm=0;
while( 1 ){// loop over all moves***********************************************************************************
i++; // this is the only place to do this
// create a split-point?
if( depth>=MIN_SLAVE_DEPTH-1 && Threads>1 && !split_point // there are slaves available. Not a split-point already
&& ( (node_type==1 && i>0) || node_type==2 || (node_type==3 && i>3) ) // PV after first move, or ALL node. Or CUT node after 3 moves.
&& ( ml.moves_generated<2 || ml.mc>5+i ) // number of remaining moves is >5
&& !((short int*)m_excl_l)[0] // not in SE search
&& depth>=MIN_SLAVE_DEPTH-(popcnt64l(sp_open_mask)<=2?1:0)+(popcnt64l(sp_open_mask)>min(2*Threads+4,MAX_SP_NUM-20)?3:0)+(popcnt64l(sp_open_mask)>min(3*Threads+6,MAX_SP_NUM-10)?3:0) // depth>min
&& popcnt64l(sp_all_mask)<MAX_SP_NUM // there are available split-points
&& b->sps_created_num<SPS_CREATED_NUM_MAX
){ // save some current info into split-point
if( !get_next_move(b,ply,&ml,&mo,cm,depth,node_type) ) // simple logic - just take next move.
break;
int c1=int(popcnt64l(sp_open_mask)); // i have so many open
c1-=(Threads-int(popcnt64l(thread_running_mask))); // i need so many for full utilization
// now c1 is excess capacity
int dd=min(int(depth0)-3,MIN_SLAVE_DEPTH+max(-1,(c1-2)/2)); // 1/2 reduction in depth
int i_min=0;
unsigned int node_type_max=3,node_type_min=1;
if( c1>0 ){ // have at least 0 open* SPs - now only open new SP on ALL node if i>0
i_min=1;
node_type_max=2; // excl 3
node_type_min=2; // excl 1
dd=max(dd,MIN_SLAVE_DEPTH+1);
}
if( depth>=dd && int(i)>=i_min ){
AcquireSRWLockExclusive(&L1); // lock the split-point
if( !b->em_break && popcnt64l(sp_all_mask)<MAX_SP_NUM && node_type<=node_type_max && node_type>=node_type_min ){// check again - this does come into play! And do not create SP if in break.
// select next available sp data structure.
DWORD bit;
BSF64l(&bit,~sp_all_mask);
assert(bit<MAX_SP_NUM);
sp_index=bit;
spl=sp_all+sp_index;
b->sps_created[b->sps_created_num++]=sp_index; // record and increment
// save some curent info into spl
spl->in_check=in_check;
spl->ext_ch=ext_ch;
spl->mlp=&ml;
b->sp_level++; // increase sp level. Do it before board is copied to spl.
size_t sizeofboard=sizeof(board)-sizeof(b->move_hist)-100*sizeof(play)-sizeof(b->slave_index)-sizeof(b->slave_index0);// this copies history and countermoves
memcpy(&(spl->b),b,sizeofboard);
spl->be=be;
spl->bm=bm_l; // copy all local BM into SP
if( ply<MAX_MOVE_HIST ) for(j=ply;j<MAX_MOVE_HIST;++j){spl->move_hist[ply][j][0]=b->move_hist[ply][j][0];spl->move_hist[ply][j][1]=b->move_hist[ply][j][1];}// copy move history into SP
bm=&spl->bm; // use SP version of bm
spl->depth=depth;
spl->ply=ply;
spl->node_type=node_type;
spl->c_0=1; // calc remaining moves
InterlockedOr64((LONG64*)&sp_open_mask,(UINT64(1)<<sp_index));// count this as open SP
spl->c_1=0; // no slaves here so far
spl->sp_index=sp_index;
spl->master_sleeping=0; // master is not sleeping
sp_all_mask=sp_all_mask|(UINT64(1)<<sp_index); // mark this split point "used"
spl->slave_bits=(UINT64(1)<<b->slave_index0); // mark it as being worked on by "master"
spl->beta_break=0; // mark this SP as "unbroken
spl->sp_created_by_thread=b->slave_index0;
// log the activity
#if SLOG
spl->id=id0++;
if( depth0>=SLOG ){
if( t0==0 ){
t0=f_timer2();
f_slog=fopen(SLOG_FILE1,"w");
fprintf(f_slog,"id,time,threads running,sp_all,sp_open,cat,SP,threadID,c_1,depth,depth0,ply,node_type,i,sp_level,elapsed,beta_cut,breaks\n");
}
spl->t1=f_timer2();
spl->i0=i;
if( (SLOG_MASK&SLOG_CREATE) ){
fprintf(f_slog,"%d,%d,%d,%d,%d,",spl->id,spl->t1-t0,int(popcnt64l(thread_running_mask)),int(popcnt64l(sp_all_mask)),int(popcnt64l(sp_open_mask)));
fprintf(f_slog,"creating,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
sp_index,b->slave_index0,0,depth,depth0,ply,node_type,i,b->sp_level);
c_s++;
}
if( c_s>65500 ){
fclose(f_slog);
exit(777);
}
}
#endif
ReleaseSRWLockExclusive(&L1); // release the split-point. It is usually better to release the lock before waking other threads to reduce the number of context switches.
if( popcnt64l(thread_running_mask)<Threads ) WakeAllConditionVariable(&CV1); // start the slaves, only if there are idle slave slots.
split_point=2; // now this is a split-point - "master" thread
}else
ReleaseSRWLockExclusive(&L1); // release the split-point.
}
}
// select the move*********************************************
else if( split_point ){
spl->lock.acquire(); // lock the split-point. This takes some time with many threads.
// get next available move.
assert(spl->mlp);
if( spl->c_0==0 || spl->bm.alp>=be || !get_next_move(b,ply,spl->mlp,&mo,cm,depth,node_type) ){// end of move list or beta cut-off - break. No need to pass best move/score here - it is always passed in alpha logic.
if( spl->c_0 ){
spl->c_0=0; // no more moves
InterlockedAnd64((LONG64*)&sp_open_mask,~(UINT64(1)<<sp_index));// count this as closed SP
}
spl->lock.release(); // release the split-point
break; // break out of the move loop
}
i=spl->mlp->next_move-1; // i is used to adjust history on cuts
spl->lock.release(); // release the split-point
}else if( !get_next_move(b,ply,&ml,&mo,cm,depth,node_type) ) // simple logic for single threaded search - just take next move
break;
if( ((short int*)m_excl_l)[0]==((short int*)&mo.from)[0] ) // skip excluded move
continue;
// pass current move to GUI
#if ENGINE
if( ply==0 && depth>14 && !b->slave_index && get_time()-time_start>3000 ){// at least 3 secs and depth 15
char sss[200];
sprintf(sss,"info depth %u currmove %c%c%c%c currmovenumber %u\n",depth,mo.from/8+'a',mo.from%8+'1',mo.to/8+'a',mo.to%8+'1',i+1);
pass_message_to_GUI(sss);
}
#endif
// See if i'm in check after the move
if( mo.from==kp0 ){
b->colorBB[player-1]^=KBB; // update occupied BB of player. here i only need to remove the king from its current position on colorBB board.
j=player_moved_into_check(b,mo.to,player);
b->colorBB[player-1]^=KBB; // update occupied BB of player.
if( j ) continue;
}
// skip moves with low SEE=+11 ELO. This does very little: all those bad moves get cut immediately by futility logic. Max speed-up is around 10%, for cutoff of -200.
else if( // not a king move - king can never be captured
SEE_limit
&& ((ml.opp_attacks>>mo.to)&1) // "to" is attacked by opponent
&& abs(piece_square[(b->piece[mo.from]&7)-1][player-1][mo.from][1])>200 // moving piece is valuable enough to miss it
&& piece_value_search[b->piece[mo.to]&7]<piece_value_search[b->piece[mo.from]&7] // bad capture
&& !moving_piece_is_pinned(b,mo.from,mo.to,player) // the move does not give discovered check. +3 ELO vs excluding all checks
&& see_move(b,mo.from,mo.to)<-200 // call SEE. Returns score for current move.
){
if( !bm->legal_moves ) bm->legal_moves=1; // count legal move.
continue;
}
// futility cuts. // FU is only +5 ELO
unsigned int mgc=move_gives_check(b,mo.from,mo.to);
if(
futility_limit // not PV, not in check, low depth
&& mo.score<100 // skip captures/TT. Not excluding killers is a wash.
&& !mgc // exclude checking moves. This is +5 ELO vs only excluding discovered checks. +11 ELO vs not excluding any checks
){
int LMR1=int(ln[depth]*ln[min(63,i)]*lmr_m[1]+lmr_a[1]);// ln+ln tabulated: baseline
new_depth=max(0,depth-LMR1);
if( new_depth<6 ){
j=(((b->piece[mo.from]&7)-1)<<1)+b->player-1; // index
int sm=piece_square[0][j][mo.to][0]-piece_square[0][j][mo.from][0]; // mid
int se=piece_square[0][j][mo.to][1]-piece_square[0][j][mo.from][1]; // end
sm+=(((se-sm)*endgame_weight_all_i[b->piece_value])>>10); // blended
if( sm<alp_in-FU[new_depth-1]-stand_pat-40 ){
if( !bm->legal_moves ) bm->legal_moves=1; // count legal move.
continue;
}
}
}
// check extension - assume promo to Q
if( mgc && see_move(b,mo.from,mo.to)>-200 )// only if losing less then 2 pawns
b->pl[ply+1].ch_ext=1;
else
b->pl[ply+1].ch_ext=0;
d.promotion=0;//init to Q
do{// beginning of the promotion loop
if( d.promotion ) b->pl[ply+1].ch_ext=0; // no check ext after under-promo
assert(b->piece[mo.from]); // piece to move is not empty
make_move(b,mo.from,mo.to,&d); // make the move. This executes 0.38 times per node.***
assert(get_pawn_hash_key(b)==b->pawn_hash_key); // make sure pawn hash key is still good
assert(get_mat_key(b)==b->mat_key); // make sure material key is still good
assert(get_TT_hash_key(b)==b->hash_key); // make sure TT hash key is still good
assert(bitboards_are_good(b)); // bitboards are good
assert(get_piece_value(b)==b->piece_value); // make sure total piece value is still good
// Extensions
new_depth=depth-1;
s0=pv10[d.w&7]; // value of current piece capture
b->pl[ply+1].cum_cap_val=s0-b->pl[ply].cum_cap_val; // cumulative value of pieces captured. Excludes pawns. Used for recaptue extension.
b->pl[ply+1].to_square=mo.to; // move - to
b->pl[ply+1].stand_pat=(short int)stand_pat;
#if NDEBUG
#else // for debugging, record stuff
b->pl[ply+1].cap_val=s0;
b->pl[ply+1].from=mo.from;
b->pl[ply+1].to=mo.to;
b->pl[ply+1].move_type=d.move_type;
#endif
#if LOG_SEARCH
for(unsigned int lc1=1;lc1<=ply+1;++lc1)
fprintf(fls,"move %d=%d/%d/%d,",lc1,b->pl[lc1].from,b->pl[lc1].to,b->pl[lc1].move_type);
fprintf(fls,"\n");
#endif
if( (
ext_ch // Check extension. Exclude checks losing more than something.
|| ( s0 // capture of piece: +5 ELO vs excluding Q
&& ply // not on the first move
&& ((ml.opp_attacks>>mo.to)&1)==0 // "to" is not attacked by opponent, so clean recapture only
&& abs(b->pl[ply].cum_cap_val-b->pl[ply-1].cum_cap_val)==s0 // prev move was capture with the same value
) // recapture ext is +12 ELO
) && ply+depth<115 // limit extreme depths
)
new_depth++;
//LMR logic
int LMR=0;
if( depth>3 // 2 vs 3 is -0 ELO. 4 vs 3 is -6 ELO. 5 vs 3 is -15 ELO.
&& i>0 // skip first move. For PV and ALL nodes it would get LMR=0 anyway.
&& piece_value_search[b->piece[mo.to]&7]>piece_value_search[d.w&7]// skip good captures: +2 ELO
&& (mo.score<=70 || mo.score>=100 ) // skip killers: +10 ELO. Here including scores of +60 and +70 is +1 ELO. Excluding cm=195 is -6 ELO. 2/2017
&& !in_check // do not cut check evasions: +2 ELO. 3/2017