From c0646c52f943c3c520a8557e6853608f8f676219 Mon Sep 17 00:00:00 2001 From: Jay Honnold Date: Fri, 6 Oct 2023 09:16:26 -0700 Subject: [PATCH] 12 bit eval range with 20 bit move (#514) Bench: 3680773 This patch does three things: Removes all endgame related code. It didn't really do anything anyways. Clamps the static eval range to [-2048, 2047] (12 bits) Re-scheme the moves to fit into 20 bits. This change had no impact on the engine. Combining these three patches, I'm able to store the move and static eval in the TT in a singular int32_t, allowing a 10 byte TT entry. This can be used with a 2 byte padding with 3 entries to create a 32 byte bucket, increasing the # of TT entries by 50%. High Hash Pressure Tests ELO | 4.88 +- 3.30 (95%) SPRT | 6.0+0.06s Threads=1 Hash=2MB LLR | 2.95 (-2.94, 2.94) [0.00, 3.00] GAMES | N: 20928 W: 5314 L: 5020 D: 10594 http://chess.grantnet.us/test/33940/ ELO | 6.47 +- 3.88 (95%) SPRT | 30.0+0.30s Threads=1 Hash=8MB LLR | 2.97 (-2.94, 2.94) [0.00, 3.00] GAMES | N: 14552 W: 3580 L: 3309 D: 7663 http://chess.grantnet.us/test/33943/ ELO | 10.21 +- 5.11 (95%) SPRT | 4.0+0.04s Threads=8 Hash=8MB LLR | 2.96 (-2.94, 2.94) [0.00, 3.00] GAMES | N: 8650 W: 2241 L: 1987 D: 4422 http://chess.grantnet.us/test/33945/ No-Adjudication Verification Elo | 1.66 +- 2.82 (95%) SPRT | 8.0+0.08s Threads=1 Hash=8MB LLR | 2.94 (-2.25, 2.89) [-2.50, 0.50] Games | N: 27912 W: 6751 L: 6618 D: 14543 Penta | [106, 3140, 7336, 3263, 111] --- src/bitbases/KBPvK.bb | Bin 196608 -> 0 bytes src/bitbases/KPvK.bb | Bin 24576 -> 0 bytes src/board.c | 14 +-- src/endgame.c | 194 +----------------------------------------- src/endgame.h | 2 - src/eval.c | 2 +- src/history.c | 2 +- src/makefile | 2 +- src/move.c | 6 +- src/move.h | 36 +++++--- src/movegen.h | 48 +++++------ src/movepick.c | 2 +- src/nn/accumulator.c | 2 +- src/search.c | 72 +++++++++------- src/see.c | 2 +- src/tb.h | 22 +++-- src/transposition.c | 28 ++++-- src/transposition.h | 43 ++++++++-- 18 files changed, 176 insertions(+), 301 deletions(-) delete mode 100644 src/bitbases/KBPvK.bb delete mode 100644 src/bitbases/KPvK.bb diff --git a/src/bitbases/KBPvK.bb b/src/bitbases/KBPvK.bb deleted file mode 100644 index 0d9048918131c4694efc76c222ec064949430f4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 196608 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 |9>IA_P$qqwY%3D%cF8C{;s?3 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

+V(m$J;O5UGibi@JIT`_$}YQCHEbF zpZrSh&+=pVMtrJ$m0mr4Q+kuH+$a6$e}o_9p7c`tir*!l`*-^v_g|I!GwIiT%KwE* zf$lf;UnT#N6c9hTpZtmbeCF)BdF((CND3$gD=8peT}nT2#Zet!=KcZJMd9n$T> z?<33}h=Gj%#oH!-drJSE|NF@b-sf>C`gXKiQpX>h_mE``9Z39UX z=U>b~aNo&i2Eys?BmT+yf9}isKMJ${zia(Tr}XbY^VtU=_4oRJ^5J3O4Fse1e^i+G zjc)%5hyVXD{r`u3l%E$B{n3+rl>ZB_@PDEFUnu_<%KwG(f1&j^KW}yiL;o+v+xWlh zUHsqc_|f0%_=%bI^3nQ()*rO~p!El>KWP0y z>u+rL-EgSCoBy}|A34WGYX6o0=)a!VKPpnJK?h{n0b~|DpfT{!;?y^*`DE<9}=P2bh7F*S}Lfqx)X_uj`}# z2QIDOx~}a1BMPzhZtllYc|`31vt6 zcUs3!eip6c=kCmZ9sRuz00z7d00tEQIr?iRL;9QFC%-ZxhmYQS>wla6MB@*^fEU4M z2f}FoBYq0dc>O=u`TZYZ>;LEe0Y&=;KIQ{mF0Z4_bfl_4L;|eB|>we(A6E_Z-^$ozg$}UGj_ht^JSMX0QIY-JHJl z{_o-!^7n!AkqlD&)A#>&4M=|fX*Yk+(Gw6K19z+g_c4=xuHRe#C%$+@>-__WHu-gL z{l62dKQvd;U+5kv==z_Gi+zZgfr$3IYW=@19s2*{{=5AHl!*MzwEwP;X#a}ZP5j^M_!aYJCjVyI z|Kzj(ulZf_i}~YwR}!Y}zv_S2hd8GAh5UI>?{Oo9;y=g#Lq7~e{9SkH_P=y9>2LlI z?#=(U{2_XJ^!8)!=dk?ibo2l0|L^oy`_Jh<`L#Quf2;rf{6_^Y^?xt*e=qglr2d=K zf0O!eqW&j;L;p?cze)W!+wgy3yRU|W|At4B`&s{!4la`ZYJa7FKa+n0|DgU)J_gJ$ z=AY2tNs|8h2i5mM`>*)V(cgR-;2#>ks~k~%9~u8g37qQB)yc;I`XBr0InbQ`@N@o8 zRPcAB^!|?U$inpZfQyf1mpIsehmP_o;uM`uE%9Zx8GLWB2u6 z$NzKWz~H~l|6S}Ef8jS|LDx|ZS;y~|=XZzo-@rfc|Kwx9d@2z5Z{q(&>-b5A_;2L@ z-Uk2!GwB~%py)tw*F*pRf6w_p^GU&99Nzy>N|?Ba`JYbv2M}9Xz*qa+{c$I#BZ&O#6R1eAoP+i&o0FoBwzHPyDX`XVOn@yZ=Xd zpN#)IX9Ur*{@*(8t^aTN0N?Gu{NKU$NUinVjrnmSpTn+V;>M-`s;et|3>P6 zquYOafXS~j>;J9yZ&Ux7_5X1`>VNT_xY3_e{l7xH8SQ@`{{Li7fA@ZO-+xf|P4x!t zDdXkn|MV;I(I5W!-j$3~{Xg-0@<;qC1MtuBf7N7azSaMW_8-0s%%q=Df{XBM`;YGQ zerW&2$H40F{uy2P4J4B3{=YEif8GA;9`%zBWjUw+op+R9q%Pk7(LYA*Kk?`DKMZ91 ze`@`|;;B#4|DQzvzdlL*K__PPXYk(>^*{M2`LzCiMD;%hekhCn*?*)!a|PMqa==}g ze?ouToAv)C`6mO!e~$h#Wc@+wFTM=GM`w@!!F&&RfBsMMMfsrswff(D46KIx=emRO zzv_QtDP#R5AMf~u{;dDk`7-(dk;6IuFYF)SaPA+_-@c{thyKB@t4C6Se9Zd)i>s*r zv;SA$X#Kyl9sfN?|6%=muy(I+$p3xlwEoXe`M>$I`cwWY8RPu}UibZ<9-+jSftmda zCHHvTKC!~4hj?D&t^?EjK$y#8M|IrahZV+U&Ae(Im?{~7E5Wk2WtYW+Oh z|Lp(Q?K$iJBi{4$-+W2`-yE#}9@YO&$Z7pI_-{P_8L$5naoYax`TtJ;!}OE=!~SpB zAw2j$;eGvQ`5ymCZ?*rzL;qL#qXe!x=ifwsKPMAU?Z4c;|I6@2-Iv2T{;#X9$WGn< zTF-yLU#0*5QqO+~e;ED0rvKBd|F>EH!Te97yX$}PWnd=#uh;$`-v6uGf9cnyeq~i=^gplt zZ>uzZ^}qPm|1kY0>;HrH|Bz38u~+|7%4Yn(OaCuh-Q+kA#8>?<-l_E;)^R0| zh+pV>mHK~~_5We-O#OH3-&;CfQ~wv^dzX!o|J&rO{@isXdn)?~ypjIm%K&`zKEMA* zisZgS`#-Gzmu~7n3ST|{cchA|^ZOg6V8CJQ7~}bWd;cfj|04yv`oBC3-Tyt; z|2x~y?EkfO@WIjF*VO;nm(>5+m(>5+kn;G~N&n}PiT(%t^YmBwqXe@4U$Kt!K>RuX zcX`P9-@5#LuK$mGIMe^varN-`HT7S=(frTg|LZT;{~Oltw>u#FU!(sw%YTjjUvd6T z^q=kjQSRpazoh^FBJk?xf#Gs$Jh!#w z+Z0*1HzP{)F-;Z%j@n4c0@n2$1c(41q z>+gHseOvb9`|rGcA0yCQU-08z2DENp2H;m2*w$O`__KP~Zsm7iNuN_d`o1XxASoa* zahcZbV?cZvIJ|%OE!=N zrQ>Mq9Jl4|c_sMp{u`0p9|kQP(A`F4e<7T&WFbp&m|x6 z^7cIjh(Gnci0K>B7y9$|z4hnqdmG^EdrYBX!?~a9|KVROLRi-Qy9Ba2z$qYo)}OcJ zKmPb*;?w@WkB!zpzP{H|Js^DR58wL3!@x)4`vbBLefh4f#8cm-;{ZZ~O0mGW%-%yVU=^)PFffe+OZOC;d`*8c zPqM}QKm7K?Se1^`_P^<$@?BHzi{E}df{EALz_w2H*YuqVP}6_MOZ|ItU;6h|2E^a* z|8q7fkR)=I^|yTJ->3c{rw;Xp<{E~R)*qhr7fOG4q+m(>`}*^>KbJrqujp??ea6qtPK{|G0a`ai7F!2Rmyseot^t&AcACk_EuT*sT z9o|3d|J|Ko{lA+{-o7V`#m9XaIFWR6M*L!a(_eAwN8i`}&%3X#|9SS|g| zy6i9j854?saXbCLy}li0h$sAC@4l-7=`U4*z~7P#@wX&H{AGMU?%K6Ka0RkhHvQw_ z9hZjwmnuNwBVQ+e#lYeHcgv{%;aPv)Lw5z&vuk2Bt|Ya;{SnHSAm~?7WkV_0zdjV@Po%nkH7yWJ9XCo_uFk&5ZHg; z>3jSCUGcd2hk-5qQ{4Ig&Gi3wqW{O+_sK8Di`IarftY7~tkL@&*^#|6_H4+kevM0?1qU-&6wvTL1R_ zL*AapfcVH)X7ne0N&!5#|6J$xU#JhrV#oD^!ejjvR{d|p|Bd*+5&t*h|3>teop-r8 zho!&x7zj*0{vYAw!l3;h+|!@1^%qKiq4XC@ zf1&gj%KwG(f1&g@Ud8%<+W!bApY}h($*;+u_kZ8dtNMR?H$4hW^(XgTex$$ZkM<)# zx?jKjSMHC?-Bkah?LV!U^ylsS@c*?^@EYxZ$@+h-6yX2sih;xO&;9MU|0Uah(Dna1 z^#{lLQ=Z)ZV>k2{N`ImB7fOGj^cSMPd9*MN%l~y;`Wurk{T)ue^mkbN{onQLZ704s zesRJ2b^f_n{J5vw(yw}neKmf8`|Kga`hWKWSpV<$u6g^Od=vj(@*f$q`@SQ#@!tZz z;^po8c>c!vfAK5p>J>k4-<2SWJF7Q8`HjbOH98RJ+1)XZx9^S9`oHPlc%3Q`0(^as3IrWkSFd<^`@RSli?8JPZ~XN= z#S`^^v%}Z-qW!Pjm-x(J6p-{x_W!*T^?!K~^?!L_{hJ-$wom<^&+5;8Mg0#-f34fs z{Etxn&oA2lsvQVdb!0d7Cts~UX#GL?KiAv;h3GFk@AAjTtv|=*|2i-KH%fnnldtyQ zVU-^ppVhx5$w=-OMaI|nQojwnb9C?d59TNSuM((y-lYF;xa9c#lg_OFJL?ZG+JDgPKN$YM+0$RV z@b69P4_bfF`h%+f;fMc&#rpYsDn4%gId1)h(qAb3g~_kdU*Y5WA8-GAc=G?irc1-A z{*o`s&zEceC13b|=-=XdmHut|R;{lWkS3ALJE=b+y8_{=iU0T3zt)~phvk5iS^u~3 zUqr|#jubHdt9=dq`?~M{v~zmKE8(f-eI446j=jN{T@ z$ECke`U{gktG}CV%*6OtUtd!H8~44GkmUZBbnE?(t;zwXW`AU!*Zq@uJhc*^k+u2V zD)?j`{M^26XQPe($bH^=rru}$zxc{L@s)YvJM%{IyOZE}Jbj@0pVd?FdHNphzh)qm zFWs^ezZNp@F#V*Hr|-H?%|M8+83^?sGy|djL*+iiuiVEWAF6-wm)wH*%s{-Y>sfvA z0Z_M`GIDB^e{G)v(#7~+KPe!-Zb7`zALJGs=9JO;bKL$fRQ)gf%_p{_{-MLtU&oVQ zS4HQ?ztQ_YUbm0?dfk5;|Dyj3nLvNzn5%z}{6BYOjw{GlcF=RaCchZz|HZ&74YlY0 z8Pxu-F%QK0e?5sn|GT~g*2xQ*zIFd?G5P+R-v5&R%wl;5LT0g;L9qVJK#&E@U=-g+ z`PR3;-D0DhhWqXgbPVIAf9W*?Vf`85i(eVxAL@@@5C4n;B+UMwZBc%hfsp@e24X2b zGZ2IR3WxuLZvR2o|6uh0+V|m^fv|orYUocss{hAzp+9K-LHWOqe}w-F-T(6pM1<$` zS1vLC$G(ra3;jQhWbOa&_x+{(|C#dr*!b5sYyQpTUnGBFemhV8*v_%`zg?%+SMj$W zf-e+*WnH}RRrP=4X%{hy`&&!YY3xa)rq{dN7j`oquPMZsR`4_bfF`h(UV zwEu%HKj57Hs&$8-a$k{A|CgTpL;tn%9sf5DSH{0u=HY!VNA0|FKlokpi}9HLuj>B| zcK4tEOvnFCzJI^=pAv}vE&eHTa<=`iwXU=NU#I`C$NKAh`u{rHf9vbxZvQzS`mfXf z*XjS{qx~O@??1ll_MhuRf6)4a)*rO~;Bo!O{Xebyk5Xp+>&wvJIKBTF{XPDb{*Nd~ zWKZ&o@tFS2u6kcyw~v8)@GAxsKLxM|+jSQg)A4`oKq&p+q9jWHTjFEjp7;CUU;TcXpWnF`^?!Y| zU;o#a?*B0|kN%%ciT5(}X9mLhF#}=!>dUDA$w%uCUS0Y6@PES5{%Zyzjwe6#=lps4 zYu!EaF`^CqpObHzcWU=K#;N|u*9CuJKG&K@zA^$O|MUHiW@9@3Q~fV~`&IH)Jmf1v z$*1qPb+rHE1hw|R@l^k-A1M9rh>w8>iXQ{wqr3Yd;NJL;=tzOvbNFumnSluXACy!2 z@Av-;9f*r)|CxbUN{;$qq<>4Njz5q3zdn!lzdq0QU)T72>i-=5%}afsr~c1V|L5WV zr|G{Ygr9gz|^xw7r9qyv`zjVZ+;rn~h{?`rphI~fHKl6wBKl$wYX#OJkUGj_Z z9RC*|_hkm+(*CdbTa>TW{=;uS>EB@ov$NxWbaw}mVrKmZ9tEh|D*L#vAsy{JCwKL~ z+W+>G{*(58-=zMVsQ=0Ls6RG! zPXD9rf8V16)pza5H|Z~I>1a6h|8|^wPJWmCVmzk*srFy-l7J$7mHth>rX$9Td?0)|8`$foa#?9DL@|o)rFt`1w*<`+W*?8{(bs?pZ?!Z{J%$k z@%yqW#?-%0|L@cP`&R#dC;Yun{rl9vKcWA){g1*HU;DHEmmdGux;;yOeIgb(*ASS z50E+Q;TtIV)Q`t~qyF!f;$vWSc>i2K`u_ruR(w>Al+zy=G09*(9k{vpMWtLu-9j6WkIGpn*%>%S;?!}C9eZ!6JOsmCIIgfIX0PriQq zfcyVC-t+vQ;alYIx(fd5I^uW!8wm1&3)p)6kDsIeuiM0L6Tdwk|8xFDpMEV){MQ@R zJMLFCe>XXgf4uKJVE*IR=Df~{HtRXYe=q($KY}2YU!}lyM9I_rU$K4@^Z#a-!}oFW zFT4~WvCI8`yN>p7kpH>LZy*Hl?AfQnRaPJ@@yC+?rUx^rLX7{u{xZgY&p!3~PaPhw zq^FjrOaA@EtpBfHCV!tMe~**Dr+fa4yLs^b3T z?T&-^=dAe$-wyMS9rN)Y>JRuI+<`Qd{m2SL@_(_cKX`W_;GzRbXd#Sq{f8BZUdQ;qUdQ}Tz&M}rf1T~;I>&#z?|b|&$A6{7 z_-`y;|EYD8{3C5-=g)Nf7kK4=;fKUyI{0Jp-&Bf!;l;o3RSLjW{3|){|L61nrpNpX zF9pI&f$`_%|E32Z3Xa#CFU>#Ni{f8W2}IraeEc`h|MibAg8$Dmem_h8KJooun?U(k zuu@3uKW~!%P2x9+-^}8FlkvaF_Ot2nALGAn{|^tMkM#z4r~miNzF89TWc-8YFb=m^ z&oTbH{lDfp_*dmuD|mS~{_&dW6W$75N?rCJbRZbV=Y#mC7+PTF|3|-8Tycr;QXoI{ z556S+jpP3B&y)XOIxZ)-symk0g70MDNNUVXJeEq`r|71d3H_QKZg+~sLpZ)Aa{vR{{*URMpa%cac7TrJEum9}wzwra62wyCZ z#edUxkpIEg0s6p)g3G)0|H4}Vcq(T{jb#rk|+6pr8t6N;iW)+=pTIP{$Jz1y#GfE z%lZGX5*`KX1zzX5|MR`&ci(<%`Sn*hf&4>GAomLht#n_P>p$dyz}Ljr#It}9*Cn0> zgobbOy#9a7y=!UOcJ}{k_U-t8B&q3Jv>z=XxPLT`50>ZhkMkz^Kg!h##BKcd^Z!4> zqb%5RFTOwio;B{sV6X#@}Q7C%hDF7xl-yUH-V2qe)sKXmS2DMN%OBi&*%U3<$w8nO?*v!O?>_8llXo< z{_D;EqojEH{=tJ+y!%f3|EU;XJsGGrJiJ?^aa)^RO}5>;@$|j$@jSVnz7M<{C$dAE)b>Wjg;Qa_AI9mXY_%|K} zDnIs7g2#gO0uPQmP%ak>9;)0juzn;QHxNP&E0vFe9rQe?s22|9?eI4v)rP zJDcuj|9-p&fM4G$0=;l#M1BS#)0ndMCK~1*j|Fr*KUxBxRH}Sss z`%wNt>gjvoQ$5$|YOc=*NMB9A9yM7 z^EB9bKL4Umc%0`BWJJrk{L9Fsk98OnplGa8ux!7Lf28A|i_Je*bIR+viI;*d-VI2l zNCDAn{<+xvi$?K3Qu`kNy59T0CG-9P&wqRUNc;;wCcekN^oswG_u2m+9qB(jd=&lf zFQWdB-iZ1SE6k^SQdhx#m^?h#|L+q#eV_OUV4l9W|ArNCCN0j{n?162B6t za-jHB=6{xnuPbVDcB}S?T2Jn0U7=OJpY$5@v@+cKb`+e_WzrH{1+V4&Hv!s|4XX>2jAsi=f%Gz{9D4m zCHz~ic!r{KtjmNF|GVceX5_EF`ou&&di1XM@ejkt)Svucegwa)f2MEl|3p8`)Axyg zJ@K013jc24WB9%|BMo>p-yiTO@dNw+)gu+2z88MK|9`F^ET9*Ta))?-z)%uDCcZMW z?%ALDpIrjUKPwO_w`c`I-z!|`zcmw;UCe-y zNB95l8<69k1R^+io&!hn^!*t9$J^MkjQ?E+B#@3s-@G@56$m~Sp1v2p?f;+0fA0mt zQZtlDWc>Ob9SC@yz8C+?`Cm*Ru`~JS4TLJUyq)}i81e5n5H8z4xSap#4TQ!e|8JXr zy$8VUmlc50S^v>43(fy5%|D+#{>Seg|7!(8_o0~Kclp=MU;ImI{a;f2ONxI<@gGvY znI3!kRBO3E$A6i(hvEI?{|Y_J`tR}4C@1}Y@;{8`v0y@n|F-@!&;QLvobUEn%TR=$ z^Z#mR{x9r+_thXNJ@A!g)UltdTm|H;{~wb7_pPATt8%ygUlyp{T~+v}?pH_n$1c79 zi~7a<52^gm^%4KbLHr@R{Bz#?ONxI<@h>US(h`?ugp8pHS{NH#e zg!FCjFL{dpUHtpb3eoo%+OcQ!K7RPWIWztf|H$vPran)VQLxAOcK_G)IQV1w9x#1# z?FRP$n+Kl07ryfJy~^kQzn|M;e`?C6Bo+`Dcf@<7hQyb|^Yp#qfB*ic_5w-2qX#9i z0zthKuN4T{Jx2LQ_W#pO`u*RM<{vx7$AC1R_W&gSH&C;PfBf>a(10vaU>WiMQO5sA zqx%p154EoCgn#(Ougd?DihoJPzog<{Qt^*$_~tU?e*7nEBmawc;-8tQ`S(74_`mM+ zAMwZhKk+ecBOVezB)%9wF;J8kKa}qZm7*00 z_y6Ai-@b2>s}A~q_!oVuf8w_+tFTbOR^-zdz%B8-n+D`9JwDD*rbAbNY_G!GBn1{s+Z>``qN5fBGvM zKpdKXr|;mOQ(gSY{0~1H_(y03Vj2IG*UkUBKE{7YQi4DC`QK#xBh5e3{3FdjGV%4S z{@-zo_XoWPK>TYTKm6ApJO10ZpI~;v|Db*R)UIeBKh87$F8upF04NZ?qM%nkMYsdX z;XCEA0>MeqXZ)CW6ujH9U--=5UcsQa#1~c|9KTUMqpu_T|J{UXM_z#d&kBU=%L;`1 zjoRD)Pq{vH{S)HfGV=fMg!r!5|Lqb;{Cuwxm{pTD zV*LmOZ<+_{(XpTFTNR>G;H7|K{r&)oO?;&u%ld09v?Xk4N|)lm9XKACvzv`5%-2v1a?3RAkSe-LCE| z`J*CSC-0Y)`S(8l3Dp1Y@eBUlkmP-uul#}e>*=ja<4^H#{!k!1&cl6c?7RK>pO*i3 z{0lDy;O9q+e@dCH|3RPMBQQq!QqaN6JoO)vn*UKsq5BU00}p-C|M&2+N1hi>zyE2O z?++Mxe}LN0nE1L*{$i@!Zr!tjkofh(hra%j`~NyFJ^!zD8||lV@54V9@zl8Df4cu( zcI!CBf3%+=@hG@iuK%CnU-%KfmdC-r@aOO^yA*vp{)Mm91H8UFJfi=n$6E5wN(NqJ zW-mMnp7!ufTdY7~E^ihx3RjQq|DSZCzV62JfBYNc zzk2hP=Kp+-@n1c;|3B>C_y6=9>;gcy%DcsROZ8{s%uMUi<%zoAZC2{9h;kll}i{2K74Q zzu{Zq$W!CLLTO6h+K2D>-z@8YYWx>?6c}G>1wS#}R!rjGUy1*LZ$zK)c%KXu<3Hig ziT~(NrJ&`1@X%K}_MgoE@JH>jKzJ!w*8jHr-&`#k%CN5ObCW1v%>SqPpYpLF@<%r>|0elIwsM=?$M3QHPqp!*`L8b*_V4k*>}33xmTtiV@KRm_@|;p_;dI-ej5M6TfxhU+JpCh@!3^C9`FB+mx5*eO&{?qDFs|qmg7Ia zM*s!yRM|E?bbZ|aLnglD`vXQy4gLdP+y4LNM9Rgf9LayZ&;I|g=4Ah08vo1v?|0+> zhx_qQ`4#u*JplLOUwA86+JBONT%+hyL&nKiu*8dZ`9M;I^yf!VUrVgFhA-}r(Fm#5<2 zwZi+${14vrfj=ky7x`a!D_GipxBTD5H~!rMMdvjB#h)gS@;?*Eh5vN@{QiG0|4%6E z@c+By|AXZJy%_(q|6k?S9slcSKNDs9_&q28FZaLi@zE$x=l|_?(sXUbWc=sP;$Q#t z^Z#~x4*$k?^Z(NRll_02{|znvyZFZciTK~~zjY-if9Pi>`9J6X^#6`~#{Y-$KU;vn znZfT*+5aELoY(&^_rKqf|C@X9Ppc((3JV!G_u^l8E7;z^_xa;LT;uTF^M5m;&i^`m zPvn2`NB(c|zra(ilK21f`7b^n=l`h-ojZ|}(GGU`|I7G%&i?iFZ~o){ z{{jC#pXo{C7wGH}_iq+4E2NKV|&4UD$uO@n08z iM*qL#e^I{L%RK*|h<`l6vE={rjQ`I<9^3!F8vY*^#>$HT diff --git a/src/bitbases/KPvK.bb b/src/bitbases/KPvK.bb deleted file mode 100644 index 31702390f52cf773880e68f2cf67b58626fe218c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 diff --git a/src/board.c b/src/board.c index 5ff91d25..ff77d34e 100644 --- a/src/board.c +++ b/src/board.c @@ -407,15 +407,15 @@ void MakeMoveUpdate(Move move, Board* board, int update) { } if (PieceType(piece) == PAWN) { - if (IsDP(move)) { + if ((from ^ to) == 16) { int epSquare = to - PawnDir(board->stm); if (GetPawnAttacks(epSquare, board->stm) & PieceBB(PAWN, board->xstm)) { board->epSquare = epSquare; board->zobrist ^= ZOBRIST_EP_KEYS[board->epSquare]; } - } else if (Promo(move)) { - int promoted = Promo(move); + } else if (IsPromo(move)) { + int promoted = PromoPiece(move, board->stm); FlipBit(board->pieces[piece], to); FlipBit(board->pieces[promoted], to); @@ -471,8 +471,8 @@ void UndoMove(Move move, Board* board) { board->threatened = board->history[board->histPly].threatened; board->easyCapture = board->history[board->histPly].easyCapture; - if (Promo(move)) { - int promoted = Promo(move); + if (IsPromo(move)) { + int promoted = PromoPiece(move, board->stm); FlipBit(board->pieces[piece], to); FlipBit(board->pieces[promoted], to); board->squares[to] = piece; @@ -647,7 +647,7 @@ int IsPseudoLegal(Move move, Board* board) { return 1; } - if (Promo(move)) { + if (IsPromo(move)) { if (BitCount(board->checkers) > 1) return 0; @@ -746,7 +746,7 @@ void InitCuckoo() { if (!GetBit(GetPieceAttacks(s1, 0, pcType), s2)) continue; - Move move = BuildMove(s1, s2, pc, NO_PROMO, QUIET); + Move move = BuildMove(s1, s2, pc, QUIET_FLAG); uint64_t hash = ZOBRIST_PIECES[pc][s1] ^ ZOBRIST_PIECES[pc][s2] ^ ZOBRIST_SIDE_KEY; uint32_t i = Hash1(hash); diff --git a/src/endgame.c b/src/endgame.c index 9e37d02c..669d5cef 100644 --- a/src/endgame.c +++ b/src/endgame.c @@ -16,165 +16,10 @@ #include "endgame.h" -#include - -#include "bits.h" -#include "board.h" #include "search.h" -#include "see.h" +#include "types.h" #include "util.h" -#define INCBIN_PREFIX -#define INCBIN_STYLE INCBIN_STYLE_CAMEL -#include "incbin.h" - -INCBIN(KPK, "bitbases/KPvK.bb"); -INCBIN(KBPK, "bitbases/KBPvK.bb"); - -INLINE int PushTogether(int sq1, int sq2) { - return 70 - Distance(sq1, sq2); -} - -INLINE int PushToEdge(int sq) { - int rankDistance = Min(Rank(sq), 7 - Rank(sq)); - int fileDistance = Min(File(sq), 7 - File(sq)); - - return 20 - (rankDistance * rankDistance) - (fileDistance * fileDistance); -} - -INLINE int MaterialValue(Board* board, const int side) { - int staticScore = 0; - for (int piece = PAWN; piece <= QUEEN; piece++) - staticScore += BitCount(PieceBB(piece, side)) * SEE_VALUE[piece]; - - return staticScore; -} - -// The following KPK code is modified for my use from Cheng (as is the dataset) -INLINE uint32_t KPKIndex(int winningKing, int losingKing, int pawn, int stm) { - int file = File(pawn); - int x = file > 3 ? 7 : 0; - - winningKing ^= x; - losingKing ^= x; - pawn ^= x; - file ^= x; - - uint32_t p = (((pawn & 0x38) - 8) >> 1) | file; - - return (uint32_t) winningKing | ((uint32_t) losingKing << 6) | ((uint32_t) stm << 12) | ((uint32_t) p << 13); -} - -INLINE uint8_t KPKDraw(int winningSide, int winningKing, int losingKing, int pawn, int stm) { - uint32_t x = (winningSide == WHITE) ? 0 : 56; - uint32_t idx = KPKIndex(winningKing ^ x, losingKing ^ x, pawn ^ x, winningSide ^ stm); - - return (uint8_t) (KPKData[idx >> 3] & (1U << (idx & 7))); -} - -INLINE int EvaluateKPK(Board* board, const int winningSide) { - const int losingSide = winningSide ^ 1; - int score = WINNING_ENDGAME + MaterialValue(board, winningSide); - - int winningKing = LSB(PieceBB(KING, winningSide)); - int losingKing = LSB(PieceBB(KING, losingSide)); - int pawn = winningSide == WHITE ? LSB(PieceBB(PAWN, WHITE)) : MSB(PieceBB(PAWN, BLACK)); - - if (KPKDraw(winningSide, winningKing, losingKing, pawn, board->stm)) - return 0; - - score += winningSide == WHITE ? (7 - Rank(pawn)) : Rank(pawn); - - return winningSide == board->stm ? score : -score; -} - -INLINE int EvaluateKXK(Board* board, const int winningSide) { - const int losingSide = winningSide ^ 1; - int score = WINNING_ENDGAME + MaterialValue(board, winningSide); - - int winningKing = LSB(PieceBB(KING, winningSide)); - int losingKing = LSB(PieceBB(KING, losingSide)); - - score += PushTogether(winningKing, losingKing) + PushToEdge(losingKing); - - score = winningSide == board->stm ? score : -score; - return Min(TB_WIN_BOUND - 1, Max(-TB_WIN_BOUND + 1, score)); -} - -INLINE int EvaluateKBNK(Board* board, const int winningSide) { - const int losingSide = winningSide ^ 1; - int score = WINNING_ENDGAME + MaterialValue(board, winningSide); - - int winningKing = LSB(PieceBB(KING, winningSide)); - int losingKing = LSB(PieceBB(KING, losingSide)); - - score += PushTogether(winningKing, losingKing); - - if (DARK_SQS & PieceBB(BISHOP, winningSide)) { - score += 50 * (7 - Min(MDistance(losingKing, A1), MDistance(losingKing, H8))); - } else { - score += 50 * (7 - Min(MDistance(losingKing, A8), MDistance(losingKing, H1))); - } - - score = winningSide == board->stm ? score : -score; - return Min(TB_WIN_BOUND - 1, Max(-TB_WIN_BOUND + 1, score)); -} - -INLINE uint32_t KBPKIndex(int wking, int lking, int bishop, int pawn, int stm) { - int file = File(pawn); - int x = file > 3 ? 7 : 0; - - wking ^= x, lking ^= x, bishop ^= x, pawn ^= x; - - uint32_t p = (pawn >> 3) - 1; - uint32_t b = bishop >> 1; - - return (uint32_t) wking | ((uint32_t) lking << 6) | (b << 12) | ((uint32_t) stm << 17) | (p << 18); -} - -INLINE uint8_t KBPKDraw(int winningSide, int winningKing, int losingKing, int bishop, int pawn, int stm) { - int x = winningSide == WHITE ? 0 : 56; - uint32_t idx = KBPKIndex(winningKing ^ x, losingKing ^ x, bishop ^ x, pawn ^ x, winningSide ^ stm); - - return !(uint8_t) (KBPKData[idx >> 3] & (1U << (idx & 7))); -} - -INLINE int EvaluateKBPK(Board* board, const int winningSide) { - const int losingSide = winningSide ^ 1; - - int pawn = winningSide == WHITE ? LSB(PieceBB(PAWN, WHITE)) : MSB(PieceBB(PAWN, BLACK)); - int promotionSq = winningSide == WHITE ? File(pawn) : A1 + File(pawn); - int darkPromoSq = !!GetBit(DARK_SQS, promotionSq); - int darkBishop = !!(DARK_SQS & PieceBB(BISHOP, winningSide)); - - uint8_t files = PawnFiles(PieceBB(PAWN, winningSide)); - - // "Winning" if not a rook pawn or have right bishop - if ((files != 0x01 && files != 0x80) || darkPromoSq == darkBishop) { - int score = WINNING_ENDGAME + MaterialValue(board, winningSide); - - score += winningSide == WHITE ? (7 - Rank(pawn)) : Rank(pawn); - score = winningSide == board->stm ? score : -score; - - return Min(TB_WIN_BOUND - 1, Max(-TB_WIN_BOUND + 1, score)); - } - - // Utilize bitbase for everything else - int winningKing = LSB(PieceBB(KING, winningSide)); - int losingKing = LSB(PieceBB(KING, losingSide)); - int bishop = LSB(PieceBB(BISHOP, winningSide)); - - if (KBPKDraw(winningSide, winningKing, losingKing, bishop, pawn, board->stm)) - return 0; - - int score = WINNING_ENDGAME + MaterialValue(board, winningSide); - - score += winningSide == WHITE ? (7 - Rank(pawn)) : Rank(pawn); - score = winningSide == board->stm ? score : -score; - - return Min(TB_WIN_BOUND - 1, Max(-TB_WIN_BOUND + 1, score)); -} - int EvaluateKnownPositions(Board* board) { switch (board->piecesCounts) { // See IsMaterialDraw @@ -190,43 +35,6 @@ int EvaluateKnownPositions(Board* board) { case 0x100100: // KNkb case 0x110000: // KBkb return 0; - case 0x1: // KPk - return EvaluateKPK(board, WHITE); - case 0x10: // Kkp - return EvaluateKPK(board, BLACK); - case 0x10100: // KBNk - return EvaluateKBNK(board, WHITE); - case 0x101000: // Kkbn - return EvaluateKBNK(board, BLACK); - default: break; - } - - if (BitCount(OccBB(BLACK)) == 1) { - // KBPK - if ((board->piecesCounts ^ 0x10000) <= 0xF) - return EvaluateKBPK(board, WHITE); - - // stacked rook pawns - if (board->piecesCounts <= 0xF) { - uint8_t files = PawnFiles(PieceBB(PAWN, WHITE)); - if (files == 0x1 || files == 0x80) - return EvaluateKPK(board, WHITE); - } - - return EvaluateKXK(board, WHITE); - } else if (BitCount(OccBB(WHITE)) == 1) { - // Kkbp - if ((board->piecesCounts ^ 0x100000) <= 0xF0) - return EvaluateKBPK(board, BLACK); - - // stacked rook pawns - if (board->piecesCounts <= 0xF0) { - uint8_t files = PawnFiles(PieceBB(PAWN, BLACK)); - if (files == 0x1 || files == 0x80) - return EvaluateKPK(board, BLACK); - } - - return EvaluateKXK(board, BLACK); } return UNKNOWN; diff --git a/src/endgame.h b/src/endgame.h index c89654de..0b27b880 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -20,8 +20,6 @@ #include "types.h" #include "util.h" -#define WINNING_ENDGAME 25000 - int EvaluateKnownPositions(Board* board); #endif \ No newline at end of file diff --git a/src/eval.c b/src/eval.c index 98d840f9..a372097e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -63,7 +63,7 @@ Score Evaluate(Board* board, ThreadData* thread) { // scaled based on phase [1, 1.5] score = (128 + board->phase) * score / 128; - return Min(TB_WIN_BOUND - 1, Max(-TB_WIN_BOUND + 1, score)); + return Min(2047, Max(-2048, score)); } void EvaluateTrace(Board* board) { diff --git a/src/history.c b/src/history.c index 7a5e1d9d..ce3f8d44 100644 --- a/src/history.c +++ b/src/history.c @@ -66,7 +66,7 @@ void UpdateHistories(SearchStack* ss, AddHistoryHeuristic(&HH(stm, bestMove, board->threatened), inc); UpdateCH(ss, bestMove, inc); - if (Promo(bestMove) < WHITE_QUEEN) { + if (PromoPT(bestMove) != QUEEN) { AddKillerMove(ss, bestMove); if ((ss - 1)->move) diff --git a/src/makefile b/src/makefile index e69b96bd..fd2de488 100644 --- a/src/makefile +++ b/src/makefile @@ -4,7 +4,7 @@ EXE = berserk SRC = *.c nn/*.c pyrrhic/tbprobe.c CC = gcc -VERSION = 20230927 +VERSION = 20231006 MAIN_NETWORK = networks/berserk-d1b801c65262.nn EVALFILE = $(MAIN_NETWORK) DEFS = -DVERSION=\"$(VERSION)\" -DEVALFILE=\"$(EVALFILE)\" -DNDEBUG diff --git a/src/move.c b/src/move.c index 48c99086..0d365e0b 100644 --- a/src/move.c +++ b/src/move.c @@ -26,7 +26,7 @@ const char* PIECE_TO_CHAR = "PpNnBbRrQqKk"; -const char* PROMOTION_TO_CHAR = "--nnbbrrqq--"; +const char* PROMOTION_TO_CHAR = "-nbrq-"; const int CHAR_TO_PIECE[] = { ['P'] = WHITE_PAWN, // @@ -95,8 +95,8 @@ char* MoveToStr(Move move, Board* board) { if (CHESS_960 && IsCas(move)) to = board->cr[CASTLING_ROOK[to]]; - if (Promo(move)) { - sprintf(buffer, "%s%s%c", SQ_TO_COORD[from], SQ_TO_COORD[to], PROMOTION_TO_CHAR[Promo(move)]); + if (IsPromo(move)) { + sprintf(buffer, "%s%s%c", SQ_TO_COORD[from], SQ_TO_COORD[to], PROMOTION_TO_CHAR[PromoPT(move)]); } else { sprintf(buffer, "%s%s", SQ_TO_COORD[from], SQ_TO_COORD[to]); } diff --git a/src/move.h b/src/move.h index fb0b57c0..d4ac4d85 100644 --- a/src/move.h +++ b/src/move.h @@ -21,6 +21,16 @@ #define NULL_MOVE 0 +#define QUIET_FLAG 0b0000 +#define CASTLE_FLAG 0b0001 +#define CAPTURE_FLAG 0b0100 +#define EP_FLAG 0b0110 +#define PROMO_FLAG 0b1000 +#define KNIGHT_PROMO_FLAG 0b1000 +#define BISHOP_PROMO_FLAG 0b1001 +#define ROOK_PROMO_FLAG 0b1010 +#define QUEEN_PROMO_FLAG 0b1011 + extern const int CHAR_TO_PIECE[]; extern const char* PIECE_TO_CHAR; extern const char* PROMOTION_TO_CHAR; @@ -28,18 +38,20 @@ extern const char* SQ_TO_COORD[64]; extern const int CASTLE_ROOK_DEST[64]; extern const int CASTLING_ROOK[64]; -#define BuildMove(from, to, piece, promo, flags) \ - (from) | ((to) << 6) | ((piece) << 12) | ((promo) << 16) | ((flags) << 20) -#define From(move) ((int) (move) &0x3f) -#define To(move) (((int) (move) &0xfc0) >> 6) -#define Moving(move) (((int) (move) &0xf000) >> 12) -#define Promo(move) (((int) (move) &0xf0000) >> 16) -#define IsCap(move) (((int) (move) &0x100000) >> 20) -#define IsDP(move) (((int) (move) &0x200000) >> 21) -#define IsEP(move) (((int) (move) &0x400000) >> 22) -#define IsCas(move) (((int) (move) &0x800000) >> 23) -// just mask the from/to bits into a single int for indexing butterfly tables -#define FromTo(move) ((int) (move) &0xfff) +#define BuildMove(from, to, piece, flags) (from) | ((to) << 6) | ((piece) << 12) | ((flags) << 16) +#define FromTo(move) (((int) (move) &0x00fff) >> 0) +#define From(move) (((int) (move) &0x0003f) >> 0) +#define To(move) (((int) (move) &0x00fc0) >> 6) +#define Moving(move) (((int) (move) &0x0f000) >> 12) +#define Flags(move) (((int) (move) &0xf0000) >> 16) + +#define IsCap(move) (!!(Flags(move) & CAPTURE_FLAG)) +#define IsEP(move) (Flags(move) == EP_FLAG) +#define IsCas(move) (Flags(move) == CASTLE_FLAG) + +#define IsPromo(move) (!!(Flags(move) & PROMO_FLAG)) +#define PromoPT(move) ((Flags(move) & 0x3) + KNIGHT) +#define PromoPiece(move, stm) (Piece(PromoPT(move), stm)) Move ParseMove(char* moveStr, Board* board); char* MoveToStr(Move move, Board* board); diff --git a/src/movegen.h b/src/movegen.h index a5ba9b1f..5ce2bde0 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -36,13 +36,6 @@ extern const int CASTLE_MAP[4][3]; #define ALL -1ULL -#define NO_PROMO 0 -#define QUIET 0b0000 -#define CAPTURE 0b0001 -#define EP 0b0101 -#define DP 0b0010 -#define CASTLE 0b1000 - #define WHITE_KS 0x8 #define WHITE_QS 0x4 #define BLACK_KS 0x2 @@ -58,20 +51,19 @@ enum { GT_LEGAL = 0b11, }; -INLINE ScoredMove* AddMove(ScoredMove* moves, int from, int to, int moving, int promo, int flags) { - *moves++ = (ScoredMove) {.move = BuildMove(from, to, moving, promo, flags), .score = 0}; +INLINE ScoredMove* AddMove(ScoredMove* moves, int from, int to, int moving, int flags) { + *moves++ = (ScoredMove) {.move = BuildMove(from, to, moving, flags), .score = 0}; return moves; } -INLINE ScoredMove* -AddPromotions(ScoredMove* moves, int from, int to, int moving, int flags, const int stm, const int type) { +INLINE ScoredMove* AddPromotions(ScoredMove* moves, int from, int to, int moving, const int baseFlag, const int type) { if (type & GT_CAPTURE) - moves = AddMove(moves, from, to, moving, Piece(QUEEN, stm), flags); + moves = AddMove(moves, from, to, moving, baseFlag | QUEEN_PROMO_FLAG); if (type & GT_QUIET) { - moves = AddMove(moves, from, to, moving, Piece(ROOK, stm), flags); - moves = AddMove(moves, from, to, moving, Piece(BISHOP, stm), flags); - moves = AddMove(moves, from, to, moving, Piece(KNIGHT, stm), flags); + moves = AddMove(moves, from, to, moving, baseFlag | ROOK_PROMO_FLAG); + moves = AddMove(moves, from, to, moving, baseFlag | BISHOP_PROMO_FLAG); + moves = AddMove(moves, from, to, moving, baseFlag | KNIGHT_PROMO_FLAG); } return moves; @@ -90,12 +82,12 @@ INLINE ScoredMove* AddPawnMoves(ScoredMove* moves, BitBoard opts, Board* board, while (targets) { int to = PopLSB(&targets); - moves = AddMove(moves, to - PawnDir(stm), to, Piece(PAWN, stm), NO_PROMO, QUIET); + moves = AddMove(moves, to - PawnDir(stm), to, Piece(PAWN, stm), QUIET_FLAG); } while (dpTargets) { int to = PopLSB(&dpTargets); - moves = AddMove(moves, to - PawnDir(stm) - PawnDir(stm), to, Piece(PAWN, stm), NO_PROMO, DP); + moves = AddMove(moves, to - PawnDir(stm) - PawnDir(stm), to, Piece(PAWN, stm), QUIET_FLAG); } } @@ -107,12 +99,12 @@ INLINE ScoredMove* AddPawnMoves(ScoredMove* moves, BitBoard opts, Board* board, while (eTargets) { int to = PopLSB(&eTargets); - moves = AddMove(moves, to - (PawnDir(stm) + E), to, Piece(PAWN, stm), NO_PROMO, CAPTURE); + moves = AddMove(moves, to - (PawnDir(stm) + E), to, Piece(PAWN, stm), CAPTURE_FLAG); } while (wTargets) { int to = PopLSB(&wTargets); - moves = AddMove(moves, to - (PawnDir(stm) + W), to, Piece(PAWN, stm), NO_PROMO, CAPTURE); + moves = AddMove(moves, to - (PawnDir(stm) + W), to, Piece(PAWN, stm), CAPTURE_FLAG); } if (board->epSquare) { @@ -120,7 +112,7 @@ INLINE ScoredMove* AddPawnMoves(ScoredMove* moves, BitBoard opts, Board* board, while (movers) { int from = PopLSB(&movers); - moves = AddMove(moves, from, board->epSquare, Piece(PAWN, stm), NO_PROMO, EP); + moves = AddMove(moves, from, board->epSquare, Piece(PAWN, stm), EP_FLAG); } } } @@ -133,17 +125,17 @@ INLINE ScoredMove* AddPawnMoves(ScoredMove* moves, BitBoard opts, Board* board, while (sTargets) { int to = PopLSB(&sTargets); - moves = AddPromotions(moves, to - PawnDir(stm), to, Piece(PAWN, stm), QUIET, stm, type); + moves = AddPromotions(moves, to - PawnDir(stm), to, Piece(PAWN, stm), QUIET_FLAG, type); } while (eTargets) { int to = PopLSB(&eTargets); - moves = AddPromotions(moves, to - (PawnDir(stm) + E), to, Piece(PAWN, stm), CAPTURE, stm, type); + moves = AddPromotions(moves, to - (PawnDir(stm) + E), to, Piece(PAWN, stm), CAPTURE_FLAG, type); } while (wTargets) { int to = PopLSB(&wTargets); - moves = AddPromotions(moves, to - (PawnDir(stm) + W), to, Piece(PAWN, stm), CAPTURE, stm, type); + moves = AddPromotions(moves, to - (PawnDir(stm) + W), to, Piece(PAWN, stm), CAPTURE_FLAG, type); } return moves; @@ -168,11 +160,11 @@ INLINE ScoredMove* AddPieceMoves(ScoredMove* moves, while (targets) { int to = PopLSB(&targets); - int flags = type == GT_QUIET ? QUIET : // - type == GT_CAPTURE ? CAPTURE : // - GetBit(OccBB(xstm), to) ? CAPTURE : QUIET; + int flags = type == GT_QUIET ? QUIET_FLAG : // + type == GT_CAPTURE ? CAPTURE_FLAG : // + GetBit(OccBB(xstm), to) ? CAPTURE_FLAG : QUIET_FLAG; - moves = AddMove(moves, from, to, Piece(piece, stm), NO_PROMO, flags); + moves = AddMove(moves, from, to, Piece(piece, stm), flags); } } @@ -200,7 +192,7 @@ INLINE ScoredMove* AddCastles(ScoredMove* moves, Board* board, const int stm) { if (!((OccBB(BOTH) ^ Bit(from) ^ Bit(rookFrom)) & between)) if (!(kingCrossing & board->threatened)) - moves = AddMove(moves, from, to, Piece(KING, stm), NO_PROMO, CASTLE); + moves = AddMove(moves, from, to, Piece(KING, stm), CASTLE_FLAG); } return moves; diff --git a/src/movepick.c b/src/movepick.c index 20d16cf1..8dfcbb93 100644 --- a/src/movepick.c +++ b/src/movepick.c @@ -72,7 +72,7 @@ void ScoreMoves(MovePicker* picker, Board* board, const int type) { } else if (type == ST_MVV) - current->score = SEE_VALUE[IsEP(move) ? PAWN : PieceType(board->squares[To(move)])] + 2000 * !!Promo(move); + current->score = SEE_VALUE[IsEP(move) ? PAWN : PieceType(board->squares[To(move)])] + 2000 * IsPromo(move); current++; } diff --git a/src/nn/accumulator.c b/src/nn/accumulator.c index a6c2c15b..ee126654 100644 --- a/src/nn/accumulator.c +++ b/src/nn/accumulator.c @@ -100,7 +100,7 @@ void ApplyUpdates(acc_t* output, acc_t* prev, Board* board, const Move move, con const int movingSide = Moving(move) & 1; int from = FeatureIdx(Moving(move), From(move), king, view); - int to = FeatureIdx(Promo(move) ?: Moving(move), To(move), king, view); + int to = FeatureIdx(IsPromo(move) ? PromoPiece(move, movingSide) : Moving(move), To(move), king, view); if (IsCas(move)) { int rookFrom = FeatureIdx(Piece(ROOK, movingSide), board->cr[CASTLING_ROOK[To(move)]], king, view); diff --git a/src/search.c b/src/search.c index e3da14bb..72e020cb 100644 --- a/src/search.c +++ b/src/search.c @@ -207,6 +207,13 @@ void Search(ThreadData* thread) { } while (1) { + // Go checkmate hunting once our eval is at the edge of it's bounds (-2048, 2047) + if (alpha < -2000) + alpha = -CHECKMATE; + + if (beta > 2000) + beta = CHECKMATE; + // search! score = Negamax(alpha, beta, Max(1, searchDepth), 0, thread, &nullPv, ss); @@ -309,9 +316,6 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* int bestScore = -CHECKMATE; // int maxScore = CHECKMATE; // best possible int origAlpha = alpha; // remember first alpha for tt storage - int ttHit = 0; - int ttScore = UNKNOWN; - int ttPv = 0; int inCheck = !!board->checkers; int improving = 0; int eval = ss->staticEval; @@ -357,15 +361,21 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* // check the transposition table for previous info // we ignore the tt on singular extension searches - TTEntry* tt = ss->skip ? NULL : TTProbe(board->zobrist, &ttHit); - ttScore = ttHit ? TTScore(tt, ss->ply) : UNKNOWN; - ttPv = isPV || (ttHit && TTPV(tt)); - hashMove = isRoot ? thread->rootMoves[thread->multiPV].move : ttHit ? tt->move : NULL_MOVE; + int ttHit = 0; + int ttScore = UNKNOWN; + int ttEval = UNKNOWN; + int ttDepth = DEPTH_OFFSET; + int ttBound = BOUND_UNKNOWN; + int ttPv = isPV; + + TTEntry* tt = + ss->skip ? NULL : TTProbe(board->zobrist, ss->ply, &ttHit, &hashMove, &ttScore, &ttEval, &ttDepth, &ttBound, &ttPv); + hashMove = isRoot ? thread->rootMoves[thread->multiPV].move : hashMove; // if the TT has a value that fits our position and has been searched to an // equal or greater depth, then we accept this score and prune - if (!isPV && ttScore != UNKNOWN && TTDepth(tt) >= depth && (cutnode || ttScore <= alpha) && - (TTBound(tt) & (ttScore >= beta ? BOUND_LOWER : BOUND_UPPER))) + if (!isPV && ttScore != UNKNOWN && ttDepth >= depth && (cutnode || ttScore <= alpha) && + (ttBound & (ttScore >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttScore; // tablebase - we do not do this at root @@ -413,14 +423,14 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* eval = ss->staticEval = UNKNOWN; } else { if (ttHit) { - eval = ss->staticEval = tt->eval; + eval = ss->staticEval = ttEval; if (ss->staticEval == UNKNOWN) eval = ss->staticEval = Evaluate(board, thread); // correct eval on fmr eval = AdjustEvalOnFMR(board, eval); - if (ttScore != UNKNOWN && (TTBound(tt) & (ttScore > eval ? BOUND_LOWER : BOUND_UPPER))) + if (ttScore != UNKNOWN && (ttBound & (ttScore > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttScore; } else if (!ss->skip) { eval = ss->staticEval = Evaluate(board, thread); @@ -457,7 +467,7 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* if (!isPV && !inCheck) { // Reverse Futility Pruning // i.e. the static eval is so far above beta we prune - if (depth <= 8 && !ss->skip && eval < WINNING_ENDGAME && eval >= beta && + if (depth <= 8 && !ss->skip && eval < TB_WIN_BOUND && eval >= beta && eval - 69 * depth + 112 * (improving && !board->easyCapture) >= beta && (!hashMove || GetHistory(ss, thread, hashMove) > 12288)) return eval; @@ -496,8 +506,7 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* // If a relatively deep search from our TT doesn't say this node is // less than beta + margin, then we run a shallow search to look int probBeta = beta + 200; - if (depth >= 5 && !ss->skip && abs(beta) < TB_WIN_BOUND && - !(ttHit && TTDepth(tt) >= depth - 3 && ttScore < probBeta)) { + if (depth >= 5 && !ss->skip && abs(beta) < TB_WIN_BOUND && !(ttHit && ttDepth >= depth - 3 && ttScore < probBeta)) { InitPCMovePicker(&mp, thread, probBeta > eval); while ((move = NextMove(&mp, board, 1))) { if (!IsLegal(move, board)) @@ -549,7 +558,7 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* if (!isRoot && legalMoves >= LMP[improving][depth]) skipQuiets = 1; - if (!IsCap(move) && PieceType(Promo(move)) != QUEEN) { + if (!IsCap(move) && PromoPT(move) != QUEEN) { int lmrDepth = Max(1, depth - LMR[Min(depth, 63)][Min(legalMoves, 63)]); if (!killerOrCounter && lmrDepth < 6 && history < -2500 * (depth - 1)) { @@ -589,8 +598,8 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* // (allows for reductions when doing singular search) if (!isRoot && ss->ply < thread->depth * 2) { // ttHit is implied for move == hashMove to ever be true - if (depth >= 6 && move == hashMove && TTDepth(tt) >= depth - 3 && (TTBound(tt) & BOUND_LOWER) && - abs(ttScore) < WINNING_ENDGAME) { + if (depth >= 6 && move == hashMove && ttDepth >= depth - 3 && (ttBound & BOUND_LOWER) && + abs(ttScore) < TB_WIN_BOUND) { int sBeta = Max(ttScore - 5 * depth / 8, -CHECKMATE); int sDepth = (depth - 1) / 2; @@ -640,7 +649,7 @@ int Negamax(int alpha, int beta, int depth, int cutnode, ThreadData* thread, PV* R -= 2; // less likely a non-capture is best - if (IsCap(hashMove) || Promo(hashMove)) + if (IsCap(hashMove) || IsPromo(hashMove)) R++; // move GAVE check @@ -746,12 +755,10 @@ int Quiesce(int alpha, int beta, int depth, ThreadData* thread, SearchStack* ss) int futility = -CHECKMATE; int bestScore = -CHECKMATE + ss->ply; int isPV = beta - alpha != 1; - int ttHit = 0; - int ttPv = 0; int inCheck = !!board->checkers; - int ttScore = UNKNOWN; int eval = ss->staticEval; + Move hashMove = NULL_MOVE; Move bestMove = NULL_MOVE; Move move = NULL_MOVE; MovePicker mp; @@ -771,26 +778,31 @@ int Quiesce(int alpha, int beta, int depth, ThreadData* thread, SearchStack* ss) return inCheck ? 0 : Evaluate(board, thread); // check the transposition table for previous info - TTEntry* tt = TTProbe(board->zobrist, &ttHit); - ttScore = ttHit ? TTScore(tt, ss->ply) : UNKNOWN; - ttPv = isPV || (ttHit && TTPV(tt)); + int ttHit = 0; + int ttScore = UNKNOWN; + int ttEval = UNKNOWN; + int ttDepth = DEPTH_OFFSET; + int ttBound = BOUND_UNKNOWN; + int ttPv = isPV; + + TTEntry* tt = TTProbe(board->zobrist, ss->ply, &ttHit, &hashMove, &ttScore, &ttEval, &ttDepth, &ttBound, &ttPv); // TT score pruning, ttHit implied with adjusted score - if (!isPV && ttScore != UNKNOWN && (TTBound(tt) & (ttScore >= beta ? BOUND_LOWER : BOUND_UPPER))) + if (!isPV && ttScore != UNKNOWN && (ttBound & (ttScore >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttScore; if (inCheck) { eval = ss->staticEval = UNKNOWN; } else { if (ttHit) { - eval = ss->staticEval = tt->eval; + eval = ss->staticEval = ttEval; if (ss->staticEval == UNKNOWN) eval = ss->staticEval = Evaluate(board, thread); // correct eval on fmr eval = AdjustEvalOnFMR(board, eval); - if (ttScore != UNKNOWN && (TTBound(tt) & (ttScore > eval ? BOUND_LOWER : BOUND_UPPER))) + if (ttScore != UNKNOWN && (ttBound & (ttScore > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttScore; } else { eval = ss->staticEval = Evaluate(board, thread); @@ -815,7 +827,7 @@ int Quiesce(int alpha, int beta, int depth, ThreadData* thread, SearchStack* ss) if (!inCheck) InitQSMovePicker(&mp, thread, depth >= -1); else - InitQSEvasionsPicker(&mp, ttHit ? tt->move : NULL_MOVE, thread, ss); + InitQSEvasionsPicker(&mp, hashMove, thread, ss); int legalMoves = 0; @@ -826,7 +838,7 @@ int Quiesce(int alpha, int beta, int depth, ThreadData* thread, SearchStack* ss) legalMoves++; if (bestScore > -TB_WIN_BOUND) { - if (inCheck && !(IsCap(move) || Promo(move))) + if (inCheck && !(IsCap(move) || IsPromo(move))) break; if (!inCheck && mp.phase != QS_PLAY_QUIET_CHECKS && futility <= alpha && !SEE(board, move, 1)) { @@ -887,7 +899,7 @@ void PrintUCI(ThreadData* thread, int alpha, int beta, Board* board) { int bounded = updated ? Max(alpha, Min(beta, thread->rootMoves[i].score)) : thread->rootMoves[i].previousScore; int printable = bounded > MATE_BOUND ? (CHECKMATE - bounded + 1) / 2 : bounded < -MATE_BOUND ? -(CHECKMATE + bounded) / 2 : - abs(bounded) > WINNING_ENDGAME ? bounded : // don't normalize our fake tb scores or real tb scores + abs(bounded) > TB_WIN_BOUND ? bounded : // don't normalize our fake tb scores or real tb scores Normalize(bounded); // convert to 50% WR at 100cp char* type = abs(bounded) > MATE_BOUND ? "mate" : "cp"; char* bound = " "; diff --git a/src/see.c b/src/see.c index 06d25b8e..b901a827 100644 --- a/src/see.c +++ b/src/see.c @@ -29,7 +29,7 @@ const int SEE_VALUE[7] = {100, 422, 422, 642, 1015, 30000, 0}; // Static exchange evaluation using The Swap Algorithm - // https://www.chessprogramming.org/SEE_-_The_Swap_Algorithm inline int SEE(Board* board, Move move, int threshold) { - if (IsCas(move) || IsEP(move) || Promo(move)) + if (IsCas(move) || IsEP(move) || IsPromo(move)) return 1; int from = From(move); diff --git a/src/tb.h b/src/tb.h index 2356da8a..5cc0e252 100644 --- a/src/tb.h +++ b/src/tb.h @@ -30,17 +30,23 @@ INLINE Move TBMoveFromResult(unsigned res, Board* board) { unsigned promo = TB_GET_PROMOTES(res); int piece = board->squares[from]; int capture = board->squares[to] != NO_PIECE; - int promoPiece = promo ? Piece(KING - promo, board->stm) : 0; - int flags = QUIET; + int flags = QUIET_FLAG; if (ep) - flags = EP; + flags = EP_FLAG; else if (capture) - flags = CAPTURE; - else if (PieceType(piece) == PAWN && (from ^ to) == 16) - flags = DP; - - return BuildMove(from, to, piece, promoPiece, flags); + flags = CAPTURE_FLAG; + + if (promo) { + switch (KING - promo) { + case KNIGHT: flags |= KNIGHT_PROMO_FLAG; break; + case BISHOP: flags |= BISHOP_PROMO_FLAG; break; + case ROOK: flags |= ROOK_PROMO_FLAG; break; + case QUEEN: flags |= QUEEN_PROMO_FLAG; break; + } + } + + return BuildMove(from, to, piece, flags); } void TBRootMoves(SimpleMoveList* moves, Board* board); diff --git a/src/transposition.c b/src/transposition.c index b9a278a9..70a6c271 100644 --- a/src/transposition.c +++ b/src/transposition.c @@ -96,14 +96,32 @@ inline void TTPrefetch(uint64_t hash) { __builtin_prefetch(&TT.buckets[TTIdx(hash)]); } -inline TTEntry* TTProbe(uint64_t hash, int* hit) { +inline TTEntry* TTProbe(uint64_t hash, + int ply, + int* hit, + Move* hashMove, + int* ttScore, + int* ttEval, + int* ttDepth, + int* ttBound, + int* pv) { TTEntry* const bucket = TT.buckets[TTIdx(hash)].entries; - const uint32_t shortHash = (uint32_t) hash; + const uint16_t shortHash = (uint16_t) hash; for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i].hash == shortHash || !bucket[i].depth) { bucket[i].agePvBound = (uint8_t) (TT.age | (bucket[i].agePvBound & (PV_MASK | BOUND_MASK))); *hit = !!bucket[i].depth; + + if (*hit) { + *hashMove = TTMove(&bucket[i]); + *ttEval = TTEval(&bucket[i]); + *ttScore = TTScore(&bucket[i], ply); + *ttDepth = TTDepth(&bucket[i]); + *ttBound = TTBound(&bucket[i]); + *pv = *pv || TTPV(&bucket[i]); + } + return &bucket[i]; } } @@ -121,7 +139,7 @@ inline TTEntry* TTProbe(uint64_t hash, int* hit) { inline void TTPut(TTEntry* tt, uint64_t hash, int depth, int16_t score, uint8_t bound, Move move, int ply, int16_t eval, int pv) { - uint32_t shortHash = (uint32_t) hash; + uint16_t shortHash = (uint16_t) hash; if (score >= TB_WIN_BOUND) score += ply; @@ -129,14 +147,14 @@ TTPut(TTEntry* tt, uint64_t hash, int depth, int16_t score, uint8_t bound, Move score -= ply; if (move || shortHash != tt->hash) - tt->move = move; + TTStoreMove(tt, move); if ((bound == BOUND_EXACT) || shortHash != tt->hash || depth + 4 > TTDepth(tt)) { tt->hash = shortHash; - tt->eval = eval; tt->score = score; tt->depth = (uint8_t) (depth - DEPTH_OFFSET); tt->agePvBound = (uint8_t) (TT.age | (pv << 2) | bound); + TTStoreEval(tt, eval); } } diff --git a/src/transposition.h b/src/transposition.h index 453b2e8e..c7a2899c 100644 --- a/src/transposition.h +++ b/src/transposition.h @@ -22,7 +22,7 @@ #define NO_ENTRY 0ULL #define MEGABYTE (1024ull * 1024ull) -#define BUCKET_SIZE 2 +#define BUCKET_SIZE 3 #define BOUND_MASK (0x3) #define PV_MASK (0x4) @@ -30,15 +30,17 @@ #define AGE_INC (0x8) #define AGE_CYCLE (255 + AGE_INC) -typedef struct { - Move move; - uint32_t hash; - int16_t eval, score; - uint8_t depth, agePvBound; +typedef struct __attribute__((packed)) { + uint16_t hash; + uint8_t depth; + uint8_t agePvBound; + uint32_t evalAndMove; + int16_t score; } TTEntry; typedef struct { TTEntry entries[BUCKET_SIZE]; + uint16_t padding; } TTBucket; typedef struct { @@ -63,7 +65,15 @@ void TTClearPart(int idx); void TTClear(); void TTUpdate(); void TTPrefetch(uint64_t hash); -TTEntry* TTProbe(uint64_t hash, int* hit); +TTEntry* TTProbe(uint64_t hash, + int ply, + int* hit, + Move* hashMove, + int* ttScore, + int* ttEval, + int* ttDepth, + int* ttBound, + int* pv); void TTPut(TTEntry* tt, uint64_t hash, int depth, @@ -77,6 +87,25 @@ int TTFull(); #define HASH_MAX ((int) (pow(2, 32) * sizeof(TTBucket) / MEGABYTE)) +INLINE Move TTMove(TTEntry* e) { + // Lower 20 bits for move + return (e->evalAndMove & 0xfffff); +} + +INLINE int TTEval(TTEntry* e) { + // Top 12 bits for eval offset by 2048 + return ((e->evalAndMove >> 20) & 0xfff) - 2048; +} + +INLINE void TTStoreMove(TTEntry* e, Move move) { + e->evalAndMove = (e->evalAndMove & 0xfff00000) | move; +} + +INLINE void TTStoreEval(TTEntry* e, int eval) { + uint32_t ueval = eval + 2048; + e->evalAndMove = (ueval << 20) | (e->evalAndMove & 0x000fffff); +} + INLINE int TTScore(TTEntry* e, int ply) { if (e->score == UNKNOWN) return UNKNOWN;